1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2014 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;
22
23import proguard.classfile.ClassPool;
24import proguard.classfile.util.WarningPrinter;
25import proguard.classfile.visitor.*;
26import proguard.io.*;
27
28import java.io.IOException;
29
30/**
31 * This class reads the input class files.
32 *
33 * @author Eric Lafortune
34 */
35public 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        WarningPrinter warningPrinter = new WarningPrinter(System.err, configuration.warn);
58        WarningPrinter notePrinter    = new WarningPrinter(System.out, configuration.note);
59
60        DuplicateClassPrinter duplicateClassPrinter = new DuplicateClassPrinter(notePrinter);
61
62        // Read the program class files.
63        // Prepare a data entry reader to filter all classes,
64        // which are then decoded to classes by a class reader,
65        // which are then put in the class pool by a class pool filler.
66        readInput("Reading program ",
67                  configuration.programJars,
68                  new ClassFilter(
69                  new ClassReader(false,
70                                  configuration.skipNonPublicLibraryClasses,
71                                  configuration.skipNonPublicLibraryClassMembers,
72                                  warningPrinter,
73                  new ClassPresenceFilter(programClassPool, duplicateClassPrinter,
74                  new ClassPoolFiller(programClassPool)))));
75
76        // Check if we have at least some input classes.
77        if (programClassPool.size() == 0)
78        {
79            throw new IOException("The input doesn't contain any classes. Did you specify the proper '-injars' options?");
80        }
81
82        // Read the library class files, if any.
83        if (configuration.libraryJars != null)
84        {
85            // Prepare a data entry reader to filter all classes,
86            // which are then decoded to classes by a class reader,
87            // which are then put in the class pool by a class pool filler.
88            readInput("Reading library ",
89                      configuration.libraryJars,
90                      new ClassFilter(
91                      new ClassReader(true,
92                                      configuration.skipNonPublicLibraryClasses,
93                                      configuration.skipNonPublicLibraryClassMembers,
94                                      warningPrinter,
95                      new ClassPresenceFilter(programClassPool, duplicateClassPrinter,
96                      new ClassPresenceFilter(libraryClassPool, duplicateClassPrinter,
97                      new ClassPoolFiller(libraryClassPool))))));
98        }
99
100        // Print out a summary of the notes, if necessary.
101        int noteCount = notePrinter.getWarningCount();
102        if (noteCount > 0)
103        {
104            System.err.println("Note: there were " + noteCount +
105                               " duplicate class definitions.");
106            System.err.println("      (http://proguard.sourceforge.net/manual/troubleshooting.html#duplicateclass)");
107        }
108
109        // Print out a summary of the warnings, if necessary.
110        int warningCount = warningPrinter.getWarningCount();
111        if (warningCount > 0)
112        {
113            System.err.println("Warning: there were " + warningCount +
114                               " classes in incorrectly named files.");
115            System.err.println("         You should make sure all file names correspond to their class names.");
116            System.err.println("         The directory hierarchies must correspond to the package hierarchies.");
117            System.err.println("         (http://proguard.sourceforge.net/manual/troubleshooting.html#unexpectedclass)");
118
119            if (!configuration.ignoreWarnings)
120            {
121                System.err.println("         If you don't mind the mentioned classes not being written out,");
122                System.err.println("         you could try your luck using the '-ignorewarnings' option.");
123                throw new IOException("Please correct the above warnings first.");
124            }
125        }
126    }
127
128
129    /**
130     * Reads all input entries from the given class path.
131     */
132    private void readInput(String          messagePrefix,
133                           ClassPath       classPath,
134                           DataEntryReader reader) throws IOException
135    {
136        readInput(messagePrefix,
137                  classPath,
138                  0,
139                  classPath.size(),
140                  reader);
141    }
142
143
144    /**
145     * Reads all input entries from the given section of the given class path.
146     */
147    public void readInput(String          messagePrefix,
148                          ClassPath       classPath,
149                          int             fromIndex,
150                          int             toIndex,
151                          DataEntryReader reader) throws IOException
152    {
153        for (int index = fromIndex; index < toIndex; index++)
154        {
155            ClassPathEntry entry = classPath.get(index);
156            if (!entry.isOutput())
157            {
158                readInput(messagePrefix, entry, reader);
159            }
160        }
161    }
162
163
164    /**
165     * Reads the given input class path entry.
166     */
167    private void readInput(String          messagePrefix,
168                           ClassPathEntry  classPathEntry,
169                           DataEntryReader dataEntryReader) throws IOException
170    {
171        try
172        {
173            // Create a reader that can unwrap jars, wars, ears, and zips.
174            DataEntryReader reader =
175                DataEntryReaderFactory.createDataEntryReader(messagePrefix,
176                                                             classPathEntry,
177                                                             dataEntryReader);
178
179            // Create the data entry pump.
180            DirectoryPump directoryPump =
181                new DirectoryPump(classPathEntry.getFile());
182
183            // Pump the data entries into the reader.
184            directoryPump.pumpDataEntries(reader);
185        }
186        catch (IOException ex)
187        {
188            throw (IOException)new IOException("Can't read [" + classPathEntry + "] (" + ex.getMessage() + ")").initCause(ex);
189        }
190    }
191}
192