Search in sources :

Example 1 with CoIterator

use of org.revapi.CoIterator in project revapi by revapi.

the class JavaApiAnalyzer method determineOrder.

private static void determineOrder(List<Element> l1, List<Element> l2, IdentityHashMap<MethodElement, Integer> l1MethodOrder, IdentityHashMap<MethodElement, Integer> l2MethodOrder) {
    TreeMap<String, List<MethodElement>> l1MethodsByName = new TreeMap<>();
    TreeMap<String, List<MethodElement>> l2MethodsByName = new TreeMap<>();
    int l1MethodsSize = addAllMethods(l1, l1MethodsByName);
    int l2MethodsSize = addAllMethods(l2, l2MethodsByName);
    // rehash overloads that are present in both collections - those are then reordered using their mutual
    // resemblance
    int index = 0;
    Iterator<Map.Entry<String, List<MethodElement>>> l1MethodsIterator = l1MethodsByName.entrySet().iterator();
    Iterator<Map.Entry<String, List<MethodElement>>> l2MethodsIterator = l2MethodsByName.entrySet().iterator();
    // iterate over the maps, sorted by name and assign the comparison index to the methods.
    // we iterate over the maps sorted by method name
    CoIterator<Map.Entry<String, List<MethodElement>>> coit = new CoIterator<>(l1MethodsIterator, l2MethodsIterator, (e1, e2) -> e1.getKey().compareTo(e2.getKey()));
    List<Element> l2MethodsInOrder = new ArrayList<>(l1MethodsSize);
    List<Element> l1MethodsInOrder = new ArrayList<>(l2MethodsSize);
    while (coit.hasNext()) {
        coit.next();
        Map.Entry<String, List<MethodElement>> l1e = coit.getLeft();
        Map.Entry<String, List<MethodElement>> l2e = coit.getRight();
        if (l1e == null) {
            // no overloads with the name present in l1
            for (MethodElement m : l2e.getValue()) {
                l2MethodOrder.put(m, index++);
                l2MethodsInOrder.add(m);
            }
        } else if (l2e == null) {
            // no overloads with the name present in l2
            for (MethodElement m : l1e.getValue()) {
                l1MethodOrder.put(m, index++);
                l1MethodsInOrder.add(m);
            }
        } else {
            // overloads of the same name present in both maps
            // the lists were already sorted by the method above
            List<MethodElement> l1Overloads = l1e.getValue();
            List<MethodElement> l2Overloads = l2e.getValue();
            if (l1Overloads.size() == 1 && l2Overloads.size() == 1) {
                // fast path for hopefully the vast majority of cases
                // just indicate the same order for both methods from l1 and l2
                MethodElement m1 = l1Overloads.get(0);
                MethodElement m2 = l2Overloads.get(0);
                l1MethodsInOrder.add(m1);
                l2MethodsInOrder.add(m2);
                l2MethodOrder.put(m2, index);
                l1MethodOrder.put(m1, index++);
            } else {
                // slow path - for each overload in l1, we need to pick the appropriate one from l2 and put it in the
                // same place
                List<MethodElement> as = l1Overloads;
                List<MethodElement> bs = l2Overloads;
                List<Element> aio = l1MethodsInOrder;
                List<Element> bio = l2MethodsInOrder;
                IdentityHashMap<MethodElement, Integer> ao = l1MethodOrder;
                IdentityHashMap<MethodElement, Integer> bo = l2MethodOrder;
                if (l1Overloads.size() > l2Overloads.size()) {
                    as = l2Overloads;
                    bs = l1Overloads;
                    aio = l2MethodsInOrder;
                    bio = l1MethodsInOrder;
                    ao = l2MethodOrder;
                    bo = l1MethodOrder;
                }
                for (MethodElement aMethod : as) {
                    ao.put(aMethod, index);
                    aio.add(aMethod);
                    MethodElement bMethod = removeBestMatch(aMethod, bs);
                    bo.put(bMethod, index++);
                    bio.add(bMethod);
                }
                // add the rest
                for (MethodElement m : bs) {
                    bo.put(m, index++);
                    bio.add(m);
                }
            }
        }
    }
    // ok, so now we have the method indices right in the comparison matrices...
    // but we also have to reorder the lists themselves to contain the methods in that order so that we
    // conform to the restrictions imposed by the co-iteration of the lists during the analysis
    // the lists are already sorted in the natural order of the java elements which is first and foremost sorted
    // by element type (see org.revapi.java.model.JavaElementFactory). Let's exploit that and just remove all the
    // methods in the list and re-add them in the correct order.
    reAddSortedMethods(l1, l1MethodsInOrder);
    reAddSortedMethods(l2, l2MethodsInOrder);
}
Also used : CoIterator(org.revapi.CoIterator) Element(org.revapi.Element) TypeElement(org.revapi.java.model.TypeElement) MethodElement(org.revapi.java.model.MethodElement) IdentityHashMap(java.util.IdentityHashMap) MethodElement(org.revapi.java.model.MethodElement) ArrayList(java.util.ArrayList) TreeMap(java.util.TreeMap) ArrayList(java.util.ArrayList) Collectors.toList(java.util.stream.Collectors.toList) List(java.util.List) HashMap(java.util.HashMap) Map(java.util.Map) IdentityHashMap(java.util.IdentityHashMap) TreeMap(java.util.TreeMap)

