MergeChangespublic class MergeChanges extends Object Convert some remove and add operations into change operations.
Once the numbers of members removed and added are known
we can deduce more information about changes. For instance, if there are
two methods with the same name, and one or more of them has a
parameter type change, then this can only be reported as removing
the old version(s) and adding the new version(s), because there are
multiple methods with the same name.
However, if only one method with a given name is removed, and
only one method with the same name is added, we can convert these
operations to a change operation. For constructors, this is true if
the types are the same. For fields, the field names have to be the same,
though this should never occur, since field names are unique.
Another merge which can be made is if two or more methods with the same name
were marked as removed and added because of changes other than signature.
See the file LICENSE.txt for copyright details. |
Fields Summary |
---|
private static boolean | traceSet to enable increased logging verbosity for debugging. |
Methods Summary |
---|
public static void | diffMethods(MemberDiff methodDiff, MethodAPI oldMethod, MethodAPI newMethod)Track changes in methods related to abstract, native, and
synchronized modifiers here.
// Abstract or not
if (oldMethod.isAbstract_ != newMethod.isAbstract_) {
String changeText = "";
if (oldMethod.isAbstract_)
changeText += "Changed from abstract to non-abstract.";
else
changeText += "Changed from non-abstract to abstract.";
methodDiff.addModifiersChange(changeText);
}
// Native or not
if (Diff.showAllChanges &&
oldMethod.isNative_ != newMethod.isNative_) {
String changeText = "";
if (oldMethod.isNative_)
changeText += "Changed from native to non-native.";
else
changeText += "Changed from non-native to native.";
methodDiff.addModifiersChange(changeText);
}
// Synchronized or not
if (Diff.showAllChanges &&
oldMethod.isSynchronized_ != newMethod.isSynchronized_) {
String changeText = "";
if (oldMethod.isSynchronized_)
changeText += "Changed from synchronized to non-synchronized.";
else
changeText += "Changed from non-synchronized to synchronized.";
methodDiff.addModifiersChange(changeText);
}
| public static void | mergeMultipleMethods(MethodAPI removedMethod, ClassDiff classDiff, PackageDiff pkgDiff)Convert multiple removed and added methods into changed methods.
This handles the case where the methods' signatures are unchanged, but
something else changed.
// Search on the name and signature of the method
int startRemoved = classDiff.methodsRemoved.indexOf(removedMethod);
int endRemoved = classDiff.methodsRemoved.lastIndexOf(removedMethod);
int startAdded = classDiff.methodsAdded.indexOf(removedMethod);
int endAdded = classDiff.methodsAdded.lastIndexOf(removedMethod);
if (startRemoved != -1 && endRemoved != -1 &&
startAdded != -1 && endAdded != -1) {
// Find the index of the current removed method
int removedIdx = -1;
for (int i = startRemoved; i <= endRemoved; i++) {
if (removedMethod.equalSignatures(classDiff.methodsRemoved.get(i))) {
removedIdx = i;
break;
}
}
if (removedIdx == -1) {
System.out.println("Error: removed method index not found");
System.exit(5);
}
// Find the index of the added method with the same signature, if
// it exists, and make sure it is defined locally.
int addedIdx = -1;
for (int i = startAdded; i <= endAdded; i++) {
MethodAPI addedMethod2 = (MethodAPI)(classDiff.methodsAdded.get(i));
if (addedMethod2.inheritedFrom_ == null &&
removedMethod.equalSignatures(addedMethod2))
addedIdx = i;
break;
}
if (addedIdx == -1)
return;
MethodAPI addedMethod = (MethodAPI)(classDiff.methodsAdded.get(addedIdx));
// Create a MemberDiff for this change
MemberDiff methodDiff = new MemberDiff(removedMethod.name_);
methodDiff.oldType_ = removedMethod.returnType_;
methodDiff.newType_ = addedMethod.returnType_;
methodDiff.oldSignature_ = removedMethod.getSignature();
methodDiff.newSignature_ = addedMethod.getSignature();
methodDiff.oldExceptions_ = removedMethod.exceptions_;
methodDiff.newExceptions_ = addedMethod.exceptions_;
// The addModifiersChange field may not have been
// initialized yet if there were multiple methods of the same
// name.
diffMethods(methodDiff, removedMethod, addedMethod);
methodDiff.addModifiersChange(removedMethod.modifiers_.diff(addedMethod.modifiers_));
// Track changes in documentation
if (APIComparator.docChanged(removedMethod.doc_, addedMethod.doc_)) {
String sig = methodDiff.newSignature_;
if (sig.compareTo("void") == 0)
sig = "";
String fqName = pkgDiff.name_ + "." + classDiff.name_;
String link1 = "<a href=\"" + fqName + HTMLReportGenerator.reportFileExt + "\" class=\"hiddenlink\">";
String link2 = "<a href=\"" + fqName + HTMLReportGenerator.reportFileExt + "#" + fqName + "." + addedMethod.name_ + "_changed(" + sig + ")\" class=\"hiddenlink\">";
String id = pkgDiff.name_ + "." + classDiff.name_ + ".dmethod." + addedMethod.name_ + "(" + HTMLReportGenerator.simpleName(sig) + ")";
String title = link1 + "Class <b>" + classDiff.name_ + "</b></a>, " +
link2 + HTMLReportGenerator.simpleName(methodDiff.newType_) + " <b>" + addedMethod.name_ + "(" + HTMLReportGenerator.simpleName(sig) + ")</b></a>";
methodDiff.documentationChange_ = Diff.saveDocDiffs(pkgDiff.name_, classDiff.name_, removedMethod.doc_, addedMethod.doc_, id, title);
}
classDiff.methodsChanged.add(methodDiff);
// Now remove the entries from the remove and add lists
classDiff.methodsRemoved.remove(removedIdx);
classDiff.methodsAdded.remove(addedIdx);
if (trace) {
System.out.println("Merged the removal and addition of method " +
removedMethod.name_ +
" into one change. There were multiple methods of this name.");
}
}
| public static void | mergeRemoveAdd(APIDiff apiDiff)Convert some remove and add operations into change operations.
Note that if a single thread modifies a collection directly while it is
iterating over the collection with a fail-fast iterator, the iterator
will throw java.util.ConcurrentModificationException
// Go through all the ClassDiff objects searching for the above cases.
Iterator iter = apiDiff.packagesChanged.iterator();
while (iter.hasNext()) {
PackageDiff pkgDiff = (PackageDiff)(iter.next());
Iterator iter2 = pkgDiff.classesChanged.iterator();
while (iter2.hasNext()) {
ClassDiff classDiff = (ClassDiff)(iter2.next());
// Note: using iterators to step through the members gives a
// ConcurrentModificationException exception with large files.
// Constructors
ConstructorAPI[] ctorArr = new ConstructorAPI[classDiff.ctorsRemoved.size()];
ctorArr = (ConstructorAPI[])classDiff.ctorsRemoved.toArray(ctorArr);
for (int ctorIdx = 0; ctorIdx < ctorArr.length; ctorIdx++) {
ConstructorAPI removedCtor = ctorArr[ctorIdx];
mergeRemoveAddCtor(removedCtor, classDiff, pkgDiff);
}
// Methods
MethodAPI[] methodArr = new MethodAPI[classDiff.methodsRemoved.size()];
methodArr = (MethodAPI[])classDiff.methodsRemoved.toArray(methodArr);
for (int methodIdx = 0; methodIdx < methodArr.length; methodIdx++) {
MethodAPI removedMethod = methodArr[methodIdx];
// Only merge locally defined methods
if (removedMethod.inheritedFrom_ == null)
mergeRemoveAddMethod(removedMethod, classDiff, pkgDiff);
}
// Fields
FieldAPI[] fieldArr = new FieldAPI[classDiff.fieldsRemoved.size()];
fieldArr = (FieldAPI[])classDiff.fieldsRemoved.toArray(fieldArr);
for (int fieldIdx = 0; fieldIdx < fieldArr.length; fieldIdx++) {
FieldAPI removedField = fieldArr[fieldIdx];
// Only merge locally defined fields
if (removedField.inheritedFrom_ == null)
mergeRemoveAddField(removedField, classDiff, pkgDiff);
}
}
}
| public static void | mergeRemoveAddCtor(ConstructorAPI removedCtor, ClassDiff classDiff, PackageDiff pkgDiff)Convert some removed and added constructors into changed constructors.
// Search on the type of the constructor
int startRemoved = classDiff.ctorsRemoved.indexOf(removedCtor);
int endRemoved = classDiff.ctorsRemoved.lastIndexOf(removedCtor);
int startAdded = classDiff.ctorsAdded.indexOf(removedCtor);
int endAdded = classDiff.ctorsAdded.lastIndexOf(removedCtor);
if (startRemoved != -1 && startRemoved == endRemoved &&
startAdded != -1 && startAdded == endAdded) {
// There is only one constructor with the type of the
// removedCtor in both the removed and added constructors.
ConstructorAPI addedCtor = (ConstructorAPI)(classDiff.ctorsAdded.get(startAdded));
// Create a MemberDiff for this change
MemberDiff ctorDiff = new MemberDiff(classDiff.name_);
ctorDiff.oldType_ = removedCtor.type_;
ctorDiff.newType_ = addedCtor.type_; // Should be the same as removedCtor.type
ctorDiff.oldExceptions_ = removedCtor.exceptions_;
ctorDiff.newExceptions_ = addedCtor.exceptions_;
ctorDiff.addModifiersChange(removedCtor.modifiers_.diff(addedCtor.modifiers_));
// Track changes in documentation
if (APIComparator.docChanged(removedCtor.doc_, addedCtor.doc_)) {
String type = ctorDiff.newType_;
if (type.compareTo("void") == 0)
type = "";
String fqName = pkgDiff.name_ + "." + classDiff.name_;
String link1 = "<a href=\"" + fqName + HTMLReportGenerator.reportFileExt + "\" class=\"hiddenlink\">";
String link2 = "<a href=\"" + fqName + HTMLReportGenerator.reportFileExt + "#" + fqName + ".ctor_changed(" + type + ")\" class=\"hiddenlink\">";
String id = pkgDiff.name_ + "." + classDiff.name_ + ".ctor(" + HTMLReportGenerator.simpleName(type) + ")";
String title = link1 + "Class <b>" + classDiff.name_ +
"</b></a>, " + link2 + "constructor <b>" + classDiff.name_ + "(" + HTMLReportGenerator.simpleName(type) + ")</b></a>";
ctorDiff.documentationChange_ = Diff.saveDocDiffs(pkgDiff.name_, classDiff.name_, removedCtor.doc_, addedCtor.doc_, id, title);
}
classDiff.ctorsChanged.add(ctorDiff);
// Now remove the entries from the remove and add lists
classDiff.ctorsRemoved.remove(startRemoved);
classDiff.ctorsAdded.remove(startAdded);
if (trace && ctorDiff.modifiersChange_ != null)
System.out.println("Merged the removal and addition of constructor into one change: " + ctorDiff.modifiersChange_);
}
| public static void | mergeRemoveAddField(FieldAPI removedField, ClassDiff classDiff, PackageDiff pkgDiff)Convert some removed and added fields into changed fields.
// Search on the name of the field
int startRemoved = classDiff.fieldsRemoved.indexOf(removedField);
int endRemoved = classDiff.fieldsRemoved.lastIndexOf(removedField);
int startAdded = classDiff.fieldsAdded.indexOf(removedField);
int endAdded = classDiff.fieldsAdded.lastIndexOf(removedField);
if (startRemoved != -1 && startRemoved == endRemoved &&
startAdded != -1 && startAdded == endAdded) {
// There is only one field with the name of the
// removedField in both the removed and added fields.
FieldAPI addedField = (FieldAPI)(classDiff.fieldsAdded.get(startAdded));
if (addedField.inheritedFrom_ == null) {
// Create a MemberDiff for this change
MemberDiff fieldDiff = new MemberDiff(removedField.name_);
fieldDiff.oldType_ = removedField.type_;
fieldDiff.newType_ = addedField.type_;
fieldDiff.addModifiersChange(removedField.modifiers_.diff(addedField.modifiers_));
// Track changes in documentation
if (APIComparator.docChanged(removedField.doc_, addedField.doc_)) {
String fqName = pkgDiff.name_ + "." + classDiff.name_;
String link1 = "<a href=\"" + fqName + HTMLReportGenerator.reportFileExt + "\" class=\"hiddenlink\">";
String link2 = "<a href=\"" + fqName + HTMLReportGenerator.reportFileExt + "#" + fqName + "." + addedField.name_ + "\" class=\"hiddenlink\">";
String id = pkgDiff.name_ + "." + classDiff.name_ + ".field." + addedField.name_;
String title = link1 + "Class <b>" + classDiff.name_ + "</b></a>, " +
link2 + HTMLReportGenerator.simpleName(fieldDiff.newType_) + " <b>" + addedField.name_ + "</b></a>";
fieldDiff.documentationChange_ = Diff.saveDocDiffs(pkgDiff.name_, classDiff.name_, removedField.doc_, addedField.doc_, id, title);
}
classDiff.fieldsChanged.add(fieldDiff);
// Now remove the entries from the remove and add lists
classDiff.fieldsRemoved.remove(startRemoved);
classDiff.fieldsAdded.remove(startAdded);
if (trace) {
System.out.println("Merged the removal and addition of field " +
removedField.name_ +
" into one change");
}
} //if (addedField.inheritedFrom == null)
}
| public static void | mergeRemoveAddMethod(MethodAPI removedMethod, ClassDiff classDiff, PackageDiff pkgDiff)Convert some removed and added methods into changed methods.
mergeSingleMethods(removedMethod, classDiff, pkgDiff);
mergeMultipleMethods(removedMethod, classDiff, pkgDiff);
| public static void | mergeSingleMethods(MethodAPI removedMethod, ClassDiff classDiff, PackageDiff pkgDiff)Convert single removed and added methods into a changed method.
// Search on the name of the method
int startRemoved = classDiff.methodsRemoved.indexOf(removedMethod);
int endRemoved = classDiff.methodsRemoved.lastIndexOf(removedMethod);
int startAdded = classDiff.methodsAdded.indexOf(removedMethod);
int endAdded = classDiff.methodsAdded.lastIndexOf(removedMethod);
if (startRemoved != -1 && startRemoved == endRemoved &&
startAdded != -1 && startAdded == endAdded) {
// There is only one method with the name of the
// removedMethod in both the removed and added methods.
MethodAPI addedMethod = (MethodAPI)(classDiff.methodsAdded.get(startAdded));
if (addedMethod.inheritedFrom_ == null) {
// Create a MemberDiff for this change
MemberDiff methodDiff = new MemberDiff(removedMethod.name_);
methodDiff.oldType_ = removedMethod.returnType_;
methodDiff.newType_ = addedMethod.returnType_;
methodDiff.oldSignature_ = removedMethod.getSignature();
methodDiff.newSignature_ = addedMethod.getSignature();
methodDiff.oldExceptions_ = removedMethod.exceptions_;
methodDiff.newExceptions_ = addedMethod.exceptions_;
// The addModifiersChange field may not have been
// initialized yet if there were multiple methods of the same
// name.
diffMethods(methodDiff, removedMethod, addedMethod);
methodDiff.addModifiersChange(removedMethod.modifiers_.diff(addedMethod.modifiers_));
// Track changes in documentation
if (APIComparator.docChanged(removedMethod.doc_, addedMethod.doc_)) {
String sig = methodDiff.newSignature_;
if (sig.compareTo("void") == 0)
sig = "";
String fqName = pkgDiff.name_ + "." + classDiff.name_;
String link1 = "<a href=\"" + fqName + HTMLReportGenerator.reportFileExt + "\" class=\"hiddenlink\">";
String link2 = "<a href=\"" + fqName + HTMLReportGenerator.reportFileExt + "#" + fqName + "." + addedMethod.name_ + "_changed(" + sig + ")\" class=\"hiddenlink\">";
String id = pkgDiff.name_ + "." + classDiff.name_ + ".dmethod." + addedMethod.name_ + "(" + HTMLReportGenerator.simpleName(sig) + ")";
String title = link1 + "Class <b>" + classDiff.name_ + "</b></a>, " +
link2 + HTMLReportGenerator.simpleName(methodDiff.newType_) + " <b>" + addedMethod.name_ + "(" + HTMLReportGenerator.simpleName(sig) + ")</b></a>";
methodDiff.documentationChange_ = Diff.saveDocDiffs(pkgDiff.name_, classDiff.name_, removedMethod.doc_, addedMethod.doc_, id, title);
}
classDiff.methodsChanged.add(methodDiff);
// Now remove the entries from the remove and add lists
classDiff.methodsRemoved.remove(startRemoved);
classDiff.methodsAdded.remove(startAdded);
if (trace) {
System.out.println("Merged the removal and addition of method " +
removedMethod.name_ +
" into one change");
}
} //if (addedMethod.inheritedFrom_ == null)
}
|
|