Home | History | Annotate | Download | only in shrink
      1 /*
      2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
      3  *             of Java bytecode.
      4  *
      5  * Copyright (c) 2002-2009 Eric Lafortune (eric (at) 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  */
     21 package proguard.shrink;
     22 
     23 import proguard.classfile.*;
     24 import proguard.classfile.util.ClassUtil;
     25 import proguard.classfile.visitor.*;
     26 
     27 import 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  */
     38 public class ShortestUsagePrinter
     39 implements   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