Example 2 with CoIterator

use of org.revapi.CoIterator in project revapi by revapi.

the class InheritanceChainChanged method doEnd.

@Override
protected List<Difference> doEnd() {
    ActiveElements<JavaTypeElement> types = popIfActive();
    if (types != null) {
        List<Difference> ret = new ArrayList<>();
        @SuppressWarnings("unchecked") List<TypeMirror> oldSuperClasses = (List<TypeMirror>) types.context[0];
        @SuppressWarnings("unchecked") List<TypeMirror> newSuperClasses = (List<TypeMirror>) types.context[1];
        Comparator<TypeMirror> typeNameComparator = Comparator.comparing(Util::toUniqueString);
        List<TypeMirror> removedSuperClasses = new ArrayList<>();
        List<TypeMirror> addedSuperClasses = new ArrayList<>();
        oldSuperClasses.sort(typeNameComparator);
        newSuperClasses.sort(typeNameComparator);
        CoIterator<TypeMirror> iterator = new CoIterator<>(oldSuperClasses.iterator(), newSuperClasses.iterator(), typeNameComparator);
        while (iterator.hasNext()) {
            iterator.next();
            TypeMirror oldType = iterator.getLeft();
            TypeMirror newType = iterator.getRight();
            if (oldType == null) {
                addedSuperClasses.add(newType);
            } else if (newType == null) {
                removedSuperClasses.add(oldType);
            }
        }
        // this will give us the equivalent of removed/added superclasses but ordered by the inheritance chain
        // not by name
        removedSuperClasses = retainInCopy(oldSuperClasses, removedSuperClasses);
        addedSuperClasses = retainInCopy(newSuperClasses, addedSuperClasses);
        Iterator<TypeMirror> removedIt = removedSuperClasses.iterator();
        Iterator<TypeMirror> addedIt = addedSuperClasses.iterator();
        // always report the most concrete classes
        if (removedIt.hasNext()) {
            removedIt.next();
        }
        if (addedIt.hasNext()) {
            addedIt.next();
        }
        // ok, now we only have super types left of the most concrete removed/added super class.
        // we are only going to report those that changed their inheritance hierarchy in the other version of the API.
        removeClassesWithEquivalentSuperClassChain(removedIt, getOldTypeEnvironment(), getNewTypeEnvironment());
        removeClassesWithEquivalentSuperClassChain(addedIt, getNewTypeEnvironment(), getOldTypeEnvironment());
        for (TypeMirror t : removedSuperClasses) {
            String str = Util.toHumanReadableString(t);
            ret.add(createDifference(Code.CLASS_NO_LONGER_INHERITS_FROM_CLASS, Code.attachmentsFor(types.oldElement, types.newElement, "superClass", str)));
        }
        for (TypeMirror t : addedSuperClasses) {
            String str = Util.toHumanReadableString(t);
            Code code = types.oldElement.getDeclaringElement().getModifiers().contains(Modifier.FINAL) ? Code.CLASS_FINAL_CLASS_INHERITS_FROM_NEW_CLASS : Code.CLASS_NON_FINAL_CLASS_INHERITS_FROM_NEW_CLASS;
            ret.add(createDifference(code, Code.attachmentsFor(types.oldElement, types.newElement, "superClass", str)));
            // additionally add a difference about checked exceptions
            if (changedToCheckedException(getNewTypeEnvironment().getTypeUtils(), t, oldSuperClasses)) {
                ret.add(createDifference(Code.CLASS_NOW_CHECKED_EXCEPTION, Code.attachmentsFor(types.oldElement, types.newElement)));
            }
        }
        return ret;
    }
    return null;
}
Also used : CoIterator(org.revapi.CoIterator) ArrayList(java.util.ArrayList) Util(org.revapi.java.spi.Util) Difference(org.revapi.Difference) Code(org.revapi.java.spi.Code) JavaTypeElement(org.revapi.java.spi.JavaTypeElement) TypeMirror(javax.lang.model.type.TypeMirror) ArrayList(java.util.ArrayList) List(java.util.List)

Example 3 with CoIterator

use of org.revapi.CoIterator in project revapi by revapi.

the class ExceptionsThrownChanged method doEnd.

