Improve dexdeps output.

This adds the list of referenced classes to the output.  Previously it
only emitted fields and methods, so classes that were referenced by type
only (e.g. some dalvik annotation classes) weren't visible.

The XML output now more closely resembles the API files, having
distinct package/class tags.  We no longer emit a return type for
constructors.
diff --git a/tools/dexdeps/src/com/android/dexdeps/ClassRef.java b/tools/dexdeps/src/com/android/dexdeps/ClassRef.java
new file mode 100644
index 0000000..8065a0a
--- /dev/null
+++ b/tools/dexdeps/src/com/android/dexdeps/ClassRef.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dexdeps;
+
+import java.util.ArrayList;
+
+public class ClassRef {
+    private String mClassName;
+    private ArrayList<FieldRef> mFieldRefs;
+    private ArrayList<MethodRef> mMethodRefs;
+
+    /**
+     * Initializes a new class reference.
+     */
+    public ClassRef(String className) {
+        mClassName = className;
+        mFieldRefs = new ArrayList<FieldRef>();
+        mMethodRefs = new ArrayList<MethodRef>();
+    }
+
+    /**
+     * Adds the field to the field list.
+     */
+    public void addField(FieldRef fref) {
+        mFieldRefs.add(fref);
+    }
+
+    /**
+     * Returns the field list as an array.
+     */
+    public FieldRef[] getFieldArray() {
+        return mFieldRefs.toArray(new FieldRef[mFieldRefs.size()]);
+    }
+
+    /**
+     * Adds the method to the method list.
+     */
+    public void addMethod(MethodRef mref) {
+        mMethodRefs.add(mref);
+    }
+
+    /**
+     * Returns the method list as an array.
+     */
+    public MethodRef[] getMethodArray() {
+        return mMethodRefs.toArray(new MethodRef[mMethodRefs.size()]);
+    }
+
+    /**
+     * Gets the class name.
+     */
+    public String getName() {
+        return mClassName;
+    }
+}
+
diff --git a/tools/dexdeps/src/com/android/dexdeps/DexData.java b/tools/dexdeps/src/com/android/dexdeps/DexData.java
index fa79d60..0a7fa2d 100644
--- a/tools/dexdeps/src/com/android/dexdeps/DexData.java
+++ b/tools/dexdeps/src/com/android/dexdeps/DexData.java
@@ -341,66 +341,75 @@
     }
 
     /**
-     * Returns an array with all of the field references that don't
-     * correspond to classes in the DEX file.
+     * Returns an array with all of the class references that don't
+     * correspond to classes in the DEX file.  Each class reference has
+     * a list of the referenced fields and methods associated with
+     * that class.
      */
