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