@Nullable
@Override
protected List<Difference> doEnd() {
    ActiveElements<JavaMethodElement> methods = popIfActive();
    if (methods == null) {
        return null;
    }
    List<? extends TypeMirror> oldExceptions = new ArrayList<>(methods.oldElement.getModelRepresentation().getThrownTypes());
    List<? extends TypeMirror> newExceptions = new ArrayList<>(methods.newElement.getModelRepresentation().getThrownTypes());
    Comparator<TypeMirror> byClassName = Comparator.comparing(Util::toUniqueString);
    Collections.sort(oldExceptions, byClassName);
    Collections.sort(newExceptions, byClassName);
    CoIterator<TypeMirror> it = new CoIterator<>(oldExceptions.iterator(), newExceptions.iterator(), byClassName);
    List<String> removedRuntimeExceptions = new ArrayList<>();
    List<String> addedRuntimeExceptions = new ArrayList<>();
    List<String> removedCheckedExceptions = new ArrayList<>();
    List<String> addedCheckedExceptions = new ArrayList<>();
    boolean reportSomething = false;
    while (it.hasNext()) {
        it.next();
        TypeMirror oldType = it.getLeft();
        TypeMirror newType = it.getRight();
        if (oldType != null && newType != null) {
            // they match, so move on, nothing to report here
            continue;
        }
        reportSomething = true;
        TypeElement oldException = oldType == null ? null : oldType.accept(CONVERT_TO_ELEMENT, null);
        TypeElement newException = newType == null ? null : newType.accept(CONVERT_TO_ELEMENT, null);
        if (oldException != null) {
            if (isRuntimeException(oldException)) {
                removedRuntimeExceptions.add(oldException.getQualifiedName().toString());
            } else {
                removedCheckedExceptions.add(oldException.getQualifiedName().toString());
            }
        } else if (newException != null) {
            if (isRuntimeException(newException)) {
                addedRuntimeExceptions.add(newException.getQualifiedName().toString());
            } else {
                addedCheckedExceptions.add(newException.getQualifiedName().toString());
            }
        }
    }
    if (!reportSomething) {
        return null;
    }
    List<Difference> ret = new ArrayList<>();
    if (!removedRuntimeExceptions.isEmpty()) {
        removedRuntimeExceptions.forEach(ex -> ret.add(createDifference(Code.METHOD_RUNTIME_EXCEPTION_REMOVED, Code.attachmentsFor(methods.oldElement, methods.newElement, "exception", ex))));
    }
    if (!addedRuntimeExceptions.isEmpty()) {
        addedRuntimeExceptions.forEach(ex -> ret.add(createDifference(Code.METHOD_RUNTIME_EXCEPTION_ADDED, Code.attachmentsFor(methods.oldElement, methods.newElement, "exception", ex))));
    }
    if (!addedCheckedExceptions.isEmpty()) {
        addedCheckedExceptions.forEach(ex -> ret.add(createDifference(Code.METHOD_CHECKED_EXCEPTION_ADDED, Code.attachmentsFor(methods.oldElement, methods.newElement, "exception", ex))));
    }
    if (!removedCheckedExceptions.isEmpty()) {
        removedCheckedExceptions.forEach(ex -> ret.add(createDifference(Code.METHOD_CHECKED_EXCEPTION_REMOVED, Code.attachmentsFor(methods.oldElement, methods.newElement, "exception", ex))));
    }
    return ret;
}
Also used : CoIterator(org.revapi.CoIterator) JavaMethodElement(org.revapi.java.spi.JavaMethodElement) TypeElement(javax.lang.model.element.TypeElement) ArrayList(java.util.ArrayList) Util(org.revapi.java.spi.Util) Difference(org.revapi.Difference) TypeMirror(javax.lang.model.type.TypeMirror) Nullable(javax.annotation.Nullable)

Aggregations

ArrayList (java.util.ArrayList)3 CoIterator (org.revapi.CoIterator)3 List (java.util.List)2 TypeMirror (javax.lang.model.type.TypeMirror)2 Difference (org.revapi.Difference)2 Util (org.revapi.java.spi.Util)2 HashMap (java.util.HashMap)1 IdentityHashMap (java.util.IdentityHashMap)1 Map (java.util.Map)1 TreeMap (java.util.TreeMap)1 Collectors.toList (java.util.stream.Collectors.toList)1 Nullable (javax.annotation.Nullable)1 TypeElement (javax.lang.model.element.TypeElement)1 Element (org.revapi.Element)1 MethodElement (org.revapi.java.model.MethodElement)1 TypeElement (org.revapi.java.model.TypeElement)1 Code (org.revapi.java.spi.Code)1 JavaMethodElement (org.revapi.java.spi.JavaMethodElement)1 JavaTypeElement (org.revapi.java.spi.JavaTypeElement)1