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