1 /* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2014 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.ClassUtil; 25 import proguard.io.*; 26 27 import java.io.IOException; 28 import java.util.*; 29 30 /** 31 * This class writes the output class files. 32 * 33 * @author Eric Lafortune 34 */ 35 public class OutputWriter 36 { 37 private final Configuration configuration; 38 39 40 /** 41 * Creates a new OutputWriter to write output class files as specified by 42 * the given configuration. 43 */ 44 public OutputWriter(Configuration configuration) 45 { 46 this.configuration = configuration; 47 } 48 49 50 /** 51 * Writes the given class pool to class files, based on the current 52 * configuration. 53 */ 54 public void execute(ClassPool programClassPool) throws IOException 55 { 56 ClassPath programJars = configuration.programJars; 57 58 int firstInputIndex = 0; 59 int lastInputIndex = 0; 60 61 // Go over all program class path entries. 62 for (int index = 0; index < programJars.size(); index++) 63 { 64 // Is it an input entry? 65 ClassPathEntry entry = programJars.get(index); 66 if (!entry.isOutput()) 67 { 68 // Remember the index of the last input entry. 69 lastInputIndex = index; 70 } 71 else 72 { 73 // Check if this the last output entry in a series. 74 int nextIndex = index + 1; 75 if (nextIndex == programJars.size() || 76 !programJars.get(nextIndex).isOutput()) 77 { 78 // Write the processed input entries to the output entries. 79 writeOutput(programClassPool, 80 programJars, 81 firstInputIndex, 82 lastInputIndex + 1, 83 nextIndex); 84 85 // Start with the next series of input entries. 86 firstInputIndex = nextIndex; 87 } 88 } 89 } 90 } 91 92 93 /** 94 * Transfers the specified input jars to the specified output jars. 95 */ 96 private void writeOutput(ClassPool programClassPool, 97 ClassPath classPath, 98 int fromInputIndex, 99 int fromOutputIndex, 100 int toOutputIndex) 101 throws IOException 102 { 103 try 104 { 105 // Construct the writer that can write jars, wars, ears, zips, and 106 // directories, cascading over the specified output entries. 107 DataEntryWriter writer = 108 DataEntryWriterFactory.createDataEntryWriter(classPath, 109 fromOutputIndex, 110 toOutputIndex); 111 112 // The writer will be used to write possibly obfuscated class files. 113 DataEntryReader classRewriter = 114 new ClassRewriter(programClassPool, writer); 115 116 // The writer will also be used to write resource files. 117 DataEntryReader resourceCopier = 118 new DataEntryCopier(writer); 119 120 DataEntryReader resourceRewriter = resourceCopier; 121 122 // Adapt resource file contents and names, if necessary. 123 if (configuration.obfuscate) 124 { 125 // Wrap the resource writer with a filter and a data entry 126 // rewriter, if required. 127 if (configuration.adaptResourceFileContents != null) 128 { 129 resourceRewriter = 130 new NameFilter(configuration.adaptResourceFileContents, 131 new NameFilter("META-INF/MANIFEST.MF,META-INF/*.SF", 132 new ManifestRewriter(programClassPool, writer), 133 new DataEntryRewriter(programClassPool, writer)), 134 resourceRewriter); 135 } 136 137 // Wrap the resource writer with a filter and a data entry 138 // renamer, if required. 139 if (configuration.adaptResourceFileNames != null) 140 { 141 Map packagePrefixMap = createPackagePrefixMap(programClassPool); 142 143 resourceRewriter = 144 new NameFilter(configuration.adaptResourceFileNames, 145 new DataEntryObfuscator(programClassPool, 146 packagePrefixMap, 147 resourceRewriter), 148 resourceRewriter); 149 } 150 } 151 152 DataEntryReader directoryRewriter = null; 153 154 // Wrap the directory writer with a filter and a data entry renamer, 155 // if required. 156 if (configuration.keepDirectories != null) 157 { 158 Map packagePrefixMap = createPackagePrefixMap(programClassPool); 159 160 directoryRewriter = 161 new NameFilter(configuration.keepDirectories, 162 new DataEntryRenamer(packagePrefixMap, 163 resourceCopier, 164 resourceCopier)); 165 } 166 167 // Create the reader that can write class files and copy directories 168 // and resource files to the main writer. 169 DataEntryReader reader = 170 new ClassFilter( classRewriter, 171 new DirectoryFilter(directoryRewriter, 172 resourceRewriter)); 173 174 // Go over the specified input entries and write their processed 175 // versions. 176 new InputReader(configuration).readInput(" Copying resources from program ", 177 classPath, 178 fromInputIndex, 179 fromOutputIndex, 180 reader); 181 182 // Close all output entries. 183 writer.close(); 184 } 185 catch (IOException ex) 186 { 187 throw (IOException)new IOException("Can't write [" + classPath.get(fromOutputIndex).getName() + "] (" + ex.getMessage() + ")").initCause(ex); 188 } 189 } 190 191 192 /** 193 * Creates a map of old package prefixes to new package prefixes, based on 194 * the given class pool. 195 */ 196 private static Map createPackagePrefixMap(ClassPool classPool) 197 { 198 Map packagePrefixMap = new HashMap(); 199 200 Iterator iterator = classPool.classNames(); 201 while (iterator.hasNext()) 202 { 203 String className = (String)iterator.next(); 204 String packagePrefix = ClassUtil.internalPackagePrefix(className); 205 206 String mappedNewPackagePrefix = (String)packagePrefixMap.get(packagePrefix); 207 if (mappedNewPackagePrefix == null || 208 !mappedNewPackagePrefix.equals(packagePrefix)) 209 { 210 String newClassName = classPool.getClass(className).getName(); 211 String newPackagePrefix = ClassUtil.internalPackagePrefix(newClassName); 212 213 packagePrefixMap.put(packagePrefix, newPackagePrefix); 214 } 215 } 216 217 return packagePrefixMap; 218 } 219 } 220