Search in sources :

Example 1 with Annotation

use of aQute.bnd.osgi.Annotation in project felix by apache.

the class AnnotationCollector method parseCommonComponentAttributes.

/**
 * Parses attributes common to all kind of components.
 * First, Property annotation is parsed which represents a list of key-value pair.
 * The properties are encoded using the following json form:
 *
 * properties       ::= key-value-pair*
 * key-value-pair   ::= key value
 * value            ::= String | String[] | value-type
 * value-type       ::= jsonObject with value-type-info
 * value-type-info  ::= "type"=primitive java type
 *                      "value"=String|String[]
 *
 * Exemple:
 *
 *  "properties" : {
 *      "string-param" : "string-value",
 *      "string-array-param" : ["str1", "str2],
 *      "long-param" : {"type":"java.lang.Long", "value":"1"}}
 *      "long-array-param" : {"type":"java.lang.Long", "value":["1"]}}
 *  }
 * }
 *
 * @param component the component annotation which contains a "properties" attribute. The component can be either a @Component, or an aspect, or an adapter.
 * @param writer the object where the parsed attributes are written.
 */
private void parseCommonComponentAttributes(Annotation component, EntryWriter writer) {
    // Parse properties attribute.
    Object[] properties = component.get(EntryParam.properties.toString());
    if (properties != null) {
        for (Object property : properties) {
            Annotation propertyAnnotation = (Annotation) property;
            parseProperty(propertyAnnotation, writer);
        }
    }
}
Also used : Annotation(aQute.bnd.osgi.Annotation)

Example 2 with Annotation

use of aQute.bnd.osgi.Annotation 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 3 with Annotation

use of aQute.bnd.osgi.Annotation in project bnd by bndtools.

the class ExtensionDef method addAttributes.

// non-matching attributes have already been removed
public void addAttributes(Tag tag, Namespaces namespaces) {
    if (namespaces != null) {
        for (Map.Entry<XMLAttribute, Annotation> entry : attributes.entrySet()) {
            String prefix = namespaces.getPrefix(entry.getKey().namespace());
            Annotation a = entry.getValue();
            Map<String, String> props = finder.getDefaults(a);
            for (String key : entry.getValue().keySet()) {
                Object obj = entry.getValue().get(key);
                String value;
                if (obj.getClass().isArray()) {
                    StringBuilder sb = new StringBuilder();
                    String sep = "";
                    for (int i = 0; i < Array.getLength(obj); i++) {
                        Object el = Array.get(obj, i);
                        sb.append(sep).append(String.valueOf(el));
                        sep = " ";
                    }
                    value = sb.toString();
                } else {
                    value = String.valueOf(obj);
                }
                props.put(key, value);
            }
            String[] mapping = entry.getKey().mapping();
            for (Map.Entry<String, String> prop : props.entrySet()) {
                String key = prop.getKey();
                if (mapping != null && mapping.length > 0) {
                    String match = key + "=";
                    for (String map : mapping) {
                        if (map.startsWith(match))
                            key = map.substring(match.length());
                    }
                }
                tag.addAttribute(prefix + ":" + key, prop.getValue());
            }
        }
    }
}
Also used : XMLAttribute(aQute.bnd.annotation.xml.XMLAttribute) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map) Annotation(aQute.bnd.osgi.Annotation)

Example 4 with Annotation

use of aQute.bnd.osgi.Annotation in project bnd by bndtools.

the class Target method testNestedAnnotations.

@SuppressWarnings({ "rawtypes", "unchecked" })
public static void testNestedAnnotations() throws Exception {
    try (Analyzer analyzer = new Analyzer()) {
        TypeRef typeref = analyzer.getTypeRefFromFQN(ActualAnnotation.class.getName());
        Map<String, Object> annMap = (Map) Collections.singletonMap("a", 5);
        Annotation annotation = new Annotation(typeref, annMap, ElementType.FIELD, RetentionPolicy.RUNTIME);
        Map<String, Object> properties = (Map) Collections.singletonMap("ann", annotation);
        ConfigurableInterface a = Configurable.createConfigurable(ConfigurableInterface.class, properties);
        assertNotNull(a);
        assertNotNull(a.ann());
        assertEquals(5, a.ann().a());
    }
}
Also used : TypeRef(aQute.bnd.osgi.Descriptors.TypeRef) Analyzer(aQute.bnd.osgi.Analyzer) Map(java.util.Map) Annotation(aQute.bnd.osgi.Annotation)

Example 5 with Annotation

use of aQute.bnd.osgi.Annotation in project bnd by bndtools.

the class AnnotationReader method doComponent.

/**
	 * @param annotation
	 * @throws Exception
	 */
