Search in sources :

Example 1 with Delta

use of aQute.bnd.service.diff.Delta in project bnd by bndtools.

the class Builder method show.

/**
	 * Show the diff recursively
	 * 
	 * @param p
	 * @param i
	 */
private void show(Diff p, String indent, boolean warning) {
    Delta d = p.getDelta();
    if (d == Delta.UNCHANGED)
        return;
    if (warning)
        warning("%s%s", indent, p).header("-diff");
    else
        error("%s%s", indent, p).header("-diff");
    indent = indent + " ";
    switch(d) {
        case CHANGED:
        case MAJOR:
        case MINOR:
        case MICRO:
            break;
        default:
            return;
    }
    for (Diff c : p.getChildren()) show(c, indent, warning);
}
Also used : Delta(aQute.bnd.service.diff.Delta) Diff(aQute.bnd.service.diff.Diff)

Example 2 with Delta

use of aQute.bnd.service.diff.Delta in project bnd by bndtools.

the class Baseline method baseline.

public Set<Info> baseline(Tree n, Parameters nExports, Tree o, Parameters oExports, Instructions packageFilters) throws Exception {
    diff = n.diff(o);
    Diff apiDiff = diff.get("<api>");
    infos = Create.set();
    bsn = getBsn(n);
    newerVersion = getVersion(n);
    olderVersion = getVersion(o);
    boolean firstRelease = false;
    if (o.get("<manifest>") == null) {
        firstRelease = true;
        if (newerVersion.equals(Version.emptyVersion)) {
            newerVersion = Version.ONE;
        }
    }
    Delta highestDelta = Delta.UNCHANGED;
    for (Diff pdiff : apiDiff.getChildren()) {
        if (// Just packages
        pdiff.getType() != Type.PACKAGE)
            continue;
        if (pdiff.getName().startsWith("java."))
            continue;
        if (!packageFilters.matches(pdiff.getName()))
            continue;
        final Info info = new Info();
        infos.add(info);
        info.reason = getRootCauses(pdiff);
        info.packageDiff = pdiff;
        info.packageName = pdiff.getName();
        info.attributes = nExports.get(info.packageName);
        logger.debug("attrs for {} {}", info.packageName, info.attributes);
        info.newerVersion = getVersion(info.attributes);
        info.olderVersion = getVersion(oExports.get(info.packageName));
        if (pdiff.getDelta() == Delta.UNCHANGED) {
            info.suggestedVersion = info.olderVersion;
            // Fix previously released package containing version qualifier
            if (info.olderVersion.getQualifier() != null) {
                info.suggestedVersion = bump(Delta.MICRO, info.olderVersion, 1, 0);
                info.warning += "Found package version with qualifier. Bumping micro version";
            } else if (!info.newerVersion.equals(info.olderVersion)) {
                info.warning += "No difference but versions are not equal";
            }
        } else if (pdiff.getDelta() == Delta.REMOVED) {
            info.suggestedVersion = null;
        } else if (pdiff.getDelta() == Delta.ADDED) {
            info.suggestedVersion = info.newerVersion;
        } else {
            // We have an API change
            info.suggestedVersion = bump(pdiff.getDelta(), info.olderVersion, 1, 0);
            if (info.newerVersion.compareTo(info.suggestedVersion) < 0) {
                // our suggested version is smaller
                info.mismatch = true;
                // that an interface is a provider interface
                if (pdiff.getDelta() == Delta.MAJOR) {
                    info.providers = Create.set();
                    if (info.attributes != null)
                        info.providers.addAll(Processor.split(info.attributes.get(Constants.PROVIDER_TYPE_DIRECTIVE)));
                    // Calculate the new delta assuming we fix all the major
                    // interfaces
                    // by making them providers
                    Delta tryDelta = pdiff.getDelta(new Ignore() {

                        public boolean contains(Diff diff) {
                            if (diff.getType() == Type.INTERFACE && diff.getDelta() == Delta.MAJOR) {
                                info.providers.add(Descriptors.getShortName(diff.getName()));
                                return true;
                            }
                            return false;
                        }
                    });
                    if (tryDelta != Delta.MAJOR) {
                        info.suggestedIfProviders = bump(tryDelta, info.olderVersion, 1, 0);
                    }
                }
            }
        }
        Delta content;
        switch(pdiff.getDelta()) {
            case IGNORED:
            case UNCHANGED:
                content = Delta.UNCHANGED;
                break;
            case ADDED:
                content = Delta.MINOR;
                break;
            case // cannot happen
            CHANGED:
                content = Delta.MICRO;
                break;
            case MICRO:
                content = pdiff.getDelta();
                break;
            case MINOR:
                content = pdiff.getDelta();
                break;
            case MAJOR:
                content = pdiff.getDelta();
                break;
            case REMOVED:
            default:
                content = Delta.MAJOR;
                break;
        }
        if (content.compareTo(highestDelta) > 0) {
            highestDelta = content;
        }
    }
    // biggest semantic change
    if (firstRelease || !bsn.equals(getBsn(o))) {
        suggestedVersion = newerVersion;
    } else {
        suggestedVersion = bumpBundle(highestDelta, olderVersion, 1, 0);
        if (suggestedVersion.compareTo(newerVersion) < 0)
            suggestedVersion = newerVersion;
    }
    binfo.bsn = bsn;
    binfo.suggestedVersion = suggestedVersion;
    binfo.version = binfo.olderVersion = olderVersion;
    binfo.newerVersion = newerVersion;
    if (newerVersion.getWithoutQualifier().equals(olderVersion.getWithoutQualifier())) {
        if (getDiff().getDelta() == Delta.UNCHANGED)
            return infos;
    }
    // Ok, now our bundle version must be >= the suggestedVersion
    if (newerVersion.getWithoutQualifier().compareTo(getSuggestedVersion()) < 0) {
        binfo.mismatch = true;
        binfo.reason = getRootCauses(apiDiff);
    }
    return infos;
}
Also used : Ignore(aQute.bnd.service.diff.Diff.Ignore) Diff(aQute.bnd.service.diff.Diff) Delta(aQute.bnd.service.diff.Delta)

