| /* |
| * ProGuard -- shrinking, optimization, obfuscation, and preverification |
| * of Java bytecode. |
| * |
| * Copyright (c) 2002-2014 Eric Lafortune ([email protected]) |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the Free |
| * Software Foundation; either version 2 of the License, or (at your option) |
| * any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| * more details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with this program; if not, write to the Free Software Foundation, Inc., |
| * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| package proguard; |
| |
| import proguard.classfile.ClassPool; |
| import proguard.classfile.util.ClassUtil; |
| import proguard.io.*; |
| |
| import java.io.IOException; |
| import java.util.*; |
| |
| /** |
| * This class writes the output class files. |
| * |
| * @author Eric Lafortune |
| */ |
| public class OutputWriter |
| { |
| private final Configuration configuration; |
| |
| |
| /** |
| * Creates a new OutputWriter to write output class files as specified by |
| * the given configuration. |
| */ |
| public OutputWriter(Configuration configuration) |
| { |
| this.configuration = configuration; |
| } |
| |
| |
| /** |
| * Writes the given class pool to class files, based on the current |
| * configuration. |
| */ |
| public void execute(ClassPool programClassPool) throws IOException |
| { |
| ClassPath programJars = configuration.programJars; |
| |
| int firstInputIndex = 0; |
| int lastInputIndex = 0; |
| |
| // Go over all program class path entries. |
| for (int index = 0; index < programJars.size(); index++) |
| { |
| // Is it an input entry? |
| ClassPathEntry entry = programJars.get(index); |
| if (!entry.isOutput()) |
| { |
| // Remember the index of the last input entry. |
| lastInputIndex = index; |
| } |
| else |
| { |
| // Check if this the last output entry in a series. |
| int nextIndex = index + 1; |
| if (nextIndex == programJars.size() || |
| !programJars.get(nextIndex).isOutput()) |
| { |
| // Write the processed input entries to the output entries. |
| writeOutput(programClassPool, |
| programJars, |
| firstInputIndex, |
| lastInputIndex + 1, |
| nextIndex); |
| |
| // Start with the next series of input entries. |
| firstInputIndex = nextIndex; |
| } |
| } |
| } |
| } |
| |
| |
| /** |
| * Transfers the specified input jars to the specified output jars. |
| */ |
| private void writeOutput(ClassPool programClassPool, |
| ClassPath classPath, |
| int fromInputIndex, |
| int fromOutputIndex, |
| int toOutputIndex) |
| throws IOException |
| { |
| try |
| { |
| // Construct the writer that can write jars, wars, ears, zips, and |
| // directories, cascading over the specified output entries. |
| DataEntryWriter writer = |
| DataEntryWriterFactory.createDataEntryWriter(classPath, |
| fromOutputIndex, |
| toOutputIndex); |
| |
| // The writer will be used to write possibly obfuscated class files. |
| DataEntryReader classRewriter = |
| new ClassRewriter(programClassPool, writer); |
| |
| // The writer will also be used to write resource files. |
| DataEntryReader resourceCopier = |
| new DataEntryCopier(writer); |
| |
| DataEntryReader resourceRewriter = resourceCopier; |
| |
| // Adapt resource file contents and names, if necessary. |
| if (configuration.obfuscate) |
| { |
| // Wrap the resource writer with a filter and a data entry |
| // rewriter, if required. |
| if (configuration.adaptResourceFileContents != null) |
| { |
| resourceRewriter = |
| new NameFilter(configuration.adaptResourceFileContents, |
| new NameFilter("META-INF/MANIFEST.MF,META-INF/*.SF", |
| new ManifestRewriter(programClassPool, writer), |
| new DataEntryRewriter(programClassPool, writer)), |
| resourceRewriter); |
| } |
| |
| // Wrap the resource writer with a filter and a data entry |
| // renamer, if required. |
| if (configuration.adaptResourceFileNames != null) |
| { |
| Map packagePrefixMap = createPackagePrefixMap(programClassPool); |
| |
| resourceRewriter = |
| new NameFilter(configuration.adaptResourceFileNames, |
| new DataEntryObfuscator(programClassPool, |
| packagePrefixMap, |
| resourceRewriter), |
| resourceRewriter); |
| } |
| } |
| |
| DataEntryReader directoryRewriter = null; |
| |
| // Wrap the directory writer with a filter and a data entry renamer, |
| // if required. |
| if (configuration.keepDirectories != null) |
| { |
| Map packagePrefixMap = createPackagePrefixMap(programClassPool); |
| |
| directoryRewriter = |
| new NameFilter(configuration.keepDirectories, |
| new DataEntryRenamer(packagePrefixMap, |
| resourceCopier, |
| resourceCopier)); |
| } |
| |
| // Create the reader that can write class files and copy directories |
| // and resource files to the main writer. |
| DataEntryReader reader = |
| new ClassFilter( classRewriter, |
| new DirectoryFilter(directoryRewriter, |
| resourceRewriter)); |
| |
| // Go over the specified input entries and write their processed |
| // versions. |
| new InputReader(configuration).readInput(" Copying resources from program ", |
| classPath, |
| fromInputIndex, |
| fromOutputIndex, |
| reader); |
| |
| // Close all output entries. |
| writer.close(); |
| } |
| catch (IOException ex) |
| { |
| throw (IOException)new IOException("Can't write [" + classPath.get(fromOutputIndex).getName() + "] (" + ex.getMessage() + ")").initCause(ex); |
| } |
| } |
| |
| |
| /** |
| * Creates a map of old package prefixes to new package prefixes, based on |
| * the given class pool. |
| */ |
| private static Map createPackagePrefixMap(ClassPool classPool) |
| { |
| Map packagePrefixMap = new HashMap(); |
| |
| Iterator iterator = classPool.classNames(); |
| while (iterator.hasNext()) |
| { |
| String className = (String)iterator.next(); |
| String packagePrefix = ClassUtil.internalPackagePrefix(className); |
| |
| String mappedNewPackagePrefix = (String)packagePrefixMap.get(packagePrefix); |
| if (mappedNewPackagePrefix == null || |
| !mappedNewPackagePrefix.equals(packagePrefix)) |
| { |
| String newClassName = classPool.getClass(className).getName(); |
| String newPackagePrefix = ClassUtil.internalPackagePrefix(newClassName); |
| |
| packagePrefixMap.put(packagePrefix, newPackagePrefix); |
| } |
| } |
| |
| return packagePrefixMap; |
| } |
| } |