1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu)
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21package proguard.shrink;
22
23import proguard.classfile.*;
24import proguard.classfile.util.ClassUtil;
25import proguard.classfile.visitor.*;
26
27import java.io.PrintStream;
28
29
30/**
31 * This ClassVisitor     and MemberVisitor prints out the reasons why
32 * classes and class members have been marked as being used.
33 *
34 * @see UsageMarker
35 *
36 * @author Eric Lafortune
37 */
38public class ShortestUsagePrinter
39implements   ClassVisitor,
40             MemberVisitor
41{
42    private final ShortestUsageMarker shortestUsageMarker;
43    private final boolean             verbose;
44    private final PrintStream         ps;
45
46
47    /**
48     * Creates a new UsagePrinter that prints verbosely to <code>System.out</code>.
49     * @param shortestUsageMarker the usage marker that was used to mark the
50     *                            classes and class members.
51     */
52    public ShortestUsagePrinter(ShortestUsageMarker shortestUsageMarker)
53    {
54        this(shortestUsageMarker, true);
55    }
56
57
58    /**
59     * Creates a new UsagePrinter that prints to the given stream.
60     * @param shortestUsageMarker the usage marker that was used to mark the
61     *                            classes and class members.
62     * @param verbose             specifies whether the output should be verbose.
63     */
64    public ShortestUsagePrinter(ShortestUsageMarker shortestUsageMarker,
65                                boolean             verbose)
66    {
67        this(shortestUsageMarker, verbose, System.out);
68    }
69
70    /**
71     * Creates a new UsagePrinter that prints to the given stream.
72     * @param shortestUsageMarker the usage marker that was used to mark the
73     *                            classes and class members.
74     * @param verbose             specifies whether the output should be verbose.
75     * @param printStream         the stream to which to print.
76     */
77    public ShortestUsagePrinter(ShortestUsageMarker shortestUsageMarker,
78                                boolean             verbose,
79                                PrintStream         printStream)
80    {
81        this.shortestUsageMarker = shortestUsageMarker;
82        this.verbose             = verbose;
83        this.ps                  = printStream;
84    }
85
86
87    // Implementations for ClassVisitor.
88
89    public void visitProgramClass(ProgramClass programClass)
90    {
91        // Print the name of this class.
92        ps.println(ClassUtil.externalClassName(programClass.getName()));
93
94        // Print the reason for keeping this class.
95        printReason(programClass);
96    }
97
98
99    public void visitLibraryClass(LibraryClass libraryClass)
100    {
101        // Print the name of this class.
102        ps.println(ClassUtil.externalClassName(libraryClass.getName()));
103
104        // Print the reason for keeping this class.
105        ps.println("  is a library class.\n");
106    }
107
108
109    // Implementations for MemberVisitor.
110
111    public void visitProgramField(ProgramClass programClass, ProgramField programField)
112    {
113        // Print the name of this field.
114        String name = programField.getName(programClass);
115        String type = programField.getDescriptor(programClass);
116
117        ps.println(ClassUtil.externalClassName(programClass.getName()) +
118                   (verbose ?
119                        ": " + ClassUtil.externalFullFieldDescription(0, name, type):
120                        "."  + name) +
121                   lineNumberRange(programClass, programField));
122
123        // Print the reason for keeping this method.
124        printReason(programField);
125    }
126
127
128    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
129    {
130        // Print the name of this method.
131        String name = programMethod.getName(programClass);
132        String type = programMethod.getDescriptor(programClass);
133
134        ps.println(ClassUtil.externalClassName(programClass.getName()) +
135                   (verbose ?
136                        ": " + ClassUtil.externalFullMethodDescription(programClass.getName(), 0, name, type):
137                        "."  + name) +
138                   lineNumberRange(programClass, programMethod));
139
140        // Print the reason for keeping this method.
141        printReason(programMethod);
142    }
143
144
145    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
146    {
147        // Print the name of this field.
148        String name = libraryField.getName(libraryClass);
149        String type = libraryField.getDescriptor(libraryClass);
150
151        ps.println(ClassUtil.externalClassName(libraryClass.getName()) +
152                   (verbose ?
153                        ": " + ClassUtil.externalFullFieldDescription(0, name, type):
154                        "."  + name));
155
156        // Print the reason for keeping this field.
157        ps.println("  is a library field.\n");
158    }
159
160
161    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
162    {
163        // Print the name of this method.
164        String name = libraryMethod.getName(libraryClass);
165        String type = libraryMethod.getDescriptor(libraryClass);
166
167        ps.println(ClassUtil.externalClassName(libraryClass.getName()) +
168                   (verbose ?
169                        ": " + ClassUtil.externalFullMethodDescription(libraryClass.getName(), 0, name, type):
170                        "."  + name));
171
172        // Print the reason for keeping this method.
173        ps.println("  is a library method.\n");
174    }
175
176
177    // Small utility methods.
178
179    private void printReason(VisitorAccepter visitorAccepter)
180    {
181        if (shortestUsageMarker.isUsed(visitorAccepter))
182        {
183            ShortestUsageMark shortestUsageMark = shortestUsageMarker.getShortestUsageMark(visitorAccepter);
184
185            // Print the reason for keeping this class.
186            ps.print("  " + shortestUsageMark.getReason());
187
188            // Print the class or method that is responsible, with its reasons.
189            shortestUsageMark.acceptClassVisitor(this);
190            shortestUsageMark.acceptMemberVisitor(this);
191        }
192        else
193        {
194            ps.println("  is not being kept.\n");
195        }
196    }
197
198
199    /**
200     * Returns the line number range of the given class member, followed by a
201     * colon, or just an empty String if no range is available.
202     */
203    private static String lineNumberRange(ProgramClass programClass, ProgramMember programMember)
204    {
205        String range = programMember.getLineNumberRange(programClass);
206        return range != null ?
207            (" (" + range + ")") :
208            "";
209    }
210}
211