Example 3 with Delta

use of aQute.bnd.service.diff.Delta 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 4 with Delta

use of aQute.bnd.service.diff.Delta in project bndtools by bndtools.

the class BaselineErrorHandler method generateStructuralChangeMarkers.

List<MarkerData> generateStructuralChangeMarkers(Info baselineInfo, IJavaProject javaProject) throws JavaModelException {
    List<MarkerData> markers = new LinkedList<MarkerData>();
    Delta packageDelta = baselineInfo.packageDiff.getDelta();
    // Iterate into the package member diffs
    for (Diff pkgMemberDiff : baselineInfo.packageDiff.getChildren()) {
        // Skip deltas that have lesser significance than the overall package delta
        if (pkgMemberDiff.getDelta().ordinal() < packageDelta.ordinal())
            continue;
        if (Delta.ADDED == pkgMemberDiff.getDelta()) {
            @SuppressWarnings("unused") Tree pkgMember = pkgMemberDiff.getNewer();
        // markers.addAll(generateAddedTypeMarker(javaProject, pkgMember.getName(), pkgMember.ifAdded()));
        } else if (Delta.REMOVED == pkgMemberDiff.getDelta()) {
        } else {
            Tree pkgMember = pkgMemberDiff.getOlder();
            if (pkgMember != null && (Type.INTERFACE == pkgMember.getType() || Type.CLASS == pkgMember.getType())) {
                String className = pkgMember.getName();
                // Iterate into the class member diffs
                for (Diff classMemberDiff : pkgMemberDiff.getChildren()) {
                    // Skip deltas that have lesser significance than the overall package delta (again)
                    if (classMemberDiff.getDelta().ordinal() < packageDelta.ordinal())
                        continue;
                    if (Delta.ADDED == classMemberDiff.getDelta()) {
                        Tree classMember = classMemberDiff.getNewer();
                        if (Type.METHOD == classMember.getType())
                            markers.addAll(generateAddedMethodMarker(javaProject, className, classMember.getName(), classMember.ifAdded()));
                    } else if (Delta.REMOVED == classMemberDiff.getDelta()) {
                        Tree classMember = classMemberDiff.getOlder();
                        if (Type.METHOD == classMember.getType()) {
                            markers.addAll(generateRemovedMethodMarker(javaProject, className, classMember.getName(), classMember.ifRemoved()));
                        }
                    }
                }
            }
        }
    }
    return markers;
}
Also used : MarkerData(org.bndtools.build.api.MarkerData) Delta(aQute.bnd.service.diff.Delta) Diff(aQute.bnd.service.diff.Diff) Tree(aQute.bnd.service.diff.Tree) LinkedList(java.util.LinkedList)

Aggregations

Delta (aQute.bnd.service.diff.Delta)4 Diff (aQute.bnd.service.diff.Diff)3 Annotation (aQute.bnd.osgi.Annotation)1 ClassDataCollector (aQute.bnd.osgi.ClassDataCollector)1 Clazz (aQute.bnd.osgi.Clazz)1 MethodDef (aQute.bnd.osgi.Clazz.MethodDef)1 TypeRef (aQute.bnd.osgi.Descriptors.TypeRef)1 Instructions (aQute.bnd.osgi.Instructions)1 Ignore (aQute.bnd.service.diff.Diff.Ignore)1 Tree (aQute.bnd.service.diff.Tree)1 Type (aQute.bnd.service.diff.Type)1 MultiMap (aQute.lib.collections.MultiMap)1 Collection (java.util.Collection)1 HashSet (java.util.HashSet)1 LinkedList (java.util.LinkedList)1 AtomicBoolean (java.util.concurrent.atomic.AtomicBoolean)1 MarkerData (org.bndtools.build.api.MarkerData)1