-    public FieldRef[] getExternalFieldReferences() {
-        // get a count
+    public ClassRef[] getExternalReferences() {
+        // create a sparse array of ClassRef that parallels mTypeIds
+        ClassRef[] sparseRefs = new ClassRef[mTypeIds.length];
+
+        // create entries for all externally-referenced classes
         int count = 0;
-        for (int i = 0; i < mFieldIds.length; i++) {
-            if (!mTypeIds[mFieldIds[i].classIdx].internal)
+        for (int i = 0; i < mTypeIds.length; i++) {
+            if (!mTypeIds[i].internal) {
+                sparseRefs[i] =
+                    new ClassRef(mStrings[mTypeIds[i].descriptorIdx]);
                 count++;
-        }
-
-        //System.out.println("count is " + count + " of " + mFieldIds.length);
-
-        FieldRef[] fieldRefs = new FieldRef[count];
-        count = 0;
-        for (int i = 0; i < mFieldIds.length; i++) {
-            if (!mTypeIds[mFieldIds[i].classIdx].internal) {
-                FieldIdItem fieldId = mFieldIds[i];
-                fieldRefs[count++] =
-                    new FieldRef(classNameFromTypeIndex(fieldId.classIdx),
-                                 classNameFromTypeIndex(fieldId.typeIdx),
-                                 mStrings[fieldId.nameIdx]);
             }
         }
 
-        assert count == fieldRefs.length;
+        // add fields and methods to the appropriate class entry
+        addExternalFieldReferences(sparseRefs);
+        addExternalMethodReferences(sparseRefs);
 
-        return fieldRefs;
+        // crunch out the sparseness
+        ClassRef[] classRefs = new ClassRef[count];
+        int idx = 0;
+        for (int i = 0; i < mTypeIds.length; i++) {
+            if (sparseRefs[i] != null)
+                classRefs[idx++] = sparseRefs[i];
+        }
+
+        assert idx == count;
+
+        return classRefs;
     }
 
     /**
-     * Returns an array with all of the method references that don't
-     * correspond to classes in the DEX file.
+     * Runs through the list of field references, inserting external
+     * references into the appropriate ClassRef.
      */
-    public MethodRef[] getExternalMethodReferences() {
-        // get a count
-        int count = 0;
-        for (int i = 0; i < mMethodIds.length; i++) {
-            if (!mTypeIds[mMethodIds[i].classIdx].internal)
-                count++;
+    private void addExternalFieldReferences(ClassRef[] sparseRefs) {
+        for (int i = 0; i < mFieldIds.length; i++) {
+            if (!mTypeIds[mFieldIds[i].classIdx].internal) {
+                FieldIdItem fieldId = mFieldIds[i];
+                FieldRef newFieldRef = new FieldRef(
+                        classNameFromTypeIndex(fieldId.classIdx),
+                        classNameFromTypeIndex(fieldId.typeIdx),
+                        mStrings[fieldId.nameIdx]);
+                sparseRefs[mFieldIds[i].classIdx].addField(newFieldRef);
+            }
         }
+    }
 
-        //System.out.println("count is " + count + " of " + mMethodIds.length);
-
-        MethodRef[] methodRefs = new MethodRef[count];
-        count = 0;
+    /**
+     * Runs through the list of method references, inserting external
+     * references into the appropriate ClassRef.
+     */
+    private void addExternalMethodReferences(ClassRef[] sparseRefs) {
         for (int i = 0; i < mMethodIds.length; i++) {
             if (!mTypeIds[mMethodIds[i].classIdx].internal) {
                 MethodIdItem methodId = mMethodIds[i];
-                methodRefs[count++] =
-                    new MethodRef(classNameFromTypeIndex(methodId.classIdx),
-                                 argArrayFromProtoIndex(methodId.protoIdx),
-                                 returnTypeFromProtoIndex(methodId.protoIdx),
-                                 mStrings[methodId.nameIdx]);
+                MethodRef newMethodRef = new MethodRef(
+                        classNameFromTypeIndex(methodId.classIdx),
+                        argArrayFromProtoIndex(methodId.protoIdx),
+                        returnTypeFromProtoIndex(methodId.protoIdx),
+                        mStrings[methodId.nameIdx]);
+                sparseRefs[mMethodIds[i].classIdx].addMethod(newMethodRef);
             }
         }
-
-        assert count == methodRefs.length;
-
-        return methodRefs;
     }
 
 
diff --git a/tools/dexdeps/src/com/android/dexdeps/Output.java b/tools/dexdeps/src/com/android/dexdeps/Output.java
index 0039b33..7114951 100644
--- a/tools/dexdeps/src/com/android/dexdeps/Output.java
+++ b/tools/dexdeps/src/com/android/dexdeps/Output.java
@@ -20,6 +20,12 @@
  * Generate fancy output.
  */
 public class Output {
+    private static final String IN0 = "";
+    private static final String IN1 = "  ";
+    private static final String IN2 = "    ";
+    private static final String IN3 = "      ";
+    private static final String IN4 = "        ";
+
     public static void generate(DexData dexData, String format) {
         if (format.equals("brief")) {
             printBrief(dexData);
@@ -35,36 +41,56 @@
      * Prints the data in a simple human-readable format.
      */
     static void printBrief(DexData dexData) {
-        FieldRef[] externFieldRefs = dexData.getExternalFieldReferences();
-        MethodRef[] externMethodRefs = dexData.getExternalMethodReferences();
+        ClassRef[] externClassRefs = dexData.getExternalReferences();
 
-        printFieldRefs(externFieldRefs);
-        printMethodRefs(externMethodRefs);
+        printClassRefs(externClassRefs);
+        printFieldRefs(externClassRefs);
+        printMethodRefs(externClassRefs);
+    }
+
+    /**
+     * Prints the list of classes in a simple human-readable format.
+     */
+    static void printClassRefs(ClassRef[] classes) {
+        System.out.println("Classes:");
+        for (int i = 0; i < classes.length; i++) {
+            ClassRef ref = classes[i];
+
+            System.out.println(descriptorToDot(ref.getName()));
+        }
     }
 
     /**
      * Prints the list of fields in a simple human-readable format.
      */
-    static void printFieldRefs(FieldRef[] fields) {
+    static void printFieldRefs(ClassRef[] classes) {
         System.out.println("Fields:");
-        for (int i = 0; i < fields.length; i++) {
-            FieldRef ref = fields[i];
+        for (int i = 0; i < classes.length; i++) {
+            FieldRef[] fields = classes[i].getFieldArray();
 
-            System.out.println(descriptorToDot(ref.getDeclClassName()) + "." +
-                ref.getName() + " : " + ref.getTypeName());
+            for (int j = 0; j < fields.length; j++) {
+                FieldRef ref = fields[j];
+
+                System.out.println(descriptorToDot(ref.getDeclClassName()) +
+                    "." + ref.getName() + " : " + ref.getTypeName());
+            }
         }
     }
 
     /**
      * Prints the list of methods in a simple human-readable format.
      */
-    static void printMethodRefs(MethodRef[] methods) {
+    static void printMethodRefs(ClassRef[] classes) {
         System.out.println("Methods:");
-        for (int i = 0; i < methods.length; i++) {
-            MethodRef ref = methods[i];
+        for (int i = 0; i < classes.length; i++) {
+            MethodRef[] methods = classes[i].getMethodArray();
 
-            System.out.println(descriptorToDot(ref.getDeclClassName()) +
-                "." + ref.getName() + " : " + ref.getDescriptor());
+            for (int j = 0; j < methods.length; j++) {
+                MethodRef ref = methods[j];
+
+                System.out.println(descriptorToDot(ref.getDeclClassName()) +
+                    "." + ref.getName() + " : " + ref.getDescriptor());
+            }
         }
     }
 
@@ -75,79 +101,90 @@
      * We shouldn't need to XML-escape the field/method info.
      */
     static void printXml(DexData dexData) {
-        final String IN0 = "";
-        final String IN1 = "  ";
-        final String IN2 = "    ";
-        final String IN3 = "      ";
-        FieldRef[] externFieldRefs = dexData.getExternalFieldReferences();
-        MethodRef[] externMethodRefs = dexData.getExternalMethodReferences();
-        String prevClass = null;
+        ClassRef[] externClassRefs = dexData.getExternalReferences();
 
         System.out.println(IN0 + "<external>");
 
-        /* print fields */
-        for (int i = 0; i < externFieldRefs.length; i++) {
-            FieldRef fref = externFieldRefs[i];
-            String declClassName = fref.getDeclClassName();
+        /*
+         * Iterate through externClassRefs.  For each class, dump all of
+         * the matching fields and methods.
+         */
+        String prevPackage = null;
+        for (int i = 0; i < externClassRefs.length; i++) {
+            ClassRef cref = externClassRefs[i];
+            String declClassName = cref.getName();
+            String className = classNameOnly(declClassName);
+            String packageName = packageNameOnly(declClassName);
 
-            if (prevClass != null && !prevClass.equals(declClassName)) {
-                System.out.println(IN1 + "</class>");
-            }
-            if (!declClassName.equals(prevClass)) {
-                String className = classNameOnly(declClassName);
-                String packageName = packageNameOnly(declClassName);
-                System.out.println(IN1 + "<class package=\"" + packageName +
-                    "\" name=\"" + className + "\">");
-                prevClass = declClassName;
+            /*
+             * If we're in a different package, emit the appropriate tags.
+             */
+            if (!packageName.equals(prevPackage)) {
+                if (prevPackage != null) {
+                    System.out.println(IN1 + "</package>");
+                }
+
+                System.out.println(IN1 +
+                    "<package name=\"" + packageName + "\">");
+
+                prevPackage = packageName;
             }
 
-            System.out.println(IN2 + "<field name=\"" + fref.getName() +
-                "\" type=\"" + descriptorToDot(fref.getTypeName()) + "\"/>");
+            System.out.println(IN2 + "<class name=\"" + className + "\">");
+            printXmlFields(cref);
+            printXmlMethods(cref);
+            System.out.println(IN2 + "</class>");
         }
 
-        /* print methods */
-        for (int i = 0; i < externMethodRefs.length; i++) {
-            MethodRef mref = externMethodRefs[i];
+        if (prevPackage != null)
+            System.out.println(IN1 + "</package>");
+        System.out.println(IN0 + "</external>");
+    }
+
+    /**
+     * Prints the externally-visible fields in XML format.
+     */
+    private static void printXmlFields(ClassRef cref) {
+        FieldRef[] fields = cref.getFieldArray();
+        for (int i = 0; i < fields.length; i++) {
+            FieldRef fref = fields[i];
+
+            System.out.println(IN3 + "<field name=\"" + fref.getName() +
+                "\" type=\"" + descriptorToDot(fref.getTypeName()) + "\"/>");
+        }
+    }
+
+    /**
+     * Prints the externally-visible methods in XML format.
+     */
+    private static void printXmlMethods(ClassRef cref) {
+        MethodRef[] methods = cref.getMethodArray();
+        for (int i = 0; i < methods.length; i++) {
+            MethodRef mref = methods[i];
             String declClassName = mref.getDeclClassName();
             boolean constructor;
 
-            if (prevClass != null && !prevClass.equals(declClassName)) {
-                System.out.println(IN1 + "</class>");
-            }
-            if (!declClassName.equals(prevClass)) {
-                String className = classNameOnly(declClassName);
-                String packageName = packageNameOnly(declClassName);
-                System.out.println(IN1 + "<class package=\"" + packageName +
-                    "\" name=\"" + className + "\">");
-                prevClass = declClassName;
-            }
-
             constructor = mref.getName().equals("<init>");
             if (constructor) {
-                /* use class name instead of method name */
-                System.out.println(IN2 + "<constructor name=\"" +
-                    classNameOnly(declClassName) + "\" return=\"" +
-                    descriptorToDot(mref.getReturnTypeName()) + "\">");
+                // use class name instead of method name
+                System.out.println(IN3 + "<constructor name=\"" +
+                    classNameOnly(declClassName) + "\">");
             } else {
-                System.out.println(IN2 + "<method name=\"" + mref.getName() +
+                System.out.println(IN3 + "<method name=\"" + mref.getName() +
                     "\" return=\"" + descriptorToDot(mref.getReturnTypeName()) +
                     "\">");
             }
             String[] args = mref.getArgumentTypeNames();
             for (int j = 0; j < args.length; j++) {
-                System.out.println(IN3 + "<parameter type=\"" +
+                System.out.println(IN4 + "<parameter type=\"" +
                     descriptorToDot(args[j]) + "\"/>");
             }
             if (constructor) {
-                System.out.println(IN2 + "</constructor>");
+                System.out.println(IN3 + "</constructor>");
             } else {
-                System.out.println(IN2 + "</method>");
+                System.out.println(IN3 + "</method>");
             }
         }
-
-        if (prevClass != null)
-            System.out.println(IN1 + "</class>");
-        System.out.println(IN0 + "</external>");
     }