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 /**
     20  * Generate fancy output.
     21  */
     22 public class Output {
     23     private static final String IN0 = "";
     24     private static final String IN1 = "  ";
     25     private static final String IN2 = "    ";
     26     private static final String IN3 = "      ";
     27     private static final String IN4 = "        ";
     28 
     29     public static void generate(DexData dexData, String format) {
     30         if (format.equals("brief")) {
     31             printBrief(dexData);
     32         } else if (format.equals("xml")) {
     33             printXml(dexData);
     34         } else {
     35             /* should've been trapped in arg handler */
     36             throw new RuntimeException("unknown output format");
     37         }
     38     }
     39 
     40     /**
     41      * Prints the data in a simple human-readable format.
     42      */
     43     static void printBrief(DexData dexData) {
     44         ClassRef[] externClassRefs = dexData.getExternalReferences();
     45 
     46         printClassRefs(externClassRefs);
     47         printFieldRefs(externClassRefs);
     48         printMethodRefs(externClassRefs);
     49     }
     50 
     51     /**
     52      * Prints the list of classes in a simple human-readable format.
     53      */
     54     static void printClassRefs(ClassRef[] classes) {
     55         System.out.println("Classes:");
     56         for (int i = 0; i < classes.length; i++) {
     57             ClassRef ref = classes[i];
     58 
     59             System.out.println(descriptorToDot(ref.getName()));
     60         }
     61     }
     62 
     63     /**
     64      * Prints the list of fields in a simple human-readable format.
     65      */
     66     static void printFieldRefs(ClassRef[] classes) {
     67         System.out.println("\nFields:");
     68         for (int i = 0; i < classes.length; i++) {
     69             FieldRef[] fields = classes[i].getFieldArray();
     70 
     71             for (int j = 0; j < fields.length; j++) {
     72                 FieldRef ref = fields[j];
     73 
     74                 System.out.println(descriptorToDot(ref.getDeclClassName()) +
     75                     "." + ref.getName() + " : " + ref.getTypeName());
     76             }
     77         }
     78     }
     79 
     80     /**
     81      * Prints the list of methods in a simple human-readable format.
     82      */
     83     static void printMethodRefs(ClassRef[] classes) {
     84         System.out.println("\nMethods:");
     85         for (int i = 0; i < classes.length; i++) {
     86             MethodRef[] methods = classes[i].getMethodArray();
     87 
     88             for (int j = 0; j < methods.length; j++) {
     89                 MethodRef ref = methods[j];
     90 
     91                 System.out.println(descriptorToDot(ref.getDeclClassName()) +
     92                     "." + ref.getName() + " : " + ref.getDescriptor());
     93             }
     94         }
     95     }
     96 
     97 
     98     /**
     99      * Prints the output in XML format.
    100      *
    101      * We shouldn't need to XML-escape the field/method info.
    102      */
    103     static void printXml(DexData dexData) {
    104         ClassRef[] externClassRefs = dexData.getExternalReferences();
    105 
    106         System.out.println(IN0 + "<external>");
    107 
    108         /*
    109          * Iterate through externClassRefs.  For each class, dump all of
    110          * the matching fields and methods.
    111          */
    112         String prevPackage = null;
    113         for (int i = 0; i < externClassRefs.length; i++) {
    114             ClassRef cref = externClassRefs[i];
    115             String declClassName = cref.getName();
    116             String className = classNameOnly(declClassName);
    117             String packageName = packageNameOnly(declClassName);
    118 
    119             /*
    120              * If we're in a different package, emit the appropriate tags.
    121              */
    122             if (!packageName.equals(prevPackage)) {
    123                 if (prevPackage != null) {
    124                     System.out.println(IN1 + "</package>");
    125                 }
    126 
    127                 System.out.println(IN1 +
    128                     "<package name=\"" + packageName + "\">");
    129 
    130                 prevPackage = packageName;
    131             }
    132 
    133             System.out.println(IN2 + "<class name=\"" + className + "\">");
    134             printXmlFields(cref);
    135             printXmlMethods(cref);
    136             System.out.println(IN2 + "</class>");
    137         }
    138 
    139         if (prevPackage != null)
    140             System.out.println(IN1 + "</package>");
    141         System.out.println(IN0 + "</external>");
    142     }
    143 
    144     /**
    145      * Prints the externally-visible fields in XML format.
    146      */
    147     private static void printXmlFields(ClassRef cref) {
    148         FieldRef[] fields = cref.getFieldArray();
    149         for (int i = 0; i < fields.length; i++) {
    150             FieldRef fref = fields[i];
    151 
    152             System.out.println(IN3 + "<field name=\"" + fref.getName() +
    153                 "\" type=\"" + descriptorToDot(fref.getTypeName()) + "\"/>");
    154         }
    155     }
    156 
    157     /**
    158      * Prints the externally-visible methods in XML format.
    159      */
    160     private static void printXmlMethods(ClassRef cref) {
    161         MethodRef[] methods = cref.getMethodArray();
    162         for (int i = 0; i < methods.length; i++) {
    163             MethodRef mref = methods[i];
    164             String declClassName = mref.getDeclClassName();
    165             boolean constructor;
    166 
    167             constructor = mref.getName().equals("<init>");
    168             if (constructor) {
    169                 // use class name instead of method name
    170                 System.out.println(IN3 + "<constructor name=\"" +
    171                     classNameOnly(declClassName) + "\">");
    172             } else {
    173                 System.out.println(IN3 + "<method name=\"" + mref.getName() +
    174                     "\" return=\"" + descriptorToDot(mref.getReturnTypeName()) +
    175                     "\">");
    176             }
    177             String[] args = mref.getArgumentTypeNames();
    178             for (int j = 0; j < args.length; j++) {
    179                 System.out.println(IN4 + "<parameter type=\"" +
    180                     descriptorToDot(args[j]) + "\"/>");
    181             }
    182             if (constructor) {
    183                 System.out.println(IN3 + "</constructor>");
    184             } else {
    185                 System.out.println(IN3 + "</method>");
    186             }
    187         }
    188     }
    189 
    190 
    191     /*
    192      * =======================================================================
    193      *      Utility functions
    194      * =======================================================================
    195      */
    196 
    197     /**
    198      * Converts a single-character primitive type into its human-readable
    199      * equivalent.
    200      */
    201     static String primitiveTypeLabel(char typeChar) {
    202         /* primitive type; substitute human-readable name in */
    203         switch (typeChar) {
    204             case 'B':   return "byte";
    205             case 'C':   return "char";
    206             case 'D':   return "double";
    207             case 'F':   return "float";
    208             case 'I':   return "int";
    209             case 'J':   return "long";
    210             case 'S':   return "short";
    211             case 'V':   return "void";
    212             case 'Z':   return "boolean";
    213             default:
    214                 /* huh? */
    215                 System.err.println("Unexpected class char " + typeChar);
    216                 assert false;
    217                 return "UNKNOWN";
    218         }
    219     }
    220 
    221     /**
    222      * Converts a type descriptor to human-readable "dotted" form.  For
    223      * example, "Ljava/lang/String;" becomes "java.lang.String", and
    224      * "[I" becomes "int[].
    225      */
    226     static String descriptorToDot(String descr) {
    227         int targetLen = descr.length();
    228         int offset = 0;
    229         int arrayDepth = 0;
    230 
    231         /* strip leading [s; will be added to end */
    232         while (targetLen > 1 && descr.charAt(offset) == '[') {
    233             offset++;
    234             targetLen--;
    235         }
    236         arrayDepth = offset;
    237 
    238         if (targetLen == 1) {
    239             descr = primitiveTypeLabel(descr.charAt(offset));
    240             offset = 0;
    241             targetLen = descr.length();
    242         } else {
    243             /* account for leading 'L' and trailing ';' */
    244             if (targetLen >= 2 && descr.charAt(offset) == 'L' &&
    245                 descr.charAt(offset+targetLen-1) == ';')
    246             {
    247                 targetLen -= 2;     /* two fewer chars to copy */
    248                 offset++;           /* skip the 'L' */
    249             }
    250         }
    251 
    252         char[] buf = new char[targetLen + arrayDepth * 2];
    253 
    254         /* copy class name over */
    255         int i;
    256         for (i = 0; i < targetLen; i++) {
    257             char ch = descr.charAt(offset + i);
    258             buf[i] = (ch == '/') ? '.' : ch;
    259         }
    260 
    261         /* add the appopriate number of brackets for arrays */
    262         while (arrayDepth-- > 0) {
    263             buf[i++] = '[';
    264             buf[i++] = ']';
    265         }
    266         assert i == buf.length;
    267 
    268         return new String(buf);
    269     }
    270 
    271     /**
    272      * Extracts the class name from a type descriptor.
    273      */
    274     static String classNameOnly(String typeName) {
    275         String dotted = descriptorToDot(typeName);
    276 
    277         int start = dotted.lastIndexOf(".");
    278         if (start < 0) {
    279             return dotted;
    280         } else {
    281             return dotted.substring(start+1);
    282         }
    283     }
    284 
    285     /**
    286      * Extracts the package name from a type descriptor, and returns it in
    287      * dotted form.
    288      */
    289     static String packageNameOnly(String typeName) {
    290         String dotted = descriptorToDot(typeName);
    291 
    292         int end = dotted.lastIndexOf(".");
    293         if (end < 0) {
    294             /* lives in default package */
    295             return "";
    296         } else {
    297             return dotted.substring(0, end);
    298         }
    299     }
    300 }
    301