@SuppressWarnings("deprecation")
protected void doComponent(Component comp, Annotation annotation) throws Exception {
    if (!mismatchedAnnotations.isEmpty()) {
        String componentName = comp.name();
        componentName = (componentName == null) ? className.getFQN() : componentName;
        for (Entry<String, List<DeclarativeServicesAnnotationError>> e : mismatchedAnnotations.entrySet()) {
            for (DeclarativeServicesAnnotationError errorDetails : e.getValue()) {
                if (errorDetails.fieldName != null) {
                    analyzer.error("The DS component %s uses standard annotations to declare it as a component, but also uses the bnd DS annotation: %s on field %s. It is an error to mix these two types of annotations", componentName, e.getKey(), errorDetails.fieldName).details(errorDetails);
                } else if (errorDetails.methodName != null) {
                    analyzer.error("The DS component %s uses standard annotations to declare it as a component, but also uses the bnd DS annotation: %s on method %s with signature %s. It is an error to mix these two types of annotations", componentName, e.getKey(), errorDetails.methodName, errorDetails.methodSignature).details(errorDetails);
                } else {
                    analyzer.error("The DS component %s uses standard annotations to declare it as a component, but also uses the bnd DS annotation: %s. It is an error to mix these two types of annotations", componentName, e.getKey()).details(errorDetails);
                }
            }
        }
        return;
    }
    // Check if we are doing a super class
    if (component.implementation != null)
        return;
    component.implementation = clazz.getClassName();
    component.name = comp.name();
    component.factory = comp.factory();
    component.configurationPolicy = comp.configurationPolicy();
    if (annotation.get("enabled") != null)
        component.enabled = comp.enabled();
    if (annotation.get("factory") != null)
        component.factory = comp.factory();
    if (annotation.get("immediate") != null)
        component.immediate = comp.immediate();
    if (annotation.get("servicefactory") != null)
        component.scope = comp.servicefactory() ? ServiceScope.BUNDLE : ServiceScope.SINGLETON;
    if (annotation.get("scope") != null && comp.scope() != ServiceScope.DEFAULT) {
        component.scope = comp.scope();
        if (comp.scope() == ServiceScope.PROTOTYPE) {
            component.updateVersion(V1_3);
        }
    }
    if (annotation.get("configurationPid") != null) {
        component.configurationPid = comp.configurationPid();
        if (component.configurationPid.length > 1) {
            component.updateVersion(V1_3);
        } else {
            component.updateVersion(V1_2);
        }
    }
    if (annotation.get("xmlns") != null)
        component.xmlns = comp.xmlns();
    String[] properties = comp.properties();
    if (properties != null)
        for (String entry : properties) {
            if (entry.contains("=")) {
                analyzer.error("Found an = sign in an OSGi DS Component annotation on %s. In the bnd annotation " + "this is an actual property but in the OSGi, this element must refer to a path with Java properties. " + "However, found a path with an '=' sign which looks like a mixup (%s) with the 'property' element.", clazz, entry).details(new DeclarativeServicesAnnotationError(className.getFQN(), null, null, ErrorType.COMPONENT_PROPERTIES_ERROR));
            }
            component.properties.add(entry);
        }
    doProperty(comp.property());
    Object[] x = annotation.get("service");
    if (x == null) {
        // fqn.
        if (interfaces != null) {
            List<TypeRef> result = new ArrayList<TypeRef>();
            for (int i = 0; i < interfaces.length; i++) {
                if (!interfaces[i].equals(analyzer.getTypeRef("scala/ScalaObject")))
                    result.add(interfaces[i]);
            }
            component.service = result.toArray(EMPTY);
        }
    } else {
        // We have explicit interfaces set
        component.service = new TypeRef[x.length];
        for (int i = 0; i < x.length; i++) {
            TypeRef typeRef = (TypeRef) x[i];
            Clazz service = analyzer.findClass(typeRef);
            if (!analyzer.assignable(clazz, service)) {
                analyzer.error("Class %s is not assignable to specified service %s", clazz.getFQN(), typeRef.getFQN()).details(new DeclarativeServicesAnnotationError(className.getFQN(), null, null, ErrorType.INCOMPATIBLE_SERVICE));
            }
            component.service[i] = typeRef;
        }
    }
    // make sure reference processing knows this is a Reference in Component
    member = null;
    Object[] refAnnotations = annotation.get("reference");
    if (refAnnotations != null) {
        for (Object o : refAnnotations) {
            Annotation refAnnotation = (Annotation) o;
            Reference ref = refAnnotation.getAnnotation();
            doReference(ref, refAnnotation);
        }
    }
}
Also used : DeclarativeServicesAnnotationError(aQute.bnd.component.error.DeclarativeServicesAnnotationError) TypeRef(aQute.bnd.osgi.Descriptors.TypeRef) Reference(org.osgi.service.component.annotations.Reference) ArrayList(java.util.ArrayList) Annotation(aQute.bnd.osgi.Annotation) ArrayList(java.util.ArrayList) List(java.util.List) Clazz(aQute.bnd.osgi.Clazz)

Aggregations

Annotation (aQute.bnd.osgi.Annotation)7 TypeRef (aQute.bnd.osgi.Descriptors.TypeRef)4 Clazz (aQute.bnd.osgi.Clazz)3 Analyzer (aQute.bnd.osgi.Analyzer)2 ClassDataCollector (aQute.bnd.osgi.ClassDataCollector)2 Map (java.util.Map)2 XMLAttribute (aQute.bnd.annotation.xml.XMLAttribute)1 DeclarativeServicesAnnotationError (aQute.bnd.component.error.DeclarativeServicesAnnotationError)1 MethodDef (aQute.bnd.osgi.Clazz.MethodDef)1 Instructions (aQute.bnd.osgi.Instructions)1 Delta (aQute.bnd.service.diff.Delta)1 Type (aQute.bnd.service.diff.Type)1 MultiMap (aQute.lib.collections.MultiMap)1 ArrayList (java.util.ArrayList)1 Collection (java.util.Collection)1 HashSet (java.util.HashSet)1 LinkedHashMap (java.util.LinkedHashMap)1 List (java.util.List)1 AtomicBoolean (java.util.concurrent.atomic.AtomicBoolean)1 Reference (org.osgi.service.component.annotations.Reference)1