Home | History | Annotate | Download | only in proguard
      1 /*
      2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
      3  *             of Java bytecode.
      4  *
      5  * Copyright (c) 2002-2013 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;
     22 
     23 import proguard.classfile.ClassPool;
     24 import proguard.classfile.util.WarningPrinter;
     25 import proguard.classfile.visitor.*;
     26 import proguard.io.*;
     27 
     28 import java.io.IOException;
     29 
     30 /**
     31  * This class reads the input class files.
     32  *
     33  * @author Eric Lafortune
     34  */
     35 public class InputReader
     36 {
     37     private final Configuration configuration;
     38 
     39 
     40     /**
     41      * Creates a new InputReader to read input class files as specified by the
     42      * given configuration.
     43      */
     44     public InputReader(Configuration configuration)
     45     {
     46         this.configuration = configuration;
     47     }
     48 
     49 
     50     /**
     51      * Fills the given program class pool and library class pool by reading
     52      * class files, based on the current configuration.
     53      */
     54     public void execute(ClassPool programClassPool,
     55                         ClassPool libraryClassPool) throws IOException
     56     {
     57         // Check if we have at least some input classes.
     58         if (configuration.programJars == null)
     59         {
     60             throw new IOException("The input is empty. You have to specify one or more '-injars' options");
     61         }
     62 
     63         // Perform some sanity checks on the class paths.
     64         checkInputOutput(configuration.libraryJars,
     65                          configuration.programJars);
     66         checkInputOutput(configuration.programJars,
     67                          configuration.programJars);
     68 
     69         WarningPrinter warningPrinter = new WarningPrinter(System.err, configuration.warn);
     70         WarningPrinter notePrinter    = new WarningPrinter(System.out, configuration.note);
     71 
     72         DuplicateClassPrinter duplicateClassPrinter = new DuplicateClassPrinter(notePrinter);
     73 
     74         // Read the program class files.
     75         // Prepare a data entry reader to filter all classes,
     76         // which are then decoded to classes by a class reader,
     77         // which are then put in the class pool by a class pool filler.
     78         readInput("Reading program ",
     79                   configuration.programJars,
     80                   new ClassFilter(
     81                   new ClassReader(false,
     82                                   configuration.skipNonPublicLibraryClasses,
     83                                   configuration.skipNonPublicLibraryClassMembers,
     84                                   warningPrinter,
     85                   new ClassPresenceFilter(programClassPool, duplicateClassPrinter,
     86                   new ClassPoolFiller(programClassPool)))));
     87 
     88         // Check if we have at least some input classes.
     89         if (programClassPool.size() == 0)
     90         {
     91             throw new IOException("The input doesn't contain any classes. Did you specify the proper '-injars' options?");
     92         }
     93 
     94         // Read the library class files, if any.
     95         if (configuration.libraryJars != null)
     96         {
     97             // Prepare a data entry reader to filter all classes,
     98             // which are then decoded to classes by a class reader,
     99             // which are then put in the class pool by a class pool filler.
    100             readInput("Reading library ",
    101                       configuration.libraryJars,
    102                       new ClassFilter(
    103                       new ClassReader(true,
    104                                       configuration.skipNonPublicLibraryClasses,
    105                                       configuration.skipNonPublicLibraryClassMembers,
    106                                       warningPrinter,
    107                       new ClassPresenceFilter(programClassPool, duplicateClassPrinter,
    108                       new ClassPresenceFilter(libraryClassPool, duplicateClassPrinter,
    109                       new ClassPoolFiller(libraryClassPool))))));
    110         }
    111 
    112         // Print out a summary of the notes, if necessary.
    113         int noteCount = notePrinter.getWarningCount();
    114         if (noteCount > 0)
    115         {
    116             System.err.println("Note: there were " + noteCount +
    117                                " duplicate class definitions.");
    118             System.out.println("      (http://proguard.sourceforge.net/manual/troubleshooting.html#duplicateclass)");
    119         }
    120 
    121         // Print out a summary of the warnings, if necessary.
    122         int warningCount = warningPrinter.getWarningCount();
    123         if (warningCount > 0)
    124         {
    125             System.err.println("Warning: there were " + warningCount +
    126                                " classes in incorrectly named files.");
    127             System.err.println("         You should make sure all file names correspond to their class names.");
    128             System.err.println("         The directory hierarchies must correspond to the package hierarchies.");
    129             System.err.println("         (http://proguard.sourceforge.net/manual/troubleshooting.html#unexpectedclass)");
    130 
    131             if (!configuration.ignoreWarnings)
    132             {
    133                 System.err.println("         If you don't mind the mentioned classes not being written out,");
    134                 System.err.println("         you could try your luck using the '-ignorewarnings' option.");
    135                 throw new IOException("Please correct the above warnings first.");
    136             }
    137         }
    138     }
    139 
    140 
    141     /**
    142      * Performs some sanity checks on the class paths.
    143      */
    144     private void checkInputOutput(ClassPath inputClassPath,
    145                                   ClassPath outputClassPath)
    146     throws IOException
    147     {
    148         if (inputClassPath == null ||
    149             outputClassPath == null)
    150         {
    151             return;
    152         }
    153 
    154         for (int index1 = 0; index1 < inputClassPath.size(); index1++)
    155         {
    156             ClassPathEntry entry1 = inputClassPath.get(index1);
    157             if (!entry1.isOutput())
    158             {
    159                 for (int index2 = 0; index2 < outputClassPath.size(); index2++)
    160                 {
    161                     ClassPathEntry entry2 = outputClassPath.get(index2);
    162                     if (entry2.isOutput() &&
    163                         entry2.getName().equals(entry1.getName()))
    164                     {
    165                         throw new IOException("Input jars and output jars must be different ["+entry1.getName()+"]");
    166                     }
    167                 }
    168             }
    169         }
    170     }
    171 
    172 
    173     /**
    174      * Reads all input entries from the given class path.
    175      */
    176     private void readInput(String          messagePrefix,
    177                            ClassPath       classPath,
    178                            DataEntryReader reader) throws IOException
    179     {
    180         readInput(messagePrefix,
    181                   classPath,
    182                   0,
    183                   classPath.size(),
    184                   reader);
    185     }
    186 
    187 
    188     /**
    189      * Reads all input entries from the given section of the given class path.
    190      */
    191     public void readInput(String          messagePrefix,
    192                           ClassPath       classPath,
    193                           int             fromIndex,
    194                           int             toIndex,
    195                           DataEntryReader reader) throws IOException
    196     {
    197         for (int index = fromIndex; index < toIndex; index++)
    198         {
    199             ClassPathEntry entry = classPath.get(index);
    200             if (!entry.isOutput())
    201             {
    202                 readInput(messagePrefix, entry, reader);
    203             }
    204         }
    205     }
    206 
    207 
    208     /**
    209      * Reads the given input class path entry.
    210      */
    211     private void readInput(String          messagePrefix,
    212                            ClassPathEntry  classPathEntry,
    213                            DataEntryReader dataEntryReader) throws IOException
    214     {
    215         try
    216         {
    217             // Create a reader that can unwrap jars, wars, ears, and zips.
    218             DataEntryReader reader =
    219                 DataEntryReaderFactory.createDataEntryReader(messagePrefix,
    220                                                              classPathEntry,
    221                                                              dataEntryReader);
    222 
    223             // Create the data entry pump.
    224             DirectoryPump directoryPump =
    225                 new DirectoryPump(classPathEntry.getFile());
    226 
    227             // Pump the data entries into the reader.
    228             directoryPump.pumpDataEntries(reader);
    229         }
    230         catch (IOException ex)
    231         {
    232             throw (IOException)new IOException("Can't read [" + classPathEntry + "] (" + ex.getMessage() + ")").initCause(ex);
    233         }
    234     }
    235 }
    236