Search in sources :

Example 1 with MethodDef

use of aQute.bnd.osgi.Clazz.MethodDef in project bnd by bndtools.

the class HeaderReader method getNamespace.

/**
	 * Check if we need to use the v1.1 namespace (or later).
	 * 
	 * @param info
	 * @param cd TODO
	 * @param descriptors TODO
	 */
private void getNamespace(Map<String, String> info, ComponentDef cd, Map<String, MethodDef> descriptors) {
    String namespace = info.get(COMPONENT_NAMESPACE);
    if (namespace != null) {
        cd.xmlns = namespace;
    }
    String version = info.get(COMPONENT_VERSION);
    if (version != null) {
        try {
            Version v = new Version(version);
            cd.updateVersion(v);
        } catch (Exception e) {
            error("version: specified on component header but not a valid version: %s", version);
            return;
        }
    }
    for (String key : info.keySet()) {
        if (SET_COMPONENT_DIRECTIVES_1_2.contains(key)) {
            cd.updateVersion(AnnotationReader.V1_2);
            return;
        }
    }
    for (ReferenceDef rd : cd.references.values()) {
        if (rd.updated != null) {
            cd.updateVersion(AnnotationReader.V1_2);
            return;
        }
    }
    // among other things this picks up any specified lifecycle methods
    for (String key : info.keySet()) {
        if (SET_COMPONENT_DIRECTIVES_1_1.contains(key)) {
            cd.updateVersion(AnnotationReader.V1_1);
            return;
        }
    }
    for (String lifecycle : LIFECYCLE_METHODS) {
        // lifecycle methods were not specified.... check for non 1.0
        // signatures.
        MethodDef test = descriptors.get(lifecycle);
        if (descriptors.containsKey(lifecycle) && (!(test.isPublic() || test.isProtected()) || rateLifecycle(test, "deactivate".equals(lifecycle) ? allowedDeactivate : allowed) > 1)) {
            cd.updateVersion(AnnotationReader.V1_1);
            return;
        }
    }
}
Also used : Version(aQute.bnd.version.Version) MethodDef(aQute.bnd.osgi.Clazz.MethodDef)

Example 2 with MethodDef

use of aQute.bnd.osgi.Clazz.MethodDef in project bnd by bndtools.

the class AnnotationReader method doDeactivate.

/**
	 * 
	 */
protected void doDeactivate() {
    String methodDescriptor = member.getDescriptor().toString();
    DeclarativeServicesAnnotationError details = new DeclarativeServicesAnnotationError(className.getFQN(), member.getName(), methodDescriptor, ErrorType.DEACTIVATE_SIGNATURE_ERROR);
    if (!(member instanceof MethodDef)) {
        analyzer.error("Deactivate annotation on a field %s.%s", clazz, member.getDescriptor()).details(details);
        return;
    }
    boolean hasMapReturnType = false;
    Matcher m = LIFECYCLEDESCRIPTORDS10.matcher(methodDescriptor);
    if ("deactivate".equals(member.getName()) && m.matches()) {
        component.deactivate = member.getName();
        hasMapReturnType = m.group(3) != null;
        if (!member.isProtected())
            component.updateVersion(V1_1);
    } else {
        m = DEACTIVATEDESCRIPTORDS11.matcher(methodDescriptor);
        if (m.matches()) {
            component.deactivate = member.getName();
            component.updateVersion(V1_1);
            hasMapReturnType = m.group(8) != null;
        } else {
            m = DEACTIVATEDESCRIPTORDS13.matcher(methodDescriptor);
            if (m.matches()) {
                component.deactivate = member.getName();
                component.updateVersion(V1_3);
                hasMapReturnType = m.group(6) != null;
                processAnnotationArguments(methodDescriptor, details);
            } else
                analyzer.error("Deactivate method for %s descriptor %s is not acceptable.", clazz, member.getDescriptor()).details(details);
        }
    }
    checkMapReturnType(hasMapReturnType, details);
}
Also used : MethodDef(aQute.bnd.osgi.Clazz.MethodDef) Matcher(java.util.regex.Matcher) DeclarativeServicesAnnotationError(aQute.bnd.component.error.DeclarativeServicesAnnotationError)

Example 3 with MethodDef

