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; 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 // 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 } 119 120 // Print out a summary of the warnings, if necessary. 121 int warningCount = warningPrinter.getWarningCount(); 122 if (warningCount > 0) 123 { 124 System.err.println("Warning: there were " + warningCount + 125 " classes in incorrectly named files."); 126 System.err.println(" You should make sure all file names correspond to their class names."); 127 System.err.println(" The directory hierarchies must correspond to the package hierarchies."); 128 129 if (!configuration.ignoreWarnings) 130 { 131 System.err.println(" If you don't mind the mentioned classes not being written out,"); 132 System.err.println(" you could try your luck using the '-ignorewarnings' option."); 133 throw new IOException("Please correct the above warnings first."); 134 } 135 } 136 } 137 138 139 /** 140 * Performs some sanity checks on the class paths. 141 */ 142 private void checkInputOutput(ClassPath inputClassPath, 143 ClassPath outputClassPath) 144 throws IOException 145 { 146 if (inputClassPath == null || 147 outputClassPath == null) 148 { 149 return; 150 } 151 152 for (int index1 = 0; index1 < inputClassPath.size(); index1++) 153 { 154 ClassPathEntry entry1 = inputClassPath.get(index1); 155 if (!entry1.isOutput()) 156 { 157 for (int index2 = 0; index2 < outputClassPath.size(); index2++) 158 { 159 ClassPathEntry entry2 = outputClassPath.get(index2); 160 if (entry2.isOutput() && 161 entry2.getName().equals(entry1.getName())) 162 { 163 throw new IOException("Input jars and output jars must be different ["+entry1.getName()+"]"); 164 } 165 } 166 } 167 } 168 } 169 170 171 /** 172 * Reads all input entries from the given class path. 173 */ 174 private void readInput(String messagePrefix, 175 ClassPath classPath, 176 DataEntryReader reader) throws IOException 177 { 178 readInput(messagePrefix, 179 classPath, 180 0, 181 classPath.size(), 182 reader); 183 } 184 185 186 /** 187 * Reads all input entries from the given section of the given class path. 188 */ 189 public void readInput(String messagePrefix, 190 ClassPath classPath, 191 int fromIndex, 192 int toIndex, 193 DataEntryReader reader) throws IOException 194 { 195 for (int index = fromIndex; index < toIndex; index++) 196 { 197 ClassPathEntry entry = classPath.get(index); 198 if (!entry.isOutput()) 199 { 200 readInput(messagePrefix, entry, reader); 201 } 202 } 203 } 204 205 206 /** 207 * Reads the given input class path entry. 208 */ 209 private void readInput(String messagePrefix, 210 ClassPathEntry classPathEntry, 211 DataEntryReader dataEntryReader) throws IOException 212 { 213 try 214 { 215 // Create a reader that can unwrap jars, wars, ears, and zips. 216 DataEntryReader reader = 217 DataEntryReaderFactory.createDataEntryReader(messagePrefix, 218 classPathEntry, 219 dataEntryReader); 220 221 // Create the data entry pump. 222 DirectoryPump directoryPump = 223 new DirectoryPump(classPathEntry.getFile()); 224 225 // Pump the data entries into the reader. 226 directoryPump.pumpDataEntries(reader); 227 } 228 catch (IOException ex) 229 { 230 throw new IOException("Can't read [" + classPathEntry + "] (" + ex.getMessage() + ")"); 231 } 232 } 233} 234