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