summaryrefslogtreecommitdiffstats
path: root/src/proguard/OutputWriter.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/proguard/OutputWriter.java')
-rw-r--r--src/proguard/OutputWriter.java256
1 files changed, 256 insertions, 0 deletions
diff --git a/src/proguard/OutputWriter.java b/src/proguard/OutputWriter.java
new file mode 100644
index 0000000..10c18fb
--- /dev/null
+++ b/src/proguard/OutputWriter.java
@@ -0,0 +1,256 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ * of Java bytecode.
+ *
+ * Copyright (c) 2002-2009 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.");
+ }
+
+ // Perform some checks on the output jars.
+ for (int index = 0; index < programJars.size() - 1; index++)
+ {
+ ClassPathEntry entry = programJars.get(index);
+ if (entry.isOutput())
+ {
+ // Check if all but the last output jars have filters.
+ 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 jars will be empty.");
+ }
+
+ // Check if the output jar name is different from the input jar names.
+ 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.");
+ }
+ }
+ }
+ }
+
+ 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/**",
+ 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 new IOException("Can't write [" + classPath.get(fromOutputIndex).getName() + "] (" + ex.getMessage() + ")");
+ }
+ }
+
+
+ /**
+ * 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;
+ }
+}