Home | History | Annotate | Download | only in dexdeps
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.dexdeps;
     18 
     19 import java.io.PrintStream;
     20 
     21 /**
     22  * Generate fancy output.
     23  */
     24 public class Output {
     25     private static final String IN0 = "";
     26     private static final String IN1 = "  ";
     27     private static final String IN2 = "    ";
     28     private static final String IN3 = "      ";
     29     private static final String IN4 = "        ";
     30 
     31     private static final PrintStream out = System.out;
     32 
     33     private static void generateHeader0(String fileName, String format) {
     34         if (format.equals("brief")) {
     35             if (fileName != null) {
     36                 out.println("File: " + fileName);
     37             }
     38         } else if (format.equals("xml")) {
     39             if (fileName != null) {
     40                 out.println(IN0 + "<external file=\"" + fileName + "\">");
     41             } else {
     42                 out.println(IN0 + "<external>");
     43             }
     44         } else {
     45             /* should've been trapped in arg handler */
     46             throw new RuntimeException("unknown output format");
     47         }
     48     }
     49 
     50     public static void generateFirstHeader(String fileName, String format) {
     51         generateHeader0(fileName, format);
     52     }
     53 
     54     public static void generateHeader(String fileName, String format) {
     55         out.println();
     56         generateHeader0(fileName, format);
     57     }
     58 
     59     public static void generateFooter(String format) {
     60         if (format.equals("brief")) {
     61             // Nothing to do.
     62         } else if (format.equals("xml")) {
     63             out.println("</external>");
     64         } else {
     65             /* should've been trapped in arg handler */
     66             throw new RuntimeException("unknown output format");
     67         }
     68     }
     69 
     70     public static void generate(DexData dexData, String format,
     71             boolean justClasses) {
     72         if (format.equals("brief")) {
     73             printBrief(dexData, justClasses);
     74         } else if (format.equals("xml")) {
     75             printXml(dexData, justClasses);
     76         } else {
     77             /* should've been trapped in arg handler */
     78             throw new RuntimeException("unknown output format");
     79         }
     80     }
     81 
     82     /**
     83      * Prints the data in a simple human-readable format.
     84      */
     85     static void printBrief(DexData dexData, boolean justClasses) {
     86         ClassRef[] externClassRefs = dexData.getExternalReferences();
     87 
     88         printClassRefs(externClassRefs, justClasses);
     89 
     90         if (!justClasses) {
     91             printFieldRefs(externClassRefs);
     92             printMethodRefs(externClassRefs);
     93         }
     94     }
     95 
     96     /**
     97      * Prints the list of classes in a simple human-readable format.
     98      */
     99     static void printClassRefs(ClassRef[] classes, boolean justClasses) {
    100         if (!justClasses) {
    101             out.println("Classes:");
    102         }
    103 
    104         for (int i = 0; i < classes.length; i++) {
    105             ClassRef ref = classes[i];
    106 
    107             out.println(descriptorToDot(ref.getName()));
    108         }
    109     }
    110 
    111     /**
    112      * Prints the list of fields in a simple human-readable format.
    113      */
    114     static void printFieldRefs(ClassRef[] classes) {
    115         out.println("\nFields:");
    116         for (int i = 0; i < classes.length; i++) {
    117             FieldRef[] fields = classes[i].getFieldArray();
    118 
    119             for (int j = 0; j < fields.length; j++) {
    120                 FieldRef ref = fields[j];
    121 
    122                 out.println(descriptorToDot(ref.getDeclClassName()) +
    123                     "." + ref.getName() + " : " + ref.getTypeName());
    124             }
    125         }
    126     }
    127 
    128     /**
    129      * Prints the list of methods in a simple human-readable format.
    130      */
    131     static void printMethodRefs(ClassRef[] classes) {
    132         out.println("\nMethods:");
    133         for (int i = 0; i < classes.length; i++) {
    134             MethodRef[] methods = classes[i].getMethodArray();
    135 
    136             for (int j = 0; j < methods.length; j++) {
    137                 MethodRef ref = methods[j];
    138 
    139                 out.println(descriptorToDot(ref.getDeclClassName()) +
    140                     "." + ref.getName() + " : " + ref.getDescriptor());
    141             }
    142         }
    143     }
    144 
    145     /**
    146      * Prints the output in XML format.
    147      *
    148      * We shouldn't need to XML-escape the field/method info.
    149      */
    150     static void printXml(DexData dexData, boolean justClasses) {
    151         ClassRef[] externClassRefs = dexData.getExternalReferences();
    152 
    153         /*
    154          * Iterate through externClassRefs.  For each class, dump all of
    155          * the matching fields and methods.
    156          */
    157         String prevPackage = null;
    158         for (int i = 0; i < externClassRefs.length; i++) {
    159             ClassRef cref = externClassRefs[i];
    160             String declClassName = cref.getName();
    161             String className = classNameOnly(declClassName);
    162             String packageName = packageNameOnly(declClassName);
    163 
    164             /*
    165              * If we're in a different package, emit the appropriate tags.
    166              */
    167             if (!packageName.equals(prevPackage)) {
    168                 if (prevPackage != null) {
    169                     out.println(IN1 + "</package>");
    170                 }
    171 
    172                 out.println(IN1 +
    173                     "<package name=\"" + packageName + "\">");
    174 
    175                 prevPackage = packageName;
    176             }
    177 
    178             out.println(IN2 + "<class name=\"" + className + "\">");
    179             if (!justClasses) {
    180                 printXmlFields(cref);
    181                 printXmlMethods(cref);
    182             }
    183             out.println(IN2 + "</class>");
    184         }
    185 
    186         if (prevPackage != null)
    187             out.println(IN1 + "</package>");
    188     }
    189 
    190     /**
    191      * Prints the externally-visible fields in XML format.
    192      */
    193     private static void printXmlFields(ClassRef cref) {
    194         FieldRef[] fields = cref.getFieldArray();
    195         for (int i = 0; i < fields.length; i++) {
    196             FieldRef fref = fields[i];
    197 
    198             out.println(IN3 + "<field name=\"" + fref.getName() +
    199                 "\" type=\"" + descriptorToDot(fref.getTypeName()) + "\"/>");
    200         }
    201     }
    202 
    203     /**
    204      * Prints the externally-visible methods in XML format.
    205      */
    206     private static void printXmlMethods(ClassRef cref) {
    207         MethodRef[] methods = cref.getMethodArray();
    208         for (int i = 0; i < methods.length; i++) {
    209             MethodRef mref = methods[i];
    210             String declClassName = mref.getDeclClassName();
    211             boolean constructor;
    212 
    213             constructor = mref.getName().equals("<init>");
    214             if (constructor) {
    215                 // use class name instead of method name
    216                 out.println(IN3 + "<constructor name=\"" +
    217                     classNameOnly(declClassName) + "\">");
    218             } else {
    219                 out.println(IN3 + "<method name=\"" + mref.getName() +
    220                     "\" return=\"" + descriptorToDot(mref.getReturnTypeName()) +
    221                     "\">");
    222             }
    223             String[] args = mref.getArgumentTypeNames();
    224             for (int j = 0; j < args.length; j++) {
    225                 out.println(IN4 + "<parameter type=\"" +
    226                     descriptorToDot(args[j]) + "\"/>");
    227             }
    228             if (constructor) {
    229                 out.println(IN3 + "</constructor>");
    230             } else {
    231                 out.println(IN3 + "</method>");
    232             }
    233         }
    234     }
    235 
    236 
    237     /*
    238      * =======================================================================
    239      *      Utility functions
    240      * =======================================================================
    241      */
    242 
    243     /**
    244      * Converts a single-character primitive type into its human-readable
    245      * equivalent.
    246      */
    247     static String primitiveTypeLabel(char typeChar) {
    248         /* primitive type; substitute human-readable name in */
    249         switch (typeChar) {
    250             case 'B':   return "byte";
    251             case 'C':   return "char";
    252             case 'D':   return "double";
    253             case 'F':   return "float";
    254             case 'I':   return "int";
    255             case 'J':   return "long";
    256             case 'S':   return "short";
    257             case 'V':   return "void";
    258             case 'Z':   return "boolean";
    259             default:
    260                 /* huh? */
    261                 System.err.println("Unexpected class char " + typeChar);
    262                 assert false;
    263                 return "UNKNOWN";
    264         }
    265     }
    266 
    267     /**
    268      * Converts a type descriptor to human-readable "dotted" form.  For
    269      * example, "Ljava/lang/String;" becomes "java.lang.String", and
    270      * "[I" becomes "int[].
    271      */
    272     static String descriptorToDot(String descr) {
    273         int targetLen = descr.length();
    274         int offset = 0;
    275         int arrayDepth = 0;
    276 
    277         /* strip leading [s; will be added to end */
    278         while (targetLen > 1 && descr.charAt(offset) == '[') {
    279             offset++;
    280             targetLen--;
    281         }
    282         arrayDepth = offset;
    283 
    284         if (targetLen == 1) {
    285             descr = primitiveTypeLabel(descr.charAt(offset));
    286             offset = 0;
    287             targetLen = descr.length();
    288         } else {
    289             /* account for leading 'L' and trailing ';' */
    290             if (targetLen >= 2 && descr.charAt(offset) == 'L' &&
    291                 descr.charAt(offset+targetLen-1) == ';')
    292             {
    293                 targetLen -= 2;     /* two fewer chars to copy */
    294                 offset++;           /* skip the 'L' */
    295             }
    296         }
    297 
    298         char[] buf = new char[targetLen + arrayDepth * 2];
    299 
    300         /* copy class name over */
    301         int i;
    302         for (i = 0; i < targetLen; i++) {
    303             char ch = descr.charAt(offset + i);
    304             buf[i] = (ch == '/') ? '.' : ch;
    305         }
    306 
    307         /* add the appopriate number of brackets for arrays */
    308         while (arrayDepth-- > 0) {
    309             buf[i++] = '[';
    310             buf[i++] = ']';
    311         }
    312         assert i == buf.length;
    313 
    314         return new String(buf);
    315     }
    316 
    317     /**
    318      * Extracts the class name from a type descriptor.
    319      */
    320     static String classNameOnly(String typeName) {
    321         String dotted = descriptorToDot(typeName);
    322 
    323         int start = dotted.lastIndexOf(".");
    324         if (start < 0) {
    325             return dotted;
    326         } else {
    327             return dotted.substring(start+1);
    328         }
    329     }
    330 
    331     /**
    332      * Extracts the package name from a type descriptor, and returns it in
    333      * dotted form.
    334      */
    335     static String packageNameOnly(String typeName) {
    336         String dotted = descriptorToDot(typeName);
    337 
    338         int end = dotted.lastIndexOf(".");
    339         if (end < 0) {
    340             /* lives in default package */
    341             return "";
    342         } else {
    343             return dotted.substring(0, end);
    344         }
    345     }
    346 }
    347