use of aQute.bnd.osgi.Clazz.MethodDef in project bnd by bndtools.

the class AnnotationReader method doReference.

/**
	 * @param reference @Reference proxy backed by raw.
	 * @param raw @Reference contents
	 * @throws Exception
	 */
protected void doReference(Reference reference, Annotation raw) throws Exception {
    ReferenceDef def;
    if (member == null)
        def = new ReferenceDef(finder);
    else if (referencesByMember.containsKey(member))
        def = referencesByMember.get(member);
    else {
        def = new ReferenceDef(finder);
        referencesByMember.put(member, def);
    }
    def.className = className.getFQN();
    def.name = reference.name();
    def.bind = reference.bind();
    def.unbind = reference.unbind();
    def.updated = reference.updated();
    def.field = reference.field();
    def.fieldOption = reference.fieldOption();
    def.cardinality = reference.cardinality();
    def.policy = reference.policy();
    def.policyOption = reference.policyOption();
    def.scope = reference.scope();
    // Check if we have a target, this must be a filter
    def.target = reference.target();
    DeclarativeServicesAnnotationError details = getDetails(def, ErrorType.REFERENCE);
    if (def.target != null) {
        String error = Verifier.validateFilter(def.target);
        if (error != null)
            analyzer.error("Invalid target filter %s for %s: %s", def.target, def.name, error).details(getDetails(def, ErrorType.INVALID_TARGET_FILTER));
    }
    String annoService = null;
    TypeRef annoServiceTR = raw.get("service");
    if (annoServiceTR != null)
        annoService = annoServiceTR.getFQN();
    if (member != null) {
        if (member instanceof MethodDef) {
            def.bindDescriptor = member.getDescriptor().toString();
            if (!member.isProtected())
                def.updateVersion(V1_1);
            def.bind = member.getName();
            if (def.name == null) {
                Matcher m = BINDNAME.matcher(member.getName());
                if (m.matches())
                    def.name = m.group(2);
                else
                    analyzer.error("Invalid name for bind method %s", member.getName()).details(getDetails(def, ErrorType.INVALID_REFERENCE_BIND_METHOD_NAME));
            }
            def.service = determineReferenceType(def.bindDescriptor, def, annoService, member.getSignature());
            if (def.service == null)
                analyzer.error("In component %s, method %s,  cannot recognize the signature of the descriptor: %s", component.effectiveName(), def.name, member.getDescriptor());
        } else if (member instanceof FieldDef) {
            def.updateVersion(V1_3);
            def.field = member.getName();
            if (def.name == null)
                def.name = def.field;
            if (def.policy == null && member.isVolatile())
                def.policy = ReferencePolicy.DYNAMIC;
            String sig = member.getSignature();
            if (sig == null)
                // no generics, the descriptor will be the class name.
                sig = member.getDescriptor().toString();
            String[] sigs = sig.split("[<;>]");
            int sigLength = sigs.length;
            int index = 0;
            boolean isCollection = false;
            if ("Ljava/util/Collection".equals(sigs[index]) || "Ljava/util/List".equals(sigs[index])) {
                index++;
                isCollection = true;
            }
            // Along with determining the FieldCollectionType, the following
            // code positions index to read the service type.
            FieldCollectionType fieldCollectionType = null;
            if (sufficientGenerics(index, sigLength, def, sig)) {
                if ("Lorg/osgi/framework/ServiceReference".equals(sigs[index])) {
                    if (sufficientGenerics(index++, sigLength, def, sig)) {
                        fieldCollectionType = FieldCollectionType.reference;
                    }
                } else if ("Lorg/osgi/service/component/ComponentServiceObjects".equals(sigs[index])) {
                    if (sufficientGenerics(index++, sigLength, def, sig)) {
                        fieldCollectionType = FieldCollectionType.serviceobjects;
                    }
                } else if ("Ljava/util/Map".equals(sigs[index])) {
                    if (sufficientGenerics(index++, sigLength, def, sig)) {
                        fieldCollectionType = FieldCollectionType.properties;
                    }
                } else if ("Ljava/util/Map$Entry".equals(sigs[index]) && sufficientGenerics(index++ + 5, sigLength, def, sig)) {
                    if ("Ljava/util/Map".equals(sigs[index++]) && "Ljava/lang/String".equals(sigs[index++])) {
                        if ("Ljava/lang/Object".equals(sigs[index]) || "+Ljava/lang/Object".equals(sigs[index])) {
                            fieldCollectionType = FieldCollectionType.tuple;
                            // ;>;
                            index += 3;
                        } else if ("*".equals(sigs[index])) {
                            fieldCollectionType = FieldCollectionType.tuple;
                            // >;
                            index += 2;
                        } else {
                            // no idea what service might
                            index = sigLength;
                        // be.
                        }
                    }
                } else {
                    fieldCollectionType = FieldCollectionType.service;
                }
            }
            if (isCollection) {
                if (def.cardinality == null)
                    def.cardinality = ReferenceCardinality.MULTIPLE;
                def.fieldCollectionType = fieldCollectionType;
            }
            if (def.policy == ReferencePolicy.DYNAMIC && (def.cardinality == ReferenceCardinality.MULTIPLE || def.cardinality == ReferenceCardinality.AT_LEAST_ONE) && member.isFinal()) {
                if (def.fieldOption == FieldOption.REPLACE)
                    analyzer.error("In component %s, collection type field: %s is final and dynamic but marked with 'replace' fieldOption. Changing this to 'update'.", className, def.field).details(getDetails(def, ErrorType.DYNAMIC_FINAL_FIELD_WITH_REPLACE));
                def.fieldOption = FieldOption.UPDATE;
            }
            if (annoService == null && index < sigs.length) {
                annoService = sigs[index].substring(1).replace('/', '.');
            }
            def.service = annoService;
            if (def.service == null)
                analyzer.error("In component %s, method %s,  cannot recognize the signature of the descriptor: %s", component.effectiveName(), def.name, member.getDescriptor()).details(details);
        }
    // end field
    } else {
        // not a member
        def.service = annoService;
        if (def.name == null) {
            analyzer.error("Name must be supplied for a @Reference specified in the @Component annotation. Service: %s", def.service).details(getDetails(def, ErrorType.MISSING_REFERENCE_NAME));
            return;
        }
    }
    if (component.references.containsKey(def.name))
        analyzer.error("In component %s, multiple references with the same name: %s. Previous def: %s, this def: %s", className, component.references.get(def.name), def.service, "").details(getDetails(def, ErrorType.MULTIPLE_REFERENCES_SAME_NAME));
    else
        component.references.put(def.name, def);
}
Also used : FieldDef(aQute.bnd.osgi.Clazz.FieldDef) MethodDef(aQute.bnd.osgi.Clazz.MethodDef) Matcher(java.util.regex.Matcher) DeclarativeServicesAnnotationError(aQute.bnd.component.error.DeclarativeServicesAnnotationError) TypeRef(aQute.bnd.osgi.Descriptors.TypeRef)

