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