196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Projectpackage jdiff;
296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project
396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Projectimport java.util.*;
496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project
596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project/**
696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project * Convert some remove and add operations into change operations.
796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project *
896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project * Once the numbers of members removed and added are known
996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project * we can deduce more information about changes. For instance, if there are
1096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project * two methods with the same name, and one or more of them has a
1196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project * parameter type change, then this can only be reported as removing
1296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project * the old version(s) and adding the new version(s), because there are
1396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project * multiple methods with the same name.
1496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project *
1596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project * However, if only <i>one</i> method with a given name is removed, and
1696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project * only <i>one</i> method with the same name is added, we can convert these
1796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project * operations to a change operation. For constructors, this is true if
1896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project * the types are the same. For fields, the field names have to be the same,
1996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project * though this should never occur, since field names are unique.
2096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project *
2196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project * Another merge which can be made is if two or more methods with the same name
2296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project * were marked as removed and added because of changes other than signature.
2396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project *
2496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project * See the file LICENSE.txt for copyright details.
2596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project * @author Matthew Doar, mdoar@pobox.com
2696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project */
2796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Projectclass MergeChanges {
2896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project
2996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    /**
3096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project     * Convert some remove and add operations into change operations.
3196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project     *
3296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project     * Note that if a single thread modifies a collection directly while it is
3396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project     * iterating over the collection with a fail-fast iterator, the iterator
3496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project     * will throw java.util.ConcurrentModificationException
3596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project     */
3696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    public static void mergeRemoveAdd(APIDiff apiDiff) {
3796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        // Go through all the ClassDiff objects searching for the above cases.
3896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        Iterator iter = apiDiff.packagesChanged.iterator();
3996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        while (iter.hasNext()) {
4096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            PackageDiff pkgDiff = (PackageDiff)(iter.next());
4196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            Iterator iter2 = pkgDiff.classesChanged.iterator();
4296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            while (iter2.hasNext()) {
4396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                ClassDiff classDiff = (ClassDiff)(iter2.next());
4496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                // Note: using iterators to step through the members gives a
4596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                // ConcurrentModificationException exception with large files.
4696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                // Constructors
4796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                ConstructorAPI[] ctorArr = new ConstructorAPI[classDiff.ctorsRemoved.size()];
4896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                ctorArr = (ConstructorAPI[])classDiff.ctorsRemoved.toArray(ctorArr);
4996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                for (int ctorIdx = 0; ctorIdx < ctorArr.length; ctorIdx++) {
5096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    ConstructorAPI removedCtor = ctorArr[ctorIdx];
5196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    mergeRemoveAddCtor(removedCtor, classDiff, pkgDiff);
5296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                }
5396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                // Methods
5496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                MethodAPI[] methodArr = new MethodAPI[classDiff.methodsRemoved.size()];
5596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                methodArr = (MethodAPI[])classDiff.methodsRemoved.toArray(methodArr);
5696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                for (int methodIdx = 0; methodIdx < methodArr.length; methodIdx++) {
5796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    MethodAPI removedMethod = methodArr[methodIdx];
5896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    // Only merge locally defined methods
5996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    if (removedMethod.inheritedFrom_ == null)
6096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                        mergeRemoveAddMethod(removedMethod, classDiff, pkgDiff);
6196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                }
6296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                // Fields
6396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                FieldAPI[] fieldArr = new FieldAPI[classDiff.fieldsRemoved.size()];
6496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                fieldArr = (FieldAPI[])classDiff.fieldsRemoved.toArray(fieldArr);
6596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                for (int fieldIdx = 0; fieldIdx < fieldArr.length; fieldIdx++) {
6696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    FieldAPI removedField = fieldArr[fieldIdx];
6796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    // Only merge locally defined fields
6896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    if (removedField.inheritedFrom_ == null)
6996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                        mergeRemoveAddField(removedField, classDiff, pkgDiff);
7096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                }
7196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            }
7296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        }
7396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    }
7496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project
7596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    /**
7696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project     * Convert some removed and added constructors into changed constructors.
7796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project     */
7896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    public static void mergeRemoveAddCtor(ConstructorAPI removedCtor, ClassDiff classDiff, PackageDiff pkgDiff) {
7996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        // Search on the type of the constructor
8096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        int startRemoved = classDiff.ctorsRemoved.indexOf(removedCtor);
8196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        int endRemoved = classDiff.ctorsRemoved.lastIndexOf(removedCtor);
8296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        int startAdded = classDiff.ctorsAdded.indexOf(removedCtor);
8396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        int endAdded = classDiff.ctorsAdded.lastIndexOf(removedCtor);
8496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        if (startRemoved != -1 && startRemoved == endRemoved &&
8596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            startAdded != -1 && startAdded == endAdded) {
8696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            // There is only one constructor with the type of the
8796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            // removedCtor in both the removed and added constructors.
8896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            ConstructorAPI addedCtor = (ConstructorAPI)(classDiff.ctorsAdded.get(startAdded));
8996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            // Create a MemberDiff for this change
9096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            MemberDiff ctorDiff = new MemberDiff(classDiff.name_);
9196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            ctorDiff.oldType_ = removedCtor.type_;
9296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            ctorDiff.newType_ = addedCtor.type_; // Should be the same as removedCtor.type
9396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            ctorDiff.oldExceptions_ = removedCtor.exceptions_;
9496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            ctorDiff.newExceptions_ = addedCtor.exceptions_;
9596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            ctorDiff.addModifiersChange(removedCtor.modifiers_.diff(addedCtor.modifiers_));
9696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            // Track changes in documentation
9796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            if (APIComparator.docChanged(removedCtor.doc_, addedCtor.doc_)) {
9896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                String type = ctorDiff.newType_;
9996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                if (type.compareTo("void") == 0)
10096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    type = "";
10196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                String fqName = pkgDiff.name_ + "." + classDiff.name_;
10296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                String link1 = "<a href=\"" + fqName + HTMLReportGenerator.reportFileExt + "\" class=\"hiddenlink\">";
10396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                String link2 = "<a href=\"" + fqName + HTMLReportGenerator.reportFileExt + "#" + fqName + ".ctor_changed(" + type + ")\" class=\"hiddenlink\">";
10496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                String id = pkgDiff.name_ + "." + classDiff.name_ + ".ctor(" + HTMLReportGenerator.simpleName(type) + ")";
10596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                String title = link1 + "Class <b>" + classDiff.name_ +
10696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    "</b></a>, " + link2 + "constructor <b>" + classDiff.name_ + "(" + HTMLReportGenerator.simpleName(type) + ")</b></a>";
10796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                ctorDiff.documentationChange_ = Diff.saveDocDiffs(pkgDiff.name_, classDiff.name_, removedCtor.doc_, addedCtor.doc_, id, title);
10896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            }
10996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            classDiff.ctorsChanged.add(ctorDiff);
11096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            // Now remove the entries from the remove and add lists
11196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            classDiff.ctorsRemoved.remove(startRemoved);
11296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            classDiff.ctorsAdded.remove(startAdded);
11396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            if (trace && ctorDiff.modifiersChange_ != null)
11496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                System.out.println("Merged the removal and addition of constructor into one change: " + ctorDiff.modifiersChange_);
11596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        }
11696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    }
11796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project
11896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    /**
11996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project     * Convert some removed and added methods into changed methods.
12096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project     */
12196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    public static void mergeRemoveAddMethod(MethodAPI removedMethod,
12296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                                            ClassDiff classDiff,
12396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                                            PackageDiff pkgDiff) {
12496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        mergeSingleMethods(removedMethod, classDiff, pkgDiff);
12596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        mergeMultipleMethods(removedMethod, classDiff, pkgDiff);
12696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    }
12796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project
12896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    /**
12996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project     * Convert single removed and added methods into a changed method.
13096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project     */
13196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    public static void mergeSingleMethods(MethodAPI removedMethod, ClassDiff classDiff, PackageDiff pkgDiff) {
13296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        // Search on the name of the method
13396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        int startRemoved = classDiff.methodsRemoved.indexOf(removedMethod);
13496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        int endRemoved = classDiff.methodsRemoved.lastIndexOf(removedMethod);
13596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        int startAdded = classDiff.methodsAdded.indexOf(removedMethod);
13696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        int endAdded = classDiff.methodsAdded.lastIndexOf(removedMethod);
13796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        if (startRemoved != -1 && startRemoved == endRemoved &&
13896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            startAdded != -1 && startAdded == endAdded) {
13996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            // There is only one method with the name of the
14096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            // removedMethod in both the removed and added methods.
14196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            MethodAPI addedMethod = (MethodAPI)(classDiff.methodsAdded.get(startAdded));
14296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            if (addedMethod.inheritedFrom_ == null) {
14396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                // Create a MemberDiff for this change
14496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                MemberDiff methodDiff = new MemberDiff(removedMethod.name_);
14596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                methodDiff.oldType_ = removedMethod.returnType_;
14696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                methodDiff.newType_ = addedMethod.returnType_;
14796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                methodDiff.oldSignature_ = removedMethod.getSignature();
14896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                methodDiff.newSignature_ = addedMethod.getSignature();
14996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                methodDiff.oldExceptions_ = removedMethod.exceptions_;
15096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                methodDiff.newExceptions_ = addedMethod.exceptions_;
15196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                // The addModifiersChange field may not have been
15296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                // initialized yet if there were multiple methods of the same
15396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                // name.
15496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                diffMethods(methodDiff, removedMethod, addedMethod);
15596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                methodDiff.addModifiersChange(removedMethod.modifiers_.diff(addedMethod.modifiers_));
15696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                // Track changes in documentation
15796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                if (APIComparator.docChanged(removedMethod.doc_, addedMethod.doc_)) {
15896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    String sig = methodDiff.newSignature_;
15996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    if (sig.compareTo("void") == 0)
16096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                        sig = "";
16196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    String fqName = pkgDiff.name_ + "." + classDiff.name_;
16296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    String link1 = "<a href=\"" + fqName + HTMLReportGenerator.reportFileExt + "\" class=\"hiddenlink\">";
16396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    String link2 = "<a href=\"" + fqName + HTMLReportGenerator.reportFileExt + "#" + fqName + "." + addedMethod.name_ + "_changed(" + sig + ")\" class=\"hiddenlink\">";
16496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    String id = pkgDiff.name_ + "." + classDiff.name_ + ".dmethod." + addedMethod.name_ + "(" + HTMLReportGenerator.simpleName(sig) + ")";
16596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    String title = link1 + "Class <b>" + classDiff.name_ + "</b></a>, " +
16696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                        link2 +  HTMLReportGenerator.simpleName(methodDiff.newType_) + " <b>" + addedMethod.name_ + "(" + HTMLReportGenerator.simpleName(sig) + ")</b></a>";
16796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    methodDiff.documentationChange_ = Diff.saveDocDiffs(pkgDiff.name_, classDiff.name_, removedMethod.doc_, addedMethod.doc_, id, title);
16896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                }
16996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                classDiff.methodsChanged.add(methodDiff);
17096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                // Now remove the entries from the remove and add lists
17196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                classDiff.methodsRemoved.remove(startRemoved);
17296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                classDiff.methodsAdded.remove(startAdded);
17396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                if (trace) {
17496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    System.out.println("Merged the removal and addition of method " +
17596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                                       removedMethod.name_ +
17696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                                       " into one change");
17796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                }
17896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            } //if (addedMethod.inheritedFrom_ == null)
17996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        }
18096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    }
18196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project
18296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    /**
18396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project     * Convert multiple removed and added methods into changed methods.
18496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project     * This handles the case where the methods' signatures are unchanged, but
18596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project     * something else changed.
18696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project     */
18796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    public static void mergeMultipleMethods(MethodAPI removedMethod, ClassDiff classDiff, PackageDiff pkgDiff) {
18896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        // Search on the name and signature of the method
18996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        int startRemoved = classDiff.methodsRemoved.indexOf(removedMethod);
19096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        int endRemoved = classDiff.methodsRemoved.lastIndexOf(removedMethod);
19196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        int startAdded = classDiff.methodsAdded.indexOf(removedMethod);
19296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        int endAdded = classDiff.methodsAdded.lastIndexOf(removedMethod);
19396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        if (startRemoved != -1 && endRemoved != -1 &&
19496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            startAdded != -1 && endAdded != -1) {
19596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            // Find the index of the current removed method
19696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            int removedIdx = -1;
19796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            for (int i = startRemoved; i <= endRemoved; i++) {
19896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                if (removedMethod.equalSignatures(classDiff.methodsRemoved.get(i))) {
19996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    removedIdx = i;
20096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    break;
20196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                }
20296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            }
20396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            if (removedIdx == -1) {
20496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                System.out.println("Error: removed method index not found");
20596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                System.exit(5);
20696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            }
20796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            // Find the index of the added method with the same signature, if
20896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            // it exists, and make sure it is defined locally.
20996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            int addedIdx = -1;
21096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            for (int i = startAdded; i <= endAdded; i++) {
21196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                MethodAPI addedMethod2 = (MethodAPI)(classDiff.methodsAdded.get(i));
21296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                if (addedMethod2.inheritedFrom_ == null &&
21396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    removedMethod.equalSignatures(addedMethod2))
21496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    addedIdx = i;
21596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    break;
21696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            }
21796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            if (addedIdx == -1)
21896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                return;
21996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            MethodAPI addedMethod = (MethodAPI)(classDiff.methodsAdded.get(addedIdx));
22096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            // Create a MemberDiff for this change
22196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            MemberDiff methodDiff = new MemberDiff(removedMethod.name_);
22296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            methodDiff.oldType_ = removedMethod.returnType_;
22396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            methodDiff.newType_ = addedMethod.returnType_;
22496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            methodDiff.oldSignature_ = removedMethod.getSignature();
22596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            methodDiff.newSignature_ = addedMethod.getSignature();
22696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            methodDiff.oldExceptions_ = removedMethod.exceptions_;
22796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            methodDiff.newExceptions_ = addedMethod.exceptions_;
22896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                // The addModifiersChange field may not have been
22996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                // initialized yet if there were multiple methods of the same
23096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                // name.
23196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                diffMethods(methodDiff, removedMethod, addedMethod);
23296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            methodDiff.addModifiersChange(removedMethod.modifiers_.diff(addedMethod.modifiers_));
23396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            // Track changes in documentation
23496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            if (APIComparator.docChanged(removedMethod.doc_, addedMethod.doc_)) {
23596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                String sig = methodDiff.newSignature_;
23696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                if (sig.compareTo("void") == 0)
23796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    sig = "";
23896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                String fqName = pkgDiff.name_ + "." + classDiff.name_;
23996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                String link1 = "<a href=\"" + fqName + HTMLReportGenerator.reportFileExt + "\" class=\"hiddenlink\">";
24096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                String link2 = "<a href=\"" + fqName + HTMLReportGenerator.reportFileExt + "#" + fqName + "." + addedMethod.name_ + "_changed(" + sig + ")\" class=\"hiddenlink\">";
24196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                String id = pkgDiff.name_ + "." + classDiff.name_ + ".dmethod." + addedMethod.name_ + "(" + HTMLReportGenerator.simpleName(sig) + ")";
24296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                String title = link1 + "Class <b>" + classDiff.name_ + "</b></a>, " +
24396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    link2 +  HTMLReportGenerator.simpleName(methodDiff.newType_) + " <b>" + addedMethod.name_ + "(" + HTMLReportGenerator.simpleName(sig) + ")</b></a>";
24496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                methodDiff.documentationChange_ = Diff.saveDocDiffs(pkgDiff.name_, classDiff.name_, removedMethod.doc_, addedMethod.doc_, id, title);
24596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            }
24696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            classDiff.methodsChanged.add(methodDiff);
24796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            // Now remove the entries from the remove and add lists
24896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            classDiff.methodsRemoved.remove(removedIdx);
24996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            classDiff.methodsAdded.remove(addedIdx);
25096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            if (trace) {
25196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                System.out.println("Merged the removal and addition of method " +
25296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                                   removedMethod.name_ +
25396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                                   " into one change. There were multiple methods of this name.");
25496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            }
25596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        }
25696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    }
25796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project
25896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    /**
25996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project     * Track changes in methods related to abstract, native, and
26096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project     * synchronized modifiers here.
26196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project     */
26296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    public static void diffMethods(MemberDiff methodDiff,
26396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                                   MethodAPI oldMethod,
26496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                                   MethodAPI newMethod) {
26596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        // Abstract or not
26696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        if (oldMethod.isAbstract_ != newMethod.isAbstract_) {
26796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            String changeText = "";
26896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            if (oldMethod.isAbstract_)
26996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                changeText += "Changed from abstract to non-abstract.";
27096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            else
27196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                changeText += "Changed from non-abstract to abstract.";
27296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            methodDiff.addModifiersChange(changeText);
27396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        }
27496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        // Native or not
27596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        if (Diff.showAllChanges &&
27696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project	    oldMethod.isNative_ != newMethod.isNative_) {
27796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            String changeText = "";
27896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            if (oldMethod.isNative_)
27996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                changeText += "Changed from native to non-native.";
28096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            else
28196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                changeText += "Changed from non-native to native.";
28296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            methodDiff.addModifiersChange(changeText);
28396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        }
28496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        // Synchronized or not
28596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        if (Diff.showAllChanges &&
28696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project	    oldMethod.isSynchronized_ != newMethod.isSynchronized_) {
28796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            String changeText = "";
28896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            if (oldMethod.isSynchronized_)
28996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                changeText += "Changed from synchronized to non-synchronized.";
29096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            else
29196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                changeText += "Changed from non-synchronized to synchronized.";
29296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            methodDiff.addModifiersChange(changeText);
29396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        }
29496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    }
29596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project
29696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    /**
29796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project     * Convert some removed and added fields into changed fields.
29896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project     */
29996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    public static void mergeRemoveAddField(FieldAPI removedField, ClassDiff classDiff, PackageDiff pkgDiff) {
30096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        // Search on the name of the field
30196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        int startRemoved = classDiff.fieldsRemoved.indexOf(removedField);
30296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        int endRemoved = classDiff.fieldsRemoved.lastIndexOf(removedField);
30396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        int startAdded = classDiff.fieldsAdded.indexOf(removedField);
30496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        int endAdded = classDiff.fieldsAdded.lastIndexOf(removedField);
30596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        if (startRemoved != -1 && startRemoved == endRemoved &&
30696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            startAdded != -1 && startAdded == endAdded) {
30796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            // There is only one field with the name of the
30896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            // removedField in both the removed and added fields.
30996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            FieldAPI addedField = (FieldAPI)(classDiff.fieldsAdded.get(startAdded));
31096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            if (addedField.inheritedFrom_ == null) {
31196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                // Create a MemberDiff for this change
31296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                MemberDiff fieldDiff = new MemberDiff(removedField.name_);
31396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                fieldDiff.oldType_ = removedField.type_;
31496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                fieldDiff.newType_ = addedField.type_;
31596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                fieldDiff.addModifiersChange(removedField.modifiers_.diff(addedField.modifiers_));
31696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                // Track changes in documentation
31796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                if (APIComparator.docChanged(removedField.doc_, addedField.doc_)) {
31896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    String fqName = pkgDiff.name_ + "." + classDiff.name_;
31996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    String link1 = "<a href=\"" + fqName + HTMLReportGenerator.reportFileExt + "\" class=\"hiddenlink\">";
32096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    String link2 = "<a href=\"" + fqName + HTMLReportGenerator.reportFileExt + "#" + fqName + "." + addedField.name_ + "\" class=\"hiddenlink\">";
32196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    String id = pkgDiff.name_ + "." + classDiff.name_ + ".field." + addedField.name_;
32296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    String title = link1 + "Class <b>" + classDiff.name_ + "</b></a>, " +
32396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                        link2 + HTMLReportGenerator.simpleName(fieldDiff.newType_) + " <b>" + addedField.name_ + "</b></a>";
32496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    fieldDiff.documentationChange_ = Diff.saveDocDiffs(pkgDiff.name_, classDiff.name_, removedField.doc_, addedField.doc_, id, title);
32596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                }
32696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                classDiff.fieldsChanged.add(fieldDiff);
32796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                // Now remove the entries from the remove and add lists
32896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                classDiff.fieldsRemoved.remove(startRemoved);
32996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                classDiff.fieldsAdded.remove(startAdded);
33096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                if (trace) {
33196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                    System.out.println("Merged the removal and addition of field " +
33296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                                       removedField.name_ +
33396b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                                       " into one change");
33496b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project                }
33596b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project            } //if (addedField.inheritedFrom == null)
33696b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project        }
33796b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    }
33896b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project
33996b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    /** Set to enable increased logging verbosity for debugging. */
34096b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project    private static boolean trace = false;
34196b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project
34296b00fec6cd6068c1c5ae09de0358340c0ec499eThe Android Open Source Project}
343