Search in sources :

Example 16 with TypeRef

use of aQute.bnd.osgi.Descriptors.TypeRef 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 17 with TypeRef

use of aQute.bnd.osgi.Descriptors.TypeRef in project bnd by bndtools.

the class JavaElement method toString.

private String toString(TypeRef[] prototype) {
    StringBuilder sb = new StringBuilder();
    sb.append("(");
    String del = "";
    for (TypeRef ref : prototype) {
        sb.append(del);
        sb.append(ref.getFQN());
        del = ",";
    }
    sb.append(")");
    return sb.toString();
}
Also used : TypeRef(aQute.bnd.osgi.Descriptors.TypeRef)

Example 18 with TypeRef

use of aQute.bnd.osgi.Descriptors.TypeRef in project bnd by bndtools.

the class DiffPluginImpl method hasSource.

private boolean hasSource(Analyzer analyzer, String path) throws Exception {
    if (!path.endsWith(".class"))
        return false;
    TypeRef type = analyzer.getTypeRefFromPath(path);
    PackageRef packageRef = type.getPackageRef();
    Clazz clazz = analyzer.findClass(type);
    if (clazz == null)
        return false;
    String sourceFile = clazz.getSourceFile();
    if (sourceFile == null)
        return false;
    String source = "OSGI-OPT/src/" + packageRef.getBinary() + "/" + sourceFile;
    Resource sourceResource = analyzer.getJar().getResource(source);
    if (sourceResource == null)
        return false;
    return true;
}
Also used : TypeRef(aQute.bnd.osgi.Descriptors.TypeRef) Resource(aQute.bnd.osgi.Resource) Clazz(aQute.bnd.osgi.Clazz) PackageRef(aQute.bnd.osgi.Descriptors.PackageRef)

Example 19 with TypeRef

use of aQute.bnd.osgi.Descriptors.TypeRef in project bnd by bndtools.

the class XMLAttributeFinder method getDefaults.

public Map<String, String> getDefaults(Annotation a) {
    TypeRef name = a.getName();
    Map<String, String> defaults = defaultsCache.get(name);
    if (defaults == null)
        defaults = extractDefaults(name, analyzer);
    if (defaults == null)
        return new LinkedHashMap<String, String>();
    return new LinkedHashMap<String, String>(defaults);
}
Also used : TypeRef(aQute.bnd.osgi.Descriptors.TypeRef) LinkedHashMap(java.util.LinkedHashMap)

Example 20 with TypeRef

use of aQute.bnd.osgi.Descriptors.TypeRef in project bnd by bndtools.

the class XMLAttributeFinder method extractDefaults.

private Map<String, String> extractDefaults(TypeRef name, final Analyzer analyzer) {
    try {
        Clazz clazz = analyzer.findClass(name);
        final Map<String, String> props = new LinkedHashMap<String, String>();
        clazz.parseClassFileWithCollector(new ClassDataCollector() {

            @Override
            public void annotationDefault(Clazz.MethodDef defined) {
                Object value = defined.getConstant();
                // check type, exit with warning if annotation or annotation
                // array
                boolean isClass = false;
                TypeRef type = defined.getType().getClassRef();
                if (!type.isPrimitive()) {
                    if (Class.class.getName().equals(type.getFQN())) {
                        isClass = true;
                    } else {
                        try {
                            Clazz r = analyzer.findClass(type);
                            if (r.isAnnotation()) {
                                analyzer.warning("Nested annotation type found in field %s, %s", defined.getName(), type.getFQN());
                                return;
                            }
                        } catch (Exception e) {
                            analyzer.exception(e, "Exception extracting annotation defaults for type %s", type);
                            return;
                        }
                    }
                }
                if (value != null) {
                    String name = defined.getName();
                    if (value.getClass().isArray()) {
                        StringBuilder sb = new StringBuilder();
                        String sep = "";
                        // add element individually
                        for (int i = 0; i < Array.getLength(value); i++) {
                            Object element = Array.get(value, i);
                            sb.append(sep).append(convert(element, isClass));
                            sep = " ";
                        }
                        props.put(name, sb.toString());
                    } else {
                        props.put(name, convert(value, isClass));
                    }
                }
            }

            private String convert(Object value, boolean isClass) {
                if (isClass)
                    return ((TypeRef) value).getFQN();
                return String.valueOf(value);
            }
        });
        defaultsCache.put(name, props);
        return props;
    } catch (Exception e) {
        analyzer.exception(e, "Exception extracting annotation defaults for type %s", name);
    }
    return null;
}
Also used : TypeRef(aQute.bnd.osgi.Descriptors.TypeRef) Clazz(aQute.bnd.osgi.Clazz) ClassDataCollector(aQute.bnd.osgi.ClassDataCollector) LinkedHashMap(java.util.LinkedHashMap)

Aggregations

TypeRef (aQute.bnd.osgi.Descriptors.TypeRef)42 Clazz (aQute.bnd.osgi.Clazz)12 PackageRef (aQute.bnd.osgi.Descriptors.PackageRef)7 HashMap (java.util.HashMap)6 LinkedHashMap (java.util.LinkedHashMap)6 Map (java.util.Map)6 Analyzer (aQute.bnd.osgi.Analyzer)5 ClassDataCollector (aQute.bnd.osgi.ClassDataCollector)5 IOException (java.io.IOException)5 ArrayList (java.util.ArrayList)5 HashSet (java.util.HashSet)5 Matcher (java.util.regex.Matcher)5 DeclarativeServicesAnnotationError (aQute.bnd.component.error.DeclarativeServicesAnnotationError)4 Attrs (aQute.bnd.header.Attrs)4 Annotation (aQute.bnd.osgi.Annotation)4 MultiMap (aQute.lib.collections.MultiMap)4 MethodDef (aQute.bnd.osgi.Clazz.MethodDef)3 Instructions (aQute.bnd.osgi.Instructions)3 Resource (aQute.bnd.osgi.Resource)3 Tag (aQute.lib.tag.Tag)3