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);
}
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;
}
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;
}
Aggregations