Example 4 with MethodDef

use of aQute.bnd.osgi.Clazz.MethodDef in project bnd by bndtools.

the class JavaElement method classElement.

/**
	 * Calculate the class element. This requires parsing the class file and
	 * finding all the methods that were added etc. The parsing will take super
	 * interfaces and super classes into account. For this reason it maintains a
	 * queue of classes/interfaces to parse.
	 * 
	 * @param analyzer
	 * @param clazz
	 * @param infos
	 * @throws Exception
	 */
Element classElement(final Clazz clazz) throws Exception {
    Element e = cache.get(clazz);
    if (e != null)
        return e;
    final Set<Element> members = Create.set();
    final Set<MethodDef> methods = Create.set();
    final Set<Clazz.FieldDef> fields = Create.set();
    final MultiMap<Clazz.Def, Element> annotations = new MultiMap<Clazz.Def, Element>();
    final TypeRef name = clazz.getClassName();
    final String fqn = name.getFQN();
    final String shortName = name.getShortName();
    // Check if this clazz is actually a provider or not
    // providers must be listed in the exported package in the
    // PROVIDER_TYPE directive.
    Instructions matchers = providerMatcher.get(name.getPackageRef());
    boolean p = matchers != null && matchers.matches(shortName);
    final AtomicBoolean provider = new AtomicBoolean(p);
    //
    // Check if we already had this clazz in the cache
    //
    // for super classes
    Element before = cache.get(clazz);
    if (before != null)
        return before;
    clazz.parseClassFileWithCollector(new ClassDataCollector() {

        boolean memberEnd;

        Clazz.FieldDef last;

        @Override
        public void version(int minor, int major) {
            javas.add(Clazz.JAVA.getJava(major, minor));
        }

        @Override
        public void method(MethodDef defined) {
            if ((defined.isProtected() || defined.isPublic())) {
                last = defined;
                methods.add(defined);
            } else {
                last = null;
            }
        }

        @Override
        public void deprecated() {
            if (memberEnd)
                clazz.setDeprecated(true);
            else if (last != null)
                last.setDeprecated(true);
        }

        @Override
        public void field(Clazz.FieldDef defined) {
            if (defined.isProtected() || defined.isPublic()) {
                last = defined;
                fields.add(defined);
            } else
                last = null;
        }

        @Override
        public void constant(Object o) {
            if (last != null) {
                // Must be accessible now
                last.setConstant(o);
            }
        }

        @Override
        public void extendsClass(TypeRef name) throws Exception {
            String comment = null;
            if (!clazz.isInterface())
                comment = inherit(members, name);
            Clazz c = analyzer.findClass(name);
            if ((c == null || c.isPublic()) && !name.isObject())
                members.add(new Element(EXTENDS, name.getFQN(), null, MICRO, MAJOR, comment));
        }

        @Override
        public void implementsInterfaces(TypeRef[] names) throws Exception {
            // ignore type reordering
            Arrays.sort(names);
            for (TypeRef name : names) {
                String comment = null;
                if (clazz.isInterface() || clazz.isAbstract())
                    comment = inherit(members, name);
                members.add(new Element(IMPLEMENTS, name.getFQN(), null, MINOR, MAJOR, comment));
            }
        }

        /**
			 */
        Set<Element> OBJECT = Create.set();

        public String inherit(final Set<Element> members, TypeRef name) throws Exception {
            if (name.isObject()) {
                if (OBJECT.isEmpty()) {
                    Clazz c = analyzer.findClass(name);
                    if (c == null) {
                        // available
                        return null;
                    }
                    Element s = classElement(c);
                    for (Element child : s.children) {
                        if (INHERITED.contains(child.type)) {
                            String n = child.getName();
                            if (child.type == METHOD) {
                                if (n.startsWith("<init>") || "getClass()".equals(child.getName()) || n.startsWith("wait(") || n.startsWith("notify(") || n.startsWith("notifyAll("))
                                    continue;
                            }
                            if (isStatic(child))
                                continue;
                            OBJECT.add(child);
                        }
                    }
                }
                members.addAll(OBJECT);
            } else {
                Clazz c = analyzer.findClass(name);
                if (c == null) {
                    return inherit(members, analyzer.getTypeRef("java/lang/Object"));
                }
                Element s = classElement(c);
                for (Element child : s.children) {
                    if (isStatic(child))
                        continue;
                    if (INHERITED.contains(child.type) && !child.name.startsWith("<")) {
                        members.add(child);
                    }
                }
            }
            return null;
        }

        private boolean isStatic(Element child) {
            boolean isStatic = child.get("static") != null;
            return isStatic;
        }

        /**
			 * Deprecated annotations and Provider/Consumer Type (both bnd and
			 * OSGi) are treated special. Other annotations are turned into a
			 * tree. Starting with ANNOTATED, and then properties. A property is
			 * a PROPERTY property or an ANNOTATED property if it is an
			 * annotation. If it is an array, the key is suffixed with the
			 * index.
			 * 
			 * <pre>
			 *  public @interface Outer { Inner[] value(); }
			 * public @interface Inner { String[] value(); } @Outer(
			 * { @Inner("1","2"}) } class Xyz {} ANNOTATED Outer
			 * (CHANGED/CHANGED) ANNOTATED Inner (CHANGED/CHANGED) PROPERTY
			 * value.0=1 (CHANGED/CHANGED) PROPERTY value.1=2 (CHANGED/CHANGED)
			 * </pre>
			 */
        @Override
        public void annotation(Annotation annotation) {
            if (Deprecated.class.getName().equals(annotation.getName().getFQN())) {
                if (memberEnd)
                    clazz.setDeprecated(true);
                else if (last != null)
                    last.setDeprecated(true);
                return;
            }
            Element e = annotatedToElement(annotation);
            if (memberEnd) {
                members.add(e);
                //
                // Check for the provider/consumer. We use strings because
                // these are not officially
                // released yet
                //
                String name = annotation.getName().getFQN();
                if ("aQute.bnd.annotation.ProviderType".equals(name) || "org.osgi.annotation.versioning.ProviderType".equals(name)) {
                    provider.set(true);
                } else if ("aQute.bnd.annotation.ConsumerType".equals(name) || "org.osgi.annotation.versioning.ConsumerType".equals(name)) {
                    provider.set(false);
                }
            } else if (last != null)
                annotations.add(last, e);
        }

        /*
			 * Return an ANNOTATED element for this annotation. An ANNOTATED
			 * element contains either PROPERTY children or ANNOTATED children.
			 */
        private Element annotatedToElement(Annotation annotation) {
            Collection<Element> properties = Create.set();
            for (String key : annotation.keySet()) {
                addAnnotationMember(properties, key, annotation.get(key));
            }
            return new Element(ANNOTATED, annotation.getName().getFQN(), properties, CHANGED, CHANGED, null);
        }

        /*
			 * This method detects 3 cases: An Annotation, which means it
			 * creates a new child ANNOTATED element, an array, which means it
			 * will repeat recursively but suffixes the key with the index, or a
			 * simple value which is turned into a string.
			 */
        private void addAnnotationMember(Collection<Element> properties, String key, Object member) {
            if (member instanceof Annotation) {
                properties.add(annotatedToElement((Annotation) member));
            } else if (member.getClass().isArray()) {
                int l = Array.getLength(member);
                for (int i = 0; i < l; i++) {
                    addAnnotationMember(properties, key + "." + i, Array.get(member, i));
                }
            } else {
                StringBuilder sb = new StringBuilder();
                sb.append(key);
                sb.append('=');
                if (member instanceof String) {
                    sb.append("'");
                    sb.append(member);
                    sb.append("'");
                } else
                    sb.append(member);
                properties.add(new Element(PROPERTY, sb.toString(), null, CHANGED, CHANGED, null));
            }
        }

        @Override
        public void innerClass(TypeRef innerClass, TypeRef outerClass, String innerName, int innerClassAccessFlags) throws Exception {
            Clazz clazz = analyzer.findClass(innerClass);
            if (clazz != null)
                clazz.setInnerAccess(innerClassAccessFlags);
            if (Modifier.isProtected(innerClassAccessFlags) || Modifier.isPublic(innerClassAccessFlags))
                return;
            notAccessible.add(innerClass);
        }

        @Override
        public void memberEnd() {
            memberEnd = true;
        }
    });
    // This is the heart of the semantic versioning. If we
    // add or remove a method from an interface then
    Delta add;
    Delta remove;
    Type type;
    if (clazz.isInterface())
        if (clazz.isAnnotation())
            type = ANNOTATION;
        else
            type = INTERFACE;
    else if (clazz.isEnum())
        type = ENUM;
    else
        type = CLASS;
    if (type == INTERFACE) {
        if (provider.get()) {
            // Adding a method for a provider is not an issue
            // because it must be aware of the changes
            add = MINOR;
            // Removing a method influences consumers since they
            // tend to call this guy.
            remove = MAJOR;
        } else {
            // Adding a method is a major change
            // because the consumer has to implement it
            // or the provider will call a non existent
            // method on the consumer
            add = MAJOR;
            // Removing a method is not an issue for
            // providers, however, consumers could potentially
            // call through this interface :-(
            remove = MAJOR;
        }
    } else {
        // Adding a method to a class can never do any harm
        // except when the class is extended and the new
        // method clashes with the new method. That is
        // why API classes in general should be final, at
        // least not extended by consumers.
        add = MINOR;
        // Removing it will likely hurt consumers
        remove = MAJOR;
    }
    for (MethodDef m : methods) {
        if (m.isSynthetic()) {
            // Ignore synthetic methods
            continue;
        }
        Collection<Element> children = annotations.get(m);
        if (children == null)
            children = new HashSet<Element>();
        access(children, m.getAccess(), m.isDeprecated(), provider.get());
        // in a final class.
        if (clazz.isFinal())
            children.remove(FINAL);
        children.add(getReturn(m.getType()));
        if (clazz.isInterface() && !m.isAbstract()) {
            //
            // We have a Java 8 default method!
            // Such a method is always a minor update
            //
            add = MINOR;
        }
        String signature = m.getName() + toString(m.getPrototype());
        Element member = new Element(METHOD, signature, children, add, provider.get() && !m.isPublic() ? MINOR : remove, null);
        if (!members.add(member)) {
            members.remove(member);
            members.add(member);
        }
    }
    for (Clazz.FieldDef f : fields) {
        if (f.isSynthetic()) {
            // Ignore synthetic fields
            continue;
        }
        Collection<Element> children = annotations.get(f);
        if (children == null)
            children = new HashSet<Element>();
        // Fields can have a constant value, this is a new element
        if (f.getConstant() != null) {
            children.add(new Element(CONSTANT, f.getConstant().toString(), null, CHANGED, CHANGED, null));
        }
        access(children, f.getAccess(), f.isDeprecated(), provider.get());
        children.add(getReturn(f.getType()));
        Element member = new Element(FIELD, f.getName(), children, MINOR, provider.get() && !f.isPublic() ? MINOR : MAJOR, null);
        if (!members.add(member)) {
            members.remove(member);
            members.add(member);
        }
    }
    access(members, clazz.getAccess(), clazz.isDeprecated(), provider.get());
    // And make the result
    Element s = new Element(type, fqn, members, MINOR, MAJOR, null);
    cache.put(clazz, s);
    return s;
}
Also used : MethodDef(aQute.bnd.osgi.Clazz.MethodDef) TypeRef(aQute.bnd.osgi.Descriptors.TypeRef) MultiMap(aQute.lib.collections.MultiMap) Clazz(aQute.bnd.osgi.Clazz) ClassDataCollector(aQute.bnd.osgi.ClassDataCollector) HashSet(java.util.HashSet) MethodDef(aQute.bnd.osgi.Clazz.MethodDef) Instructions(aQute.bnd.osgi.Instructions) Annotation(aQute.bnd.osgi.Annotation) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) Type(aQute.bnd.service.diff.Type) Delta(aQute.bnd.service.diff.Delta) Collection(java.util.Collection)

