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 17package com.android.dexdeps; 18 19/** 20 * Generate fancy output. 21 */ 22public 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