/projects/proguard-4.9/src/proguard/OutputWriter.java
Java | 296 lines | 192 code | 36 blank | 68 comment | 32 complexity | 9de005ea14737c54b19abb69b05f4ec7 MD5 | raw file
- /*
- * ProGuard -- shrinking, optimization, obfuscation, and preverification
- * of Java bytecode.
- *
- * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu)
- *
- * 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;
- // Perform a check on the first jar.
- ClassPathEntry firstEntry = programJars.get(0);
- if (firstEntry.isOutput())
- {
- throw new IOException("The output jar [" + firstEntry.getName() +
- "] must be specified after an input jar, or it will be empty.");
- }
- // Check if the first of two subsequent the output jars has a filter.
- for (int index = 0; index < programJars.size() - 1; index++)
- {
- ClassPathEntry entry = programJars.get(index);
- if (entry.isOutput())
- {
- if (entry.getFilter() == null &&
- entry.getJarFilter() == null &&
- entry.getWarFilter() == null &&
- entry.getEarFilter() == null &&
- entry.getZipFilter() == null &&
- programJars.get(index + 1).isOutput())
- {
- throw new IOException("The output jar [" + entry.getName() +
- "] must have a filter, or all subsequent output jars will be empty.");
- }
- }
- }
- // Check if the output jar names are different from the input jar names.
- for (int outIndex = 0; outIndex < programJars.size() - 1; outIndex++)
- {
- ClassPathEntry entry = programJars.get(outIndex);
- if (entry.isOutput())
- {
- for (int inIndex = 0; inIndex < programJars.size(); inIndex++)
- {
- ClassPathEntry otherEntry = programJars.get(inIndex);
- if (!otherEntry.isOutput() &&
- entry.getFile().equals(otherEntry.getFile()))
- {
- throw new IOException("The output jar [" + entry.getName() +
- "] must be different from all input jars.");
- }
- }
- }
- }
- // Check for potential problems with mixed-case class names on
- // case-insensitive file systems.
- if (configuration.obfuscate &&
- configuration.useMixedCaseClassNames &&
- configuration.classObfuscationDictionary == null &&
- (configuration.note == null ||
- !configuration.note.isEmpty()))
- {
- String os = System.getProperty("os.name").toLowerCase();
- if (os.startsWith("windows") ||
- os.startsWith("mac os"))
- {
- // Go over all program class path entries.
- for (int index = 0; index < programJars.size(); index++)
- {
- // Is it an output directory?
- ClassPathEntry entry = programJars.get(index);
- if (entry.isOutput() &&
- !entry.isJar() &&
- !entry.isWar() &&
- !entry.isEar() &&
- !entry.isZip())
- {
- System.out.println("Note: you're writing the processed class files to a directory [" + entry.getName() +"].");
- System.out.println(" This will likely cause problems with obfuscated mixed-case class names.");
- System.out.println(" You should consider writing the output to a jar file, or otherwise");
- System.out.println(" specify '-dontusemixedcaseclassnames'.");
- break;
- }
- }
- }
- }
- 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;
- // 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;
- }
- }