Example 5 with MethodDef

use of aQute.bnd.osgi.Clazz.MethodDef in project bnd by bndtools.

the class CoverageResource method write.

@Override
public void write(OutputStream out) throws IOException {
    try {
        Map<MethodDef, List<MethodDef>> table = getCrossRef(testsuite, service);
        Tag coverage = toTag(table);
        PrintWriter pw = new PrintWriter(new OutputStreamWriter(out, Constants.DEFAULT_CHARSET));
        try {
            coverage.print(0, pw);
        } finally {
            pw.flush();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
Also used : MethodDef(aQute.bnd.osgi.Clazz.MethodDef) List(java.util.List) OutputStreamWriter(java.io.OutputStreamWriter) Tag(aQute.lib.tag.Tag) IOException(java.io.IOException) PrintWriter(java.io.PrintWriter)

Aggregations

MethodDef (aQute.bnd.osgi.Clazz.MethodDef)10 DeclarativeServicesAnnotationError (aQute.bnd.component.error.DeclarativeServicesAnnotationError)4 Matcher (java.util.regex.Matcher)4 Clazz (aQute.bnd.osgi.Clazz)3 TypeRef (aQute.bnd.osgi.Descriptors.TypeRef)3 ClassDataCollector (aQute.bnd.osgi.ClassDataCollector)2 Tag (aQute.lib.tag.Tag)2 HashMap (java.util.HashMap)2 HashSet (java.util.HashSet)2 List (java.util.List)2 Map (java.util.Map)2 Annotation (aQute.bnd.osgi.Annotation)1 FieldDef (aQute.bnd.osgi.Clazz.FieldDef)1 Instructions (aQute.bnd.osgi.Instructions)1 Delta (aQute.bnd.service.diff.Delta)1 Type (aQute.bnd.service.diff.Type)1 Version (aQute.bnd.version.Version)1 MultiMap (aQute.lib.collections.MultiMap)1 IOException (java.io.IOException)1 OutputStreamWriter (java.io.OutputStreamWriter)1