aboutsummaryrefslogtreecommitdiffstats
path: root/src/proguard/io
diff options
context:
space:
mode:
Diffstat (limited to 'src/proguard/io')
-rw-r--r--src/proguard/io/CascadingDataEntryWriter.java94
-rw-r--r--src/proguard/io/ClassFilter.java59
-rw-r--r--src/proguard/io/ClassReader.java115
-rw-r--r--src/proguard/io/ClassRewriter.java80
-rw-r--r--src/proguard/io/DataEntry.java62
-rw-r--r--src/proguard/io/DataEntryCopier.java247
-rw-r--r--src/proguard/io/DataEntryDirectoryFilter.java40
-rw-r--r--src/proguard/io/DataEntryFilter.java38
-rw-r--r--src/proguard/io/DataEntryNameFilter.java54
-rw-r--r--src/proguard/io/DataEntryObfuscator.java131
-rw-r--r--src/proguard/io/DataEntryParentFilter.java51
-rw-r--r--src/proguard/io/DataEntryPump.java43
-rw-r--r--src/proguard/io/DataEntryReader.java38
-rw-r--r--src/proguard/io/DataEntryRenamer.java104
-rw-r--r--src/proguard/io/DataEntryRewriter.java148
-rw-r--r--src/proguard/io/DataEntryWriter.java73
-rw-r--r--src/proguard/io/DirectoryFilter.java58
-rw-r--r--src/proguard/io/DirectoryPump.java78
-rw-r--r--src/proguard/io/DirectoryWriter.java165
-rw-r--r--src/proguard/io/FileDataEntry.java96
-rw-r--r--src/proguard/io/FilteredDataEntryReader.java90
-rw-r--r--src/proguard/io/FilteredDataEntryWriter.java125
-rw-r--r--src/proguard/io/Finisher.java37
-rw-r--r--src/proguard/io/JarReader.java75
-rw-r--r--src/proguard/io/JarWriter.java235
-rw-r--r--src/proguard/io/ManifestRewriter.java216
-rw-r--r--src/proguard/io/NameFilter.java83
-rw-r--r--src/proguard/io/ParentDataEntryWriter.java75
-rw-r--r--src/proguard/io/RenamedDataEntry.java83
-rw-r--r--src/proguard/io/ZipDataEntry.java98
-rw-r--r--src/proguard/io/package.html4
31 files changed, 2895 insertions, 0 deletions
diff --git a/src/proguard/io/CascadingDataEntryWriter.java b/src/proguard/io/CascadingDataEntryWriter.java
new file mode 100644
index 0000000..15719e6
--- /dev/null
+++ b/src/proguard/io/CascadingDataEntryWriter.java
@@ -0,0 +1,94 @@
+/*
+ * 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.io;
+
+import java.io.*;
+
+/**
+ * This DataEntryWriter delegates to a given DataEntryWriter, or failing that,
+ * to another given DataEntryWriter.
+ *
+ * @author Eric Lafortune
+ */
+public class CascadingDataEntryWriter implements DataEntryWriter
+{
+ private DataEntryWriter dataEntryWriter1;
+ private DataEntryWriter dataEntryWriter2;
+
+
+ /**
+ * Creates a new CascadingDataEntryWriter.
+ * @param dataEntryWriter1 the DataEntryWriter to which the writing will be
+ * delegated first.
+ * @param dataEntryWriter2 the DataEntryWriter to which the writing will be
+ * delegated, if the first one can't provide an
+ * output stream.
+ */
+ public CascadingDataEntryWriter(DataEntryWriter dataEntryWriter1,
+ DataEntryWriter dataEntryWriter2)
+ {
+ this.dataEntryWriter1 = dataEntryWriter1;
+ this.dataEntryWriter2 = dataEntryWriter2;
+ }
+
+
+ // Implementations for DataEntryWriter.
+
+
+ public boolean createDirectory(DataEntry dataEntry) throws IOException
+ {
+ // Try to create a directory with the first data entry writer, or
+ // otherwise with the second data entry writer.
+ return dataEntryWriter1.createDirectory(dataEntry) ||
+ dataEntryWriter2.createDirectory(dataEntry);
+ }
+
+
+ public OutputStream getOutputStream(DataEntry dataEntry) throws IOException
+ {
+ return getOutputStream(dataEntry, null);
+ }
+
+
+ public OutputStream getOutputStream(DataEntry dataEntry,
+ Finisher finisher) throws IOException
+ {
+ // Try to get an output stream from the first data entry writer.
+ OutputStream outputStream =
+ dataEntryWriter1.getOutputStream(dataEntry, finisher);
+
+ // Return it, if it's not null. Otherwise try to get an output stream
+ // from the second data entry writer.
+ return outputStream != null ?
+ outputStream :
+ dataEntryWriter2.getOutputStream(dataEntry, finisher);
+ }
+
+
+ public void close() throws IOException
+ {
+ dataEntryWriter1.close();
+ dataEntryWriter2.close();
+
+ dataEntryWriter1 = null;
+ dataEntryWriter2 = null;
+ }
+}
diff --git a/src/proguard/io/ClassFilter.java b/src/proguard/io/ClassFilter.java
new file mode 100644
index 0000000..bf591ab
--- /dev/null
+++ b/src/proguard/io/ClassFilter.java
@@ -0,0 +1,59 @@
+/*
+ * 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.io;
+
+import proguard.classfile.ClassConstants;
+import proguard.util.ExtensionMatcher;
+
+import java.io.IOException;
+
+
+/**
+ * This DataEntryReader delegates to one of two other DataEntryReader instances,
+ * depending on the extension of the data entry.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassFilter extends FilteredDataEntryReader
+{
+ /**
+ * Creates a new ClassFilter that delegates reading classes to the
+ * given reader.
+ */
+ public ClassFilter(DataEntryReader classReader)
+ {
+ this(classReader, null);
+ }
+
+
+ /**
+ * Creates a new ClassFilter that delegates to either of the two given
+ * readers.
+ */
+ public ClassFilter(DataEntryReader classReader,
+ DataEntryReader dataEntryReader)
+ {
+ super(new DataEntryNameFilter(
+ new ExtensionMatcher(ClassConstants.CLASS_FILE_EXTENSION)),
+ classReader,
+ dataEntryReader);
+ }
+}
diff --git a/src/proguard/io/ClassReader.java b/src/proguard/io/ClassReader.java
new file mode 100644
index 0000000..e21968c
--- /dev/null
+++ b/src/proguard/io/ClassReader.java
@@ -0,0 +1,115 @@
+/*
+ * 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.io;
+
+import proguard.classfile.*;
+import proguard.classfile.io.*;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.ClassVisitor;
+
+import java.io.*;
+
+/**
+ * This DataEntryReader applies a given ClassVisitor to the class
+ * definitions that it reads.
+ * <p>
+ * Class files are read as ProgramClass objects or LibraryClass objects,
+ * depending on the <code>isLibrary</code> flag.
+ * <p>
+ * In case of libraries, only public classes are considered, if the
+ * <code>skipNonPublicLibraryClasses</code> flag is set.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassReader implements DataEntryReader
+{
+ private final boolean isLibrary;
+ private final boolean skipNonPublicLibraryClasses;
+ private final boolean skipNonPublicLibraryClassMembers;
+ private final WarningPrinter warningPrinter;
+ private final ClassVisitor classVisitor;
+
+
+ /**
+ * Creates a new DataEntryClassFilter for reading the specified
+ * Clazz objects.
+ */
+ public ClassReader(boolean isLibrary,
+ boolean skipNonPublicLibraryClasses,
+ boolean skipNonPublicLibraryClassMembers,
+ WarningPrinter warningPrinter,
+ ClassVisitor classVisitor)
+ {
+ this.isLibrary = isLibrary;
+ this.skipNonPublicLibraryClasses = skipNonPublicLibraryClasses;
+ this.skipNonPublicLibraryClassMembers = skipNonPublicLibraryClassMembers;
+ this.warningPrinter = warningPrinter;
+ this.classVisitor = classVisitor;
+ }
+
+
+ // Implementations for DataEntryReader.
+
+ public void read(DataEntry dataEntry) throws IOException
+ {
+ try
+ {
+ // Get the input stream.
+ InputStream inputStream = dataEntry.getInputStream();
+
+ // Wrap it into a data input stream.
+ DataInputStream dataInputStream = new DataInputStream(inputStream);
+
+ // Create a Clazz representation.
+ Clazz clazz;
+ if (isLibrary)
+ {
+ clazz = new LibraryClass();
+ clazz.accept(new LibraryClassReader(dataInputStream, skipNonPublicLibraryClasses, skipNonPublicLibraryClassMembers));
+ }
+ else
+ {
+ clazz = new ProgramClass();
+ clazz.accept(new ProgramClassReader(dataInputStream));
+ }
+
+ // Apply the visitor, if we have a real class.
+ String className = clazz.getName();
+ if (className != null)
+ {
+ if (!dataEntry.getName().replace(File.pathSeparatorChar, ClassConstants.INTERNAL_PACKAGE_SEPARATOR).equals(className+ClassConstants.CLASS_FILE_EXTENSION) &&
+ warningPrinter != null)
+ {
+ warningPrinter.print(className,
+ "Warning: class [" + dataEntry.getName() + "] unexpectedly contains class [" + ClassUtil.externalClassName(className) + "]");
+ }
+
+ clazz.accept(classVisitor);
+ }
+
+ dataEntry.closeInputStream();
+ }
+ catch (Exception ex)
+ {
+ throw new IOException("Can't process class ["+dataEntry.getName()+"] ("+ex.getMessage()+")");
+ }
+ }
+}
diff --git a/src/proguard/io/ClassRewriter.java b/src/proguard/io/ClassRewriter.java
new file mode 100644
index 0000000..bd19e1d
--- /dev/null
+++ b/src/proguard/io/ClassRewriter.java
@@ -0,0 +1,80 @@
+/*
+ * 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.io;
+
+import proguard.classfile.*;
+import proguard.classfile.io.ProgramClassWriter;
+
+import java.io.*;
+
+
+/**
+ * This DataEntryReader reads class entries and writes their corresponding
+ * versions from the ClassPool to a given DataEntryWriter.
+ *
+ * @author Eric Lafortune
+ */
+public class ClassRewriter implements DataEntryReader
+{
+ private final ClassPool classPool;
+ private final DataEntryWriter dataEntryWriter;
+
+
+ public ClassRewriter(ClassPool classPool,
+ DataEntryWriter dataEntryWriter)
+ {
+ this.classPool = classPool;
+ this.dataEntryWriter = dataEntryWriter;
+ }
+
+
+ // Implementations for DataEntryReader.
+
+ public void read(DataEntry dataEntry) throws IOException
+ {
+ String inputName = dataEntry.getName();
+ String className = inputName.substring(0, inputName.length() - ClassConstants.CLASS_FILE_EXTENSION.length());
+
+ // Find the modified class corrsponding to the input entry.
+ ProgramClass programClass = (ProgramClass)classPool.getClass(className);
+ if (programClass != null)
+ {
+ // Rename the data entry if necessary.
+ String newClassName = programClass.getName();
+ if (!className.equals(newClassName))
+ {
+ dataEntry = new RenamedDataEntry(dataEntry, newClassName + ClassConstants.CLASS_FILE_EXTENSION);
+ }
+
+ // Get the output entry corresponding to this input entry.
+ OutputStream outputStream = dataEntryWriter.getOutputStream(dataEntry);
+ if (outputStream != null)
+ {
+ // Write the class to the output entry.
+ DataOutputStream classOutputStream = new DataOutputStream(outputStream);
+
+ new ProgramClassWriter(classOutputStream).visitProgramClass(programClass);
+
+ classOutputStream.flush();
+ }
+ }
+ }
+}
diff --git a/src/proguard/io/DataEntry.java b/src/proguard/io/DataEntry.java
new file mode 100644
index 0000000..af0e373
--- /dev/null
+++ b/src/proguard/io/DataEntry.java
@@ -0,0 +1,62 @@
+/*
+ * 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.io;
+
+import java.io.*;
+
+/**
+ * This interface describes a data entry, e.g. a ZIP entry, a file, or a
+ * directory.
+ *
+ * @author Eric Lafortune
+ */
+public interface DataEntry
+{
+ /**
+ * Returns the name of this data entry.
+ */
+ public String getName();
+
+ /**
+ * Returns whether the data entry represents a directory.
+ */
+ public boolean isDirectory();
+
+
+ /**
+ * Returns an input stream for reading the content of this data entry.
+ * The data entry may not represent a directory.
+ */
+ public InputStream getInputStream() throws IOException;
+
+
+ /**
+ * Closes the previously retrieved InputStream.
+ */
+ public void closeInputStream() throws IOException;
+
+
+ /**
+ * Returns the parent of this data entry, or <code>null</null> if it doesn't
+ * have one.
+ */
+ public DataEntry getParent();
+}
diff --git a/src/proguard/io/DataEntryCopier.java b/src/proguard/io/DataEntryCopier.java
new file mode 100644
index 0000000..faaa555
--- /dev/null
+++ b/src/proguard/io/DataEntryCopier.java
@@ -0,0 +1,247 @@
+/*
+ * 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.io;
+
+import proguard.util.ExtensionMatcher;
+
+import java.io.*;
+
+
+/**
+ * This DataEntryReader writes the ZIP entries and files that it reads to a
+ * given DataEntryWriter.
+ *
+ * @author Eric Lafortune
+ */
+public class DataEntryCopier implements DataEntryReader
+{
+ private static final int BUFFER_SIZE = 1024;
+
+ private final DataEntryWriter dataEntryWriter;
+ private final byte[] buffer = new byte[BUFFER_SIZE];
+
+
+
+ public DataEntryCopier(DataEntryWriter dataEntryWriter)
+ {
+ this.dataEntryWriter = dataEntryWriter;
+ }
+
+
+ // Implementations for DataEntryReader.
+
+ public void read(DataEntry dataEntry) throws IOException
+ {
+ try
+ {
+ if (dataEntry.isDirectory())
+ {
+ dataEntryWriter.createDirectory(dataEntry);
+ }
+ else
+ {
+ // Get the output entry corresponding to this input entry.
+ OutputStream outputStream = dataEntryWriter.getOutputStream(dataEntry);
+ if (outputStream != null)
+ {
+ InputStream inputStream = dataEntry.getInputStream();
+
+ // Copy the data from the input entry to the output entry.
+ copyData(inputStream, outputStream);
+
+ // Close the data entries.
+ dataEntry.closeInputStream();
+ }
+ }
+ }
+ catch (IOException ex)
+ {
+ System.err.println("Warning: can't write resource [" + dataEntry.getName() + "] (" + ex.getMessage() + ")");
+ }
+ }
+
+
+ /**
+ * Copies all data that it can read from the given input stream to the
+ * given output stream.
+ */
+ protected void copyData(InputStream inputStream,
+ OutputStream outputStream)
+ throws IOException
+ {
+ while (true)
+ {
+ int count = inputStream.read(buffer);
+ if (count < 0)
+ {
+ break;
+ }
+ outputStream.write(buffer, 0, count);
+ }
+
+ outputStream.flush();
+ }
+
+
+ /**
+ * A main method for testing file/jar/war/directory copying.
+ */
+ public static void main(String[] args)
+ {
+ try
+ {
+ String input = args[0];
+ String output = args[1];
+
+ boolean outputIsJar = output.endsWith(".jar");
+ boolean outputIsWar = output.endsWith(".war");
+ boolean outputIsEar = output.endsWith(".ear");
+ boolean outputIsZip = output.endsWith(".zip");
+
+ DataEntryWriter writer = new DirectoryWriter(new File(output),
+ outputIsJar ||
+ outputIsWar ||
+ outputIsEar ||
+ outputIsZip);
+
+ if (!outputIsJar)
+ {
+ // Zip up any zips, if necessary.
+ DataEntryWriter zipWriter = new JarWriter(writer);
+ if (outputIsZip)
+ {
+ // Always zip.
+ writer = zipWriter;
+ }
+ else
+ {
+ // Only zip up zips.
+ writer = new FilteredDataEntryWriter(new DataEntryParentFilter(
+ new DataEntryNameFilter(
+ new ExtensionMatcher(".zip"))),
+ zipWriter,
+ writer);
+ }
+
+ // Zip up any wars, if necessary.
+ DataEntryWriter warWriter = new JarWriter(writer);
+ if (outputIsWar)
+ {
+ // Always zip.
+ writer = warWriter;
+ }
+ else
+ {
+ // Only zip up wars.
+ writer = new FilteredDataEntryWriter(new DataEntryParentFilter(
+ new DataEntryNameFilter(
+ new ExtensionMatcher(".war"))),
+ warWriter,
+ writer);
+ }
+ }
+
+ // Zip up any jars, if necessary.
+ DataEntryWriter jarWriter = new JarWriter(writer);
+ if (outputIsJar)
+ {
+ // Always zip.
+ writer = jarWriter;
+ }
+ else
+ {
+ // Only zip up jars.
+ writer = new FilteredDataEntryWriter(new DataEntryParentFilter(
+ new DataEntryNameFilter(
+ new ExtensionMatcher(".jar"))),
+ jarWriter,
+ writer);
+ }
+
+
+ // Create the copying DataEntryReader.
+ DataEntryReader reader = new DataEntryCopier(writer);
+
+
+ boolean inputIsJar = input.endsWith(".jar");
+ boolean inputIsWar = input.endsWith(".war");
+ boolean inputIsZip = input.endsWith(".zip");
+
+ // Unzip any jars, if necessary.
+ DataEntryReader jarReader = new JarReader(reader);
+ if (inputIsJar)
+ {
+ // Always unzip.
+ reader = jarReader;
+ }
+ else
+ {
+ // Only unzip jar entries.
+ reader = new FilteredDataEntryReader(new DataEntryNameFilter(
+ new ExtensionMatcher(".jar")),
+ jarReader,
+ reader);
+
+ // Unzip any wars, if necessary.
+ DataEntryReader warReader = new JarReader(reader);
+ if (inputIsWar)
+ {
+ // Always unzip.
+ reader = warReader;
+ }
+ else
+ {
+ // Only unzip war entries.
+ reader = new FilteredDataEntryReader(new DataEntryNameFilter(
+ new ExtensionMatcher(".war")),
+ warReader,
+ reader);
+ }
+
+ // Unzip any zips, if necessary.
+ DataEntryReader zipReader = new JarReader(reader);
+ if (inputIsZip)
+ {
+ // Always unzip.
+ reader = zipReader;
+ }
+ else
+ {
+ // Only unzip zip entries.
+ reader = new FilteredDataEntryReader(new DataEntryNameFilter(
+ new ExtensionMatcher(".zip")),
+ zipReader,
+ reader);
+ }
+ }
+
+ DirectoryPump directoryReader = new DirectoryPump(new File(input));
+
+ directoryReader.pumpDataEntries(reader);
+
+ writer.close();
+ }
+ catch (Exception ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+}
diff --git a/src/proguard/io/DataEntryDirectoryFilter.java b/src/proguard/io/DataEntryDirectoryFilter.java
new file mode 100644
index 0000000..bb36f3e
--- /dev/null
+++ b/src/proguard/io/DataEntryDirectoryFilter.java
@@ -0,0 +1,40 @@
+/*
+ * 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.io;
+
+import proguard.util.StringMatcher;
+
+/**
+ * This DataEntryFilter filters data entries based on whether they represent
+ * directories.
+ *
+ * @author Eric Lafortune
+ */
+public class DataEntryDirectoryFilter
+implements DataEntryFilter
+{
+ // Implementations for DataEntryFilter.
+
+ public boolean accepts(DataEntry dataEntry)
+ {
+ return dataEntry != null && dataEntry.isDirectory();
+ }
+} \ No newline at end of file
diff --git a/src/proguard/io/DataEntryFilter.java b/src/proguard/io/DataEntryFilter.java
new file mode 100644
index 0000000..ddcd0be
--- /dev/null
+++ b/src/proguard/io/DataEntryFilter.java
@@ -0,0 +1,38 @@
+/*
+ * 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.io;
+
+
+/**
+ * This interface provides a method to filter data entries.
+ *
+ * @author Eric Lafortune
+ */
+public interface DataEntryFilter
+{
+ /**
+ * Checks whether the filter accepts the given data entry.
+ * @param dataEntry the data entry to filter.
+ * @return a boolean indicating whether the filter accepts the given data
+ * entry.
+ */
+ public boolean accepts(DataEntry dataEntry);
+}
diff --git a/src/proguard/io/DataEntryNameFilter.java b/src/proguard/io/DataEntryNameFilter.java
new file mode 100644
index 0000000..d6afd2e
--- /dev/null
+++ b/src/proguard/io/DataEntryNameFilter.java
@@ -0,0 +1,54 @@
+/*
+ * 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.io;
+
+import proguard.util.StringMatcher;
+
+/**
+ * This DataEntryFilter filters data entries based on whether their names match
+ * a given StringMatcher.
+ *
+ * @author Eric Lafortune
+ */
+public class DataEntryNameFilter
+implements DataEntryFilter
+{
+ private final StringMatcher stringMatcher;
+
+
+ /**
+ * Creates a new DataEntryNameFilter.
+ * @param stringMatcher the string matcher that will be applied to the names
+ * of the filtered data entries.
+ */
+ public DataEntryNameFilter(StringMatcher stringMatcher)
+ {
+ this.stringMatcher = stringMatcher;
+ }
+
+
+ // Implementations for DataEntryFilter.
+
+ public boolean accepts(DataEntry dataEntry)
+ {
+ return dataEntry != null && stringMatcher.matches(dataEntry.getName());
+ }
+}
diff --git a/src/proguard/io/DataEntryObfuscator.java b/src/proguard/io/DataEntryObfuscator.java
new file mode 100644
index 0000000..aa63af1
--- /dev/null
+++ b/src/proguard/io/DataEntryObfuscator.java
@@ -0,0 +1,131 @@
+/*
+ * 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.io;
+
+import proguard.classfile.*;
+import proguard.classfile.util.ClassUtil;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * This DataEntryReader delegates to another DataEntryReader, renaming the
+ * data entries based on the renamed classes in the given ClassPool.
+ *
+ * @author Eric Lafortune
+ */
+public class DataEntryObfuscator implements DataEntryReader
+{
+ private final ClassPool classPool;
+ private final Map packagePrefixMap;
+ private final DataEntryReader dataEntryReader;
+
+
+ /**
+ * Creates a new DataEntryObfuscator.
+ * @param classPool the class pool that maps from old names to new
+ * names.
+ * @param packagePrefixMap the map from old package prefixes to new package
+ * prefixes.
+ * @param dataEntryReader the DataEntryReader to which calls will be
+ * delegated.
+ */
+ public DataEntryObfuscator(ClassPool classPool,
+ Map packagePrefixMap,
+ DataEntryReader dataEntryReader)
+ {
+ this.classPool = classPool;
+ this.packagePrefixMap = packagePrefixMap;
+ this.dataEntryReader = dataEntryReader;
+ }
+
+
+ // Implementations for DataEntryReader.
+
+ public void read(DataEntry dataEntry) throws IOException
+ {
+ // Delegate to the actual data entry reader.
+ dataEntryReader.read(renamedDataEntry(dataEntry));
+ }
+
+
+ /**
+ * Create a renamed data entry, if possible.
+ */
+ private DataEntry renamedDataEntry(DataEntry dataEntry)
+ {
+ String dataEntryName = dataEntry.getName();
+
+ // Try to find a corresponding class name by removing increasingly
+ // long suffixes,
+ for (int suffixIndex = dataEntryName.length() - 1;
+ suffixIndex > 0;
+ suffixIndex--)
+ {
+ char c = dataEntryName.charAt(suffixIndex);
+ if (!Character.isLetterOrDigit(c))
+ {
+ // Chop off the suffix.
+ String className = dataEntryName.substring(0, suffixIndex);
+
+ // Did we get to the package separator?
+ if (c == ClassConstants.INTERNAL_PACKAGE_SEPARATOR)
+ {
+ break;
+ }
+
+ // Is there a class corresponding to the data entry?
+ Clazz clazz = classPool.getClass(className);
+ if (clazz != null)
+ {
+ // Did the class get a new name?
+ String newClassName = clazz.getName();
+ if (!className.equals(newClassName))
+ {
+ // Return a renamed data entry.
+ String newDataEntryName =
+ newClassName + dataEntryName.substring(suffixIndex);
+
+ return new RenamedDataEntry(dataEntry, newDataEntryName);
+ }
+
+ // Otherwise stop looking.
+ break;
+ }
+ }
+ }
+
+ // Did the package get a new name?
+ String packagePrefix = ClassUtil.internalPackagePrefix(dataEntryName);
+ String newPackagePrefix = (String)packagePrefixMap.get(packagePrefix);
+ if (newPackagePrefix != null &&
+ !packagePrefix.equals(newPackagePrefix))
+ {
+ // Return a renamed data entry.
+ String newDataEntryName =
+ newPackagePrefix + dataEntryName.substring(packagePrefix.length());
+
+ return new RenamedDataEntry(dataEntry, newDataEntryName);
+ }
+
+ return dataEntry;
+ }
+}
diff --git a/src/proguard/io/DataEntryParentFilter.java b/src/proguard/io/DataEntryParentFilter.java
new file mode 100644
index 0000000..fbeac4f
--- /dev/null
+++ b/src/proguard/io/DataEntryParentFilter.java
@@ -0,0 +1,51 @@
+/*
+ * 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.io;
+
+/**
+ * This DataEntryFilter delegates filtering to a DataEntryFilter for its parent.
+ *
+ * @author Eric Lafortune
+ */
+public class DataEntryParentFilter
+implements DataEntryFilter
+{
+ private final DataEntryFilter dataEntryFilter;
+
+
+ /**
+ * Creates a new ParentFilter.
+ * @param dataEntryFilter the filter that will be applied to the data
+ * entry's parent.
+ */
+ public DataEntryParentFilter(DataEntryFilter dataEntryFilter)
+ {
+ this.dataEntryFilter = dataEntryFilter;
+ }
+
+
+ // Implementations for DataEntryFilter.
+
+ public boolean accepts(DataEntry dataEntry)
+ {
+ return dataEntry != null && dataEntryFilter.accepts(dataEntry.getParent());
+ }
+}
diff --git a/src/proguard/io/DataEntryPump.java b/src/proguard/io/DataEntryPump.java
new file mode 100644
index 0000000..bfe22a3
--- /dev/null
+++ b/src/proguard/io/DataEntryPump.java
@@ -0,0 +1,43 @@
+/*
+ * 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.io;
+
+import java.io.IOException;
+
+
+/**
+ * This interface provides a method to pump data entries. The implementation
+ * determines the source and the type of the data entries. Typical examples
+ * are zip entries coming from a zip file of file entries coming from a
+ * directory structure. The reader can for instance collect the classes,
+ * or copy the resource files that are presented.
+ *
+ * @author Eric Lafortune
+ */
+public interface DataEntryPump
+{
+ /**
+ * Applies the given DataEntryReader to all data entries that the
+ * implementation can provide.
+ */
+ public void pumpDataEntries(DataEntryReader dataEntryReader)
+ throws IOException;
+}
diff --git a/src/proguard/io/DataEntryReader.java b/src/proguard/io/DataEntryReader.java
new file mode 100644
index 0000000..e77f7bf
--- /dev/null
+++ b/src/proguard/io/DataEntryReader.java
@@ -0,0 +1,38 @@
+/*
+ * 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.io;
+
+import java.io.IOException;
+
+
+/**
+ * This interface provides methods for reading data entries. The implementation
+ * determines what to do with the read data, if anything.
+ *
+ * @author Eric Lafortune
+ */
+public interface DataEntryReader
+{
+ /**
+ * Reads the given data entry.
+ */
+ public void read(DataEntry dataEntry) throws IOException;
+}
diff --git a/src/proguard/io/DataEntryRenamer.java b/src/proguard/io/DataEntryRenamer.java
new file mode 100644
index 0000000..45c61ee
--- /dev/null
+++ b/src/proguard/io/DataEntryRenamer.java
@@ -0,0 +1,104 @@
+/*
+ * 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.io;
+
+import proguard.classfile.ClassConstants;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * This DataEntryReader delegates to another DataEntryReader, renaming the
+ * data entries based on the given map. Entries whose name does not appear
+ * in the map may be passed to an alternative DataEntryReader.
+ *
+ * @author Eric Lafortune
+ */
+public class DataEntryRenamer implements DataEntryReader
+{
+ private final Map nameMap;
+ private final DataEntryReader renamedDataEntryReader;
+ private final DataEntryReader missingDataEntryReader;
+
+
+ /**
+ * Creates a new DataEntryRenamer.
+ * @param nameMap the map from old names to new names.
+ * @param renamedDataEntryReader the DataEntryReader to which renamed data
+ * entries will be passed.
+ */
+ public DataEntryRenamer(Map nameMap,
+ DataEntryReader renamedDataEntryReader)
+ {
+ this(nameMap, renamedDataEntryReader, null);
+ }
+
+
+ /**
+ * Creates a new DataEntryRenamer.
+ * @param nameMap the map from old names to new names.
+ * @param renamedDataEntryReader the DataEntryReader to which renamed data
+ * entries will be passed.
+ * @param missingDataEntryReader the optional DataEntryReader to which data
+ * entries that can't be renamed will be
+ * passed.
+ */
+ public DataEntryRenamer(Map nameMap,
+ DataEntryReader renamedDataEntryReader,
+ DataEntryReader missingDataEntryReader)
+ {
+ this.nameMap = nameMap;
+ this.renamedDataEntryReader = renamedDataEntryReader;
+ this.missingDataEntryReader = missingDataEntryReader;
+ }
+
+
+ // Implementations for DataEntryReader.
+
+ public void read(DataEntry dataEntry) throws IOException
+ {
+ String name = dataEntry.getName();
+
+ // Add a directory separator if necessary.
+ if (dataEntry.isDirectory() &&
+ name.length() > 0)
+ {
+ name += ClassConstants.INTERNAL_PACKAGE_SEPARATOR;
+ }
+
+ String newName = (String)nameMap.get(name);
+ if (newName != null)
+ {
+ // Add remove the directory separator if necessary.
+ if (dataEntry.isDirectory() &&
+ newName.length() > 0)
+ {
+ newName = newName.substring(0, newName.length() - 1);
+ }
+
+ renamedDataEntryReader.read(new RenamedDataEntry(dataEntry, newName));
+ }
+ else if (missingDataEntryReader != null)
+ {
+ missingDataEntryReader.read(dataEntry);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/proguard/io/DataEntryRewriter.java b/src/proguard/io/DataEntryRewriter.java
new file mode 100644
index 0000000..eefced4
--- /dev/null
+++ b/src/proguard/io/DataEntryRewriter.java
@@ -0,0 +1,148 @@
+/*
+ * 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.io;
+
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * This DataEntryReader writes the resource data entries that it reads to a
+ * given DataEntryWriter, updating their contents based on the renamed classes
+ * in the given ClassPool.
+ *
+ * @author Eric Lafortune
+ */
+public class DataEntryRewriter extends DataEntryCopier
+{
+ private final ClassPool classPool;
+
+
+ /**
+ * Creates a new DataEntryRewriter.
+ */
+ public DataEntryRewriter(ClassPool classPool,
+ DataEntryWriter dataEntryWriter)
+ {
+ super(dataEntryWriter);
+
+ this.classPool = classPool;
+ }
+
+
+ // Implementations for DataEntryCopier.
+
+ protected void copyData(InputStream inputStream,
+ OutputStream outputStream)
+ throws IOException
+ {
+ Reader reader = new BufferedReader(new InputStreamReader(inputStream));
+ Writer writer = new BufferedWriter(new OutputStreamWriter(outputStream));
+
+ copyData(reader, writer);
+
+ writer.flush();
+ outputStream.flush();
+ }
+
+
+ /**
+ * Copies all data that it can read from the given reader to the given
+ * writer.
+ */
+ protected void copyData(Reader reader,
+ Writer writer)
+ throws IOException
+ {
+ StringBuffer word = new StringBuffer();
+
+ while (true)
+ {
+ int i = reader.read();
+ if (i < 0)
+ {
+ break;
+ }
+
+ // Is the character part of a word?
+ char c = (char)i;
+ if (Character.isJavaIdentifierPart(c) ||
+ c == '.' ||
+ c == '-')
+ {
+ // Collect the characters in this word.
+ word.append(c);
+ }
+ else
+ {
+ // Write out the updated word, if any.
+ writeUpdatedWord(writer, word.toString());
+ word.setLength(0);
+
+ // Write out the character that terminated it.
+ writer.write(c);
+ }
+ }
+
+ // Write out the final word.
+ writeUpdatedWord(writer, word.toString());
+ }
+
+
+ // Small utility methods.
+
+ /**
+ * Writes the given word to the given writer, after having adapted it,
+ * based on the renamed class names.
+ */
+ private void writeUpdatedWord(Writer writer, String word)
+ throws IOException
+ {
+ if (word.length() > 0)
+ {
+ String newWord = word;
+
+ boolean containsDots = word.indexOf('.') >= 0;
+
+ // Replace dots by forward slashes.
+ String className = containsDots ?
+ word.replace('.', ClassConstants.INTERNAL_PACKAGE_SEPARATOR) :
+ word;
+
+ // Find the class corrsponding to the word.
+ Clazz clazz = classPool.getClass(className);
+ if (clazz != null)
+ {
+ // Update the word if necessary.
+ String newClassName = clazz.getName();
+ if (!className.equals(newClassName))
+ {
+ // Replace forward slashes by dots.
+ newWord = containsDots ?
+ newClassName.replace(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, '.') :
+ newClassName;
+ }
+ }
+
+ writer.write(newWord);
+ }
+ }
+}
diff --git a/src/proguard/io/DataEntryWriter.java b/src/proguard/io/DataEntryWriter.java
new file mode 100644
index 0000000..9ecf79b
--- /dev/null
+++ b/src/proguard/io/DataEntryWriter.java
@@ -0,0 +1,73 @@
+/*
+ * 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.io;
+
+import java.io.*;
+
+
+/**
+ * This interface provides methods for writing data entries, such as ZIP entries
+ * or files. The implementation determines to which type of data entry the
+ * data will be written.
+ *
+ * @author Eric Lafortune
+ */
+public interface DataEntryWriter
+{
+ /**
+ * Creates a directory.
+ * @param dataEntry the data entry for which the directory is to be created.
+ * @return whether the directory has been created.
+ */
+ public boolean createDirectory(DataEntry dataEntry) throws IOException;
+
+
+ /**
+ * Returns an output stream for writing data. The caller must not close
+ * the output stream; closing the output stream is the responsibility of
+ * the implementation of this interface.
+ * @param dataEntry the data entry for which the output stream is to be created.
+ * @return the output stream. The stream may be <code>null</code> to indicate
+ * that the data entry should not be written.
+ */
+ public OutputStream getOutputStream(DataEntry dataEntry) throws IOException;
+
+
+ /**
+ * Returns an output stream for writing data. The caller must not close
+ * the output stream; closing the output stream is the responsibility of
+ * the implementation of this interface.
+ * @param dataEntry the data entry for which the output stream is to be created.
+ * @param finisher the optional finisher that will be called before this
+ * class closes the output stream (at some later point in
+ * time) that will be returned (now).
+ * @return the output stream. The stream may be <code>null</code> to indicate
+ * that the data entry should not be written.
+ */
+ public OutputStream getOutputStream(DataEntry dataEntry,
+ Finisher finisher) throws IOException;
+
+
+ /**
+ * Finishes writing all data entries.
+ */
+ public void close() throws IOException;
+}
diff --git a/src/proguard/io/DirectoryFilter.java b/src/proguard/io/DirectoryFilter.java
new file mode 100644
index 0000000..a45d1aa
--- /dev/null
+++ b/src/proguard/io/DirectoryFilter.java
@@ -0,0 +1,58 @@
+/*
+ * 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.io;
+
+import proguard.classfile.ClassConstants;
+import proguard.util.ExtensionMatcher;
+
+import java.io.IOException;
+
+
+/**
+ * This DataEntryReader delegates to one of two other DataEntryReader instances,
+ * depending on whether the data entry represents a directory or not.
+ *
+ * @author Eric Lafortune
+ */
+public class DirectoryFilter extends FilteredDataEntryReader
+{
+ /**
+ * Creates a new ClassFilter that delegates reading directories to the
+ * given reader.
+ */
+ public DirectoryFilter(DataEntryReader directoryReader)
+ {
+ this (directoryReader, null);
+ }
+
+
+ /**
+ * Creates a new ClassFilter that delegates to either of the two given
+ * readers.
+ */
+ public DirectoryFilter(DataEntryReader directoryReader,
+ DataEntryReader otherReader)
+ {
+ super(new DataEntryDirectoryFilter(),
+ directoryReader,
+ otherReader);
+ }
+} \ No newline at end of file
diff --git a/src/proguard/io/DirectoryPump.java b/src/proguard/io/DirectoryPump.java
new file mode 100644
index 0000000..cab2ff3
--- /dev/null
+++ b/src/proguard/io/DirectoryPump.java
@@ -0,0 +1,78 @@
+/*
+ * 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.io;
+
+import java.io.*;
+
+
+/**
+ * This class can read a given file or directory, recursively, applying a given
+ * DataEntryReader to all files it comes across.
+ *
+ * @author Eric Lafortune
+ */
+public class DirectoryPump implements DataEntryPump
+{
+ private final File directory;
+
+
+ public DirectoryPump(File directory)
+ {
+ this.directory = directory;
+ }
+
+
+ // Implementations for DataEntryPump.
+
+ public void pumpDataEntries(DataEntryReader dataEntryReader)
+ throws IOException
+ {
+ if (!directory.exists())
+ {
+ throw new IOException("No such file or directory");
+ }
+
+ readFiles(directory, dataEntryReader);
+ }
+
+
+ /**
+ * Reads the given subdirectory recursively, applying the given DataEntryReader
+ * to all files that are encountered.
+ */
+ private void readFiles(File file, DataEntryReader dataEntryReader)
+ throws IOException
+ {
+ // Pass the file data entry to the reader.
+ dataEntryReader.read(new FileDataEntry(directory, file));
+
+ if (file.isDirectory())
+ {
+ // Recurse into the subdirectory.
+ File[] files = file.listFiles();
+
+ for (int index = 0; index < files.length; index++)
+ {
+ readFiles(files[index], dataEntryReader);
+ }
+ }
+ }
+}
diff --git a/src/proguard/io/DirectoryWriter.java b/src/proguard/io/DirectoryWriter.java
new file mode 100644
index 0000000..c6605df
--- /dev/null
+++ b/src/proguard/io/DirectoryWriter.java
@@ -0,0 +1,165 @@
+/*
+ * 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.io;
+
+import proguard.classfile.ClassConstants;
+
+import java.io.*;
+
+
+/**
+ * This DataEntryWriter writes data entries to individual files in a given
+ * directory.
+ *
+ * @author Eric Lafortune
+ */
+public class DirectoryWriter implements DataEntryWriter
+{
+ private final File baseFile;
+ private final boolean isFile;
+
+ private File currentFile;
+ private OutputStream currentOutputStream;
+ private Finisher currentFinisher;
+
+
+ /**
+ * Creates a new DirectoryWriter.
+ * @param baseFile the base directory to which all files will be written.
+ */
+ public DirectoryWriter(File baseFile,
+ boolean isFile)
+ {
+ this.baseFile = baseFile;
+ this.isFile = isFile;
+ }
+
+
+ // Implementations for DataEntryWriter.
+
+ public boolean createDirectory(DataEntry dataEntry) throws IOException
+ {
+ // Should we close the current file?
+ if (!isFile &&
+ currentFile != null)
+ {
+ closeEntry();
+ }
+
+ File directory = getFile(dataEntry);
+ if (!directory.exists() &&
+ !directory.mkdirs())
+ {
+ throw new IOException("Can't create directory [" + directory.getPath() + "]");
+ }
+
+ return true;
+ }
+
+
+ public OutputStream getOutputStream(DataEntry dataEntry) throws IOException
+ {
+ return getOutputStream(dataEntry, null);
+ }
+
+
+ public OutputStream getOutputStream(DataEntry dataEntry,
+ Finisher finisher) throws IOException
+ {
+ File file = getFile(dataEntry);
+
+ // Should we close the current file?
+ if (!isFile &&
+ currentFile != null &&
+ !currentFile.equals(file))
+ {
+ closeEntry();
+ }
+
+ // Do we need a new stream?
+ if (currentOutputStream == null)
+ {
+ // Make sure the parent directories exist.
+ File parentDirectory = file.getParentFile();
+ if (parentDirectory != null &&
+ !parentDirectory.exists() &&
+ !parentDirectory.mkdirs())
+ {
+ throw new IOException("Can't create directory [" + parentDirectory.getPath() + "]");
+ }
+
+ // Open a new output stream for writing to the file.
+ currentOutputStream =
+ new BufferedOutputStream(
+ new FileOutputStream(file));
+
+ currentFinisher = finisher;
+ currentFile = file;
+ }
+
+ return currentOutputStream;
+ }
+
+
+ public void close() throws IOException
+ {
+ // Close the file stream, if any.
+ closeEntry();
+ }
+
+
+ // Small utility methods.
+
+ /**
+ * Returns the file for the given data entry.
+ */
+ private File getFile(DataEntry dataEntry)
+ {
+ // Use the specified file, or construct a new file.
+ return isFile ?
+ baseFile :
+ new File(baseFile,
+ dataEntry.getName().replace(ClassConstants.INTERNAL_PACKAGE_SEPARATOR,
+ File.separatorChar));
+ }
+
+
+ /**
+ * Closes the previous file, if any.
+ */
+ private void closeEntry() throws IOException
+ {
+ // Close the file stream, if any.
+ if (currentOutputStream != null)
+ {
+ // Let any finisher finish up first.
+ if (currentFinisher != null)
+ {
+ currentFinisher.finish();
+ currentFinisher = null;
+ }
+
+ currentOutputStream.close();
+ currentOutputStream = null;
+ currentFile = null;
+ }
+ }
+}
diff --git a/src/proguard/io/FileDataEntry.java b/src/proguard/io/FileDataEntry.java
new file mode 100644
index 0000000..d0449ee
--- /dev/null
+++ b/src/proguard/io/FileDataEntry.java
@@ -0,0 +1,96 @@
+/*
+ * 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.io;
+
+import proguard.classfile.ClassConstants;
+
+import java.io.*;
+
+/**
+ * This <code>DataEntry</code> represents a file.
+ *
+ * @author Eric Lafortune
+ */
+public class FileDataEntry implements DataEntry
+{
+ private final File directory;
+ private final File file;
+ private InputStream inputStream;
+
+
+ public FileDataEntry(File directory,
+ File file)
+ {
+ this.directory = directory;
+ this.file = file;
+ }
+
+
+ // Implementations for DataEntry.
+
+ public String getName()
+ {
+ // Chop the directory name from the file name and get the right separators.
+ return file.equals(directory) ?
+ file.getName() :
+ file.getPath()
+ .substring(directory.getPath().length() + File.separator.length())
+ .replace(File.separatorChar, ClassConstants.INTERNAL_PACKAGE_SEPARATOR);
+ }
+
+
+ public boolean isDirectory()
+ {
+ return file.isDirectory();
+ }
+
+
+ public InputStream getInputStream() throws IOException
+ {
+ if (inputStream == null)
+ {
+ inputStream = new BufferedInputStream(new FileInputStream(file));
+ }
+
+ return inputStream;
+ }
+
+
+ public void closeInputStream() throws IOException
+ {
+ inputStream.close();
+ inputStream = null;
+ }
+
+
+ public DataEntry getParent()
+ {
+ return null;
+ }
+
+
+ // Implementations for Object.
+
+ public String toString()
+ {
+ return getName();
+ }
+}
diff --git a/src/proguard/io/FilteredDataEntryReader.java b/src/proguard/io/FilteredDataEntryReader.java
new file mode 100644
index 0000000..11da0d4
--- /dev/null
+++ b/src/proguard/io/FilteredDataEntryReader.java
@@ -0,0 +1,90 @@
+/*
+ * 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.io;
+
+import java.io.IOException;
+
+
+/**
+ * This DataEntryReader delegates to one of two other DataEntryReader instances,
+ * depending on whether the data entry passes through a given data entry filter
+ * or not.
+ *
+ * @author Eric Lafortune
+ */
+public class FilteredDataEntryReader implements DataEntryReader
+{
+ private final DataEntryFilter dataEntryFilter;
+ private final DataEntryReader acceptedDataEntryReader;
+ private final DataEntryReader rejectedDataEntryReader;
+
+
+ /**
+ * Creates a new FilteredDataEntryReader with only a reader for accepted
+ * data entries.
+ * @param dataEntryFilter the data entry filter.
+ * @param acceptedDataEntryReader the DataEntryReader to which the reading
+ * will be delegated if the filter accepts
+ * the data entry. May be <code>null</code>.
+ */
+ public FilteredDataEntryReader(DataEntryFilter dataEntryFilter,
+ DataEntryReader acceptedDataEntryReader)
+ {
+ this(dataEntryFilter, acceptedDataEntryReader, null);
+ }
+
+
+ /**
+ * Creates a new FilteredDataEntryReader.
+ * @param dataEntryFilter the data entry filter.
+ * @param acceptedDataEntryReader the DataEntryReader to which the reading
+ * will be delegated if the filter accepts
+ * the data entry. May be <code>null</code>.
+ * @param rejectedDataEntryReader the DataEntryReader to which the reading
+ * will be delegated if the filter does not
+ * accept the data entry. May be
+ * <code>null</code>.
+ */
+ public FilteredDataEntryReader(DataEntryFilter dataEntryFilter,
+ DataEntryReader acceptedDataEntryReader,
+ DataEntryReader rejectedDataEntryReader)
+ {
+ this.dataEntryFilter = dataEntryFilter;
+ this.acceptedDataEntryReader = acceptedDataEntryReader;
+ this.rejectedDataEntryReader = rejectedDataEntryReader;
+ }
+
+
+ // Implementations for DataEntryReader.
+
+ public void read(DataEntry dataEntry)
+ throws IOException
+ {
+ DataEntryReader dataEntryReader = dataEntryFilter.accepts(dataEntry) ?
+ acceptedDataEntryReader :
+ rejectedDataEntryReader;
+
+ if (dataEntryReader != null)
+ {
+ dataEntryReader.read(dataEntry);
+ }
+ }
+}
diff --git a/src/proguard/io/FilteredDataEntryWriter.java b/src/proguard/io/FilteredDataEntryWriter.java
new file mode 100644
index 0000000..40a8c64
--- /dev/null
+++ b/src/proguard/io/FilteredDataEntryWriter.java
@@ -0,0 +1,125 @@
+/*
+ * 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.io;
+
+import java.io.*;
+
+/**
+ * This DataEntryWriter delegates to one of two other DataEntryWriter instances,
+ * depending on whether the data entry passes through a given data entry filter
+ * or not.
+ *
+ * @author Eric Lafortune
+ */
+public class FilteredDataEntryWriter implements DataEntryWriter
+{
+ private final DataEntryFilter dataEntryFilter;
+ private DataEntryWriter acceptedDataEntryWriter;
+ private DataEntryWriter rejectedDataEntryWriter;
+
+
+ /**
+ * Creates a new FilteredDataEntryWriter with only a writer for accepted
+ * data entries.
+ * @param dataEntryFilter the data entry filter.
+ * @param acceptedDataEntryWriter the DataEntryWriter to which the writing
+ * will be delegated if the filter accepts
+ * the data entry. May be <code>null</code>.
+ */
+ public FilteredDataEntryWriter(DataEntryFilter dataEntryFilter,
+ DataEntryWriter acceptedDataEntryWriter)
+ {
+ this(dataEntryFilter, acceptedDataEntryWriter, null);
+ }
+
+
+ /**
+ * Creates a new FilteredDataEntryWriter.
+ * @param dataEntryFilter the data entry filter.
+ * @param acceptedDataEntryWriter the DataEntryWriter to which the writing
+ * will be delegated if the filter accepts
+ * the data entry. May be <code>null</code>.
+ * @param rejectedDataEntryWriter the DataEntryWriter to which the writing
+ * will be delegated if the filter does not
+ * accept the data entry. May be
+ * <code>null</code>.
+ */
+ public FilteredDataEntryWriter(DataEntryFilter dataEntryFilter,
+ DataEntryWriter acceptedDataEntryWriter,
+ DataEntryWriter rejectedDataEntryWriter)
+ {
+ this.dataEntryFilter = dataEntryFilter;
+ this.acceptedDataEntryWriter = acceptedDataEntryWriter;
+ this.rejectedDataEntryWriter = rejectedDataEntryWriter;
+ }
+
+
+ // Implementations for DataEntryWriter.
+
+ public boolean createDirectory(DataEntry dataEntry) throws IOException
+ {
+ // Get the right data entry writer.
+ DataEntryWriter dataEntryWriter = dataEntryFilter.accepts(dataEntry) ?
+ acceptedDataEntryWriter :
+ rejectedDataEntryWriter;
+
+ // Delegate to it, if it's not null.
+ return dataEntryWriter != null &&
+ dataEntryWriter.createDirectory(dataEntry);
+ }
+
+
+ public OutputStream getOutputStream(DataEntry dataEntry) throws IOException
+ {
+ return getOutputStream(dataEntry, null);
+ }
+
+
+ public OutputStream getOutputStream(DataEntry dataEntry,
+ Finisher finisher) throws IOException
+ {
+ // Get the right data entry writer.
+ DataEntryWriter dataEntryWriter = dataEntryFilter.accepts(dataEntry) ?
+ acceptedDataEntryWriter :
+ rejectedDataEntryWriter;
+
+ // Delegate to it, if it's not null.
+ return dataEntryWriter != null ?
+ dataEntryWriter.getOutputStream(dataEntry, finisher) :
+ null;
+ }
+
+
+ public void close() throws IOException
+ {
+ if (acceptedDataEntryWriter != null)
+ {
+ acceptedDataEntryWriter.close();
+ acceptedDataEntryWriter = null;
+ }
+
+ if (rejectedDataEntryWriter != null)
+ {
+ rejectedDataEntryWriter.close();
+ rejectedDataEntryWriter = null;
+ }
+ }
+}
diff --git a/src/proguard/io/Finisher.java b/src/proguard/io/Finisher.java
new file mode 100644
index 0000000..8c4cc1e
--- /dev/null
+++ b/src/proguard/io/Finisher.java
@@ -0,0 +1,37 @@
+/*
+ * 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.io;
+
+import java.io.IOException;
+
+/**
+ * This interface specifies a listener that is called to finish an output stream
+ * before it is closed.
+ *
+ * @author Eric Lafortune
+ */
+public interface Finisher
+{
+ /**
+ * Finishes an output stream right before it is closed.
+ */
+ public void finish() throws IOException;
+}
diff --git a/src/proguard/io/JarReader.java b/src/proguard/io/JarReader.java
new file mode 100644
index 0000000..f96b4aa
--- /dev/null
+++ b/src/proguard/io/JarReader.java
@@ -0,0 +1,75 @@
+/*
+ * 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.io;
+
+import java.io.IOException;
+import java.util.zip.*;
+
+/**
+ * This DataEntryReader lets a given DataEntryReader read all data entries of
+ * the read jar/war/zip data entries.
+ *
+ * @author Eric Lafortune
+ */
+public class JarReader implements DataEntryReader
+{
+ private final DataEntryReader dataEntryReader;
+
+
+ /**
+ * Creates a new JarReader.
+ */
+ public JarReader(DataEntryReader dataEntryReader)
+ {
+ this.dataEntryReader = dataEntryReader;
+ }
+
+
+ // Implementation for DataEntryReader.
+
+ public void read(DataEntry dataEntry) throws IOException
+ {
+ ZipInputStream zipInputStream = new ZipInputStream(dataEntry.getInputStream());
+
+ try
+ {
+ // Get all entries from the input jar.
+ while (true)
+ {
+ // Can we get another entry?
+ ZipEntry zipEntry = zipInputStream.getNextEntry();
+ if (zipEntry == null)
+ {
+ break;
+ }
+
+ // Delegate the actual reading to the data entry reader.
+ dataEntryReader.read(new ZipDataEntry(dataEntry,
+ zipEntry,
+ zipInputStream));
+ }
+ }
+ finally
+ {
+ dataEntry.closeInputStream();
+ }
+ }
+}
diff --git a/src/proguard/io/JarWriter.java b/src/proguard/io/JarWriter.java
new file mode 100644
index 0000000..3e40cdf
--- /dev/null
+++ b/src/proguard/io/JarWriter.java
@@ -0,0 +1,235 @@
+/*
+ * 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.io;
+
+import proguard.classfile.ClassConstants;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+import java.util.zip.*;
+
+
+/**
+ * This DataEntryWriter sends data entries to a given jar/zip file.
+ * The manifest and comment properties can optionally be set.
+ *
+ * @author Eric Lafortune
+ */
+public class JarWriter implements DataEntryWriter, Finisher
+{
+ private final DataEntryWriter dataEntryWriter;
+ private final Manifest manifest;
+ private final String comment;
+
+ private OutputStream currentParentOutputStream;
+ private ZipOutputStream currentJarOutputStream;
+ private Finisher currentFinisher;
+ private DataEntry currentDataEntry;
+
+ // The names of the jar entries that are already in the jar.
+ private final Set jarEntryNames = new HashSet();
+
+
+ /**
+ * Creates a new JarWriter without manifest or comment.
+ */
+ public JarWriter(DataEntryWriter dataEntryWriter)
+ {
+ this(dataEntryWriter, null, null);
+ }
+
+
+ /**
+ * Creates a new JarWriter.
+ */
+ public JarWriter(DataEntryWriter dataEntryWriter,
+ Manifest manifest,
+ String comment)
+ {
+ this.dataEntryWriter = dataEntryWriter;
+ this.manifest = manifest;
+ this.comment = comment;
+ }
+
+
+ // Implementations for DataEntryWriter.
+
+ public boolean createDirectory(DataEntry dataEntry) throws IOException
+ {
+ //Make sure we can start with a new entry.
+ if (!prepareEntry(dataEntry))
+ {
+ return false;
+ }
+
+ // Close the previous ZIP entry, if any.
+ closeEntry();
+
+ // Get the directory entry name.
+ String name = dataEntry.getName() + ClassConstants.INTERNAL_PACKAGE_SEPARATOR;
+
+ // We have to check if the name is already used, because
+ // ZipOutputStream doesn't handle this case properly (it throws
+ // an exception which can be caught, but the ZipDataEntry is
+ // remembered anyway).
+ if (jarEntryNames.add(name))
+ {
+ // Create a new directory entry.
+ currentJarOutputStream.putNextEntry(new ZipEntry(name));
+ currentJarOutputStream.closeEntry();
+ }
+
+ // Clear the finisher.
+ currentFinisher = null;
+ currentDataEntry = null;
+
+ return true;
+ }
+
+
+ public OutputStream getOutputStream(DataEntry dataEntry) throws IOException
+ {
+ return getOutputStream(dataEntry, null);
+ }
+
+
+ public OutputStream getOutputStream(DataEntry dataEntry,
+ Finisher finisher) throws IOException
+ {
+ //Make sure we can start with a new entry.
+ if (!prepareEntry(dataEntry))
+ {
+ return null;
+ }
+
+ // Do we need a new entry?
+ if (!dataEntry.equals(currentDataEntry))
+ {
+ // Close the previous ZIP entry, if any.
+ closeEntry();
+
+ // Get the entry name.
+ String name = dataEntry.getName();
+
+ // We have to check if the name is already used, because
+ // ZipOutputStream doesn't handle this case properly (it throws
+ // an exception which can be caught, but the ZipDataEntry is
+ // remembered anyway).
+ if (!jarEntryNames.add(name))
+ {
+ throw new IOException("Duplicate zip entry ["+dataEntry+"]");
+ }
+
+ // Create a new entry.
+ currentJarOutputStream.putNextEntry(new ZipEntry(name));
+
+ // Set up the finisher for the entry.
+ currentFinisher = finisher;
+ currentDataEntry = dataEntry;
+ }
+
+ return currentJarOutputStream;
+ }
+
+
+ public void finish() throws IOException
+ {
+ // Finish the entire ZIP stream, if any.
+ if (currentJarOutputStream != null)
+ {
+ // Close the previous ZIP entry, if any.
+ closeEntry();
+
+ // Finish the entire ZIP stream.
+ currentJarOutputStream.finish();
+ currentJarOutputStream = null;
+ currentParentOutputStream = null;
+ jarEntryNames.clear();
+ }
+ }
+
+
+ public void close() throws IOException
+ {
+ // Close the parent stream.
+ dataEntryWriter.close();
+ }
+
+
+ // Small utility methods.
+
+ /**
+ * Makes sure the current output stream is set up for the given entry.
+ */
+ private boolean prepareEntry(DataEntry dataEntry) throws IOException
+ {
+ // Get the parent stream, new or exisiting.
+ // This may finish our own jar output stream.
+ OutputStream parentOutputStream =
+ dataEntryWriter.getOutputStream(dataEntry.getParent(), this);
+
+ // Did we get a stream?
+ if (parentOutputStream == null)
+ {
+ return false;
+ }
+
+ // Do we need a new stream?
+ if (currentParentOutputStream == null)
+ {
+ currentParentOutputStream = parentOutputStream;
+
+ // Create a new jar stream, with a manifest, if set.
+ currentJarOutputStream = manifest != null ?
+ new JarOutputStream(parentOutputStream, manifest) :
+ new ZipOutputStream(parentOutputStream);
+
+ // Add a comment, if set.
+ if (comment != null)
+ {
+ currentJarOutputStream.setComment(comment);
+ }
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Closes the previous ZIP entry, if any.
+ */
+ private void closeEntry() throws IOException
+ {
+ if (currentDataEntry != null)
+ {
+ // Let any finisher finish up first.
+ if (currentFinisher != null)
+ {
+ currentFinisher.finish();
+ currentFinisher = null;
+ }
+
+ currentJarOutputStream.closeEntry();
+ currentDataEntry = null;
+ }
+ }
+}
diff --git a/src/proguard/io/ManifestRewriter.java b/src/proguard/io/ManifestRewriter.java
new file mode 100644
index 0000000..f10307e
--- /dev/null
+++ b/src/proguard/io/ManifestRewriter.java
@@ -0,0 +1,216 @@
+/*
+ * 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.io;
+
+import proguard.classfile.*;
+
+import java.io.*;
+
+/**
+ * This DataEntryReader writes the manifest data entries that it reads to a
+ * given DataEntryWriter, updating their contents based on the renamed classes
+ * in the given ClassPool.
+ *
+ * @author Eric Lafortune
+ */
+public class ManifestRewriter extends DataEntryRewriter
+{
+ /**
+ * Creates a new ManifestRewriter.
+ */
+ public ManifestRewriter(ClassPool classPool,
+ DataEntryWriter dataEntryWriter)
+ {
+ super(classPool, dataEntryWriter);
+ }
+
+
+ // Implementations for DataEntryRewriter.
+
+ protected void copyData(Reader reader,
+ Writer writer)
+ throws IOException
+ {
+ super.copyData(new SplitLineReader(reader),
+ new SplitLineWriter(writer));
+ }
+
+
+ /**
+ * This Reader reads manifest files, joining any split lines.
+ */
+ private static class SplitLineReader extends FilterReader
+ {
+ private char[] buffer = new char[2];
+ private int bufferIndex = 0;
+ private int bufferSize = 0;
+
+
+ public SplitLineReader(Reader reader)
+ {
+ super(reader);
+ }
+
+
+ // Implementations for Reader.
+
+ public int read() throws IOException
+ {
+ while (true)
+ {
+ if (bufferIndex < bufferSize)
+ {
+ return buffer[bufferIndex++];
+ }
+
+ // Read the first character.
+ int c1 = super.read();
+ if (c1 != '\n' && c1 != '\r')
+ {
+ return c1;
+ }
+
+ bufferIndex = 0;
+ bufferSize = 0;
+ buffer[bufferSize++] = '\n';
+
+ // Read the second character.
+ int c2 = super.read();
+ if (c2 == ' ')
+ {
+ bufferSize = 0;
+ continue;
+ }
+
+ if (c1 != '\r' || c2 != '\n')
+ {
+ buffer[bufferSize++] = (char)c2;
+ continue;
+ }
+
+ // Read the third character.
+ int c3 = super.read();
+ if (c3 == ' ')
+ {
+ bufferSize = 0;
+ continue;
+ }
+
+ buffer[bufferSize++] = (char)c3;
+ }
+ }
+
+
+ public int read(char[] cbuf, int off, int len) throws IOException
+ {
+ // Delegate to reading a single character at a time.
+ int count = 0;
+ while (count < len)
+ {
+ int c = read();
+ if (c == -1)
+ {
+ break;
+ }
+
+ cbuf[off + count++] = (char)c;
+ }
+
+ return count;
+ }
+
+
+ public long skip(long n) throws IOException
+ {
+ // Delegate to reading a single character at a time.
+ int count = 0;
+ while (count < n)
+ {
+ int c = read();
+ if (c == -1)
+ {
+ break;
+ }
+
+ count++;
+ }
+
+ return count;
+ }
+ }
+
+
+ /**
+ * This Writer writes manifest files, splitting any long lines.
+ */
+ private static class SplitLineWriter extends FilterWriter
+ {
+ private int counter = 0;
+
+
+ public SplitLineWriter(Writer writer)
+ {
+ super(writer);
+ }
+
+
+ // Implementations for Reader.
+
+ public void write(int c) throws IOException
+ {
+ // TODO: We should actually count the Utf-8 bytes, not the characters.
+ if (c == '\n')
+ {
+ // Reset the character count.
+ counter = 0;
+ }
+ else if (counter == 70)
+ {
+ // Insert are newline and space.
+ super.write('\n');
+ super.write(' ');
+
+ counter = 2;
+ }
+ else
+ {
+ counter++;
+ }
+
+ super.write(c);
+ }
+
+
+ public void write(char[] cbuf, int off, int len) throws IOException
+ {
+ for (int count = 0; count < len; count++)
+ {
+ write(cbuf[off + count]);
+ }
+ }
+
+
+ public void write(String str, int off, int len) throws IOException
+ {
+ write(str.toCharArray(), off, len);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/proguard/io/NameFilter.java b/src/proguard/io/NameFilter.java
new file mode 100644
index 0000000..2a9fbc3
--- /dev/null
+++ b/src/proguard/io/NameFilter.java
@@ -0,0 +1,83 @@
+/*
+ * 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.io;
+
+import proguard.util.*;
+
+import java.util.List;
+
+/**
+ * This DataEntryReader delegates to one of two other DataEntryReader instances,
+ * depending on the name of the data entry.
+ *
+ * @author Eric Lafortune
+ */
+public class NameFilter extends FilteredDataEntryReader
+{
+ /**
+ * Creates a new NameFilter that delegates to the given reader, depending
+ * on the given list of filters.
+ */
+ public NameFilter(String regularExpression,
+ DataEntryReader acceptedDataEntryReader)
+ {
+ this(regularExpression, acceptedDataEntryReader, null);
+ }
+
+
+ /**
+ * Creates a new NameFilter that delegates to either of the two given
+ * readers, depending on the given list of filters.
+ */
+ public NameFilter(String regularExpression,
+ DataEntryReader acceptedDataEntryReader,
+ DataEntryReader rejectedDataEntryReader)
+ {
+ super(new DataEntryNameFilter(new ListParser(new FileNameParser()).parse(regularExpression)),
+ acceptedDataEntryReader,
+ rejectedDataEntryReader);
+ }
+
+
+ /**
+ * Creates a new NameFilter that delegates to the given reader, depending
+ * on the given list of filters.
+ */
+ public NameFilter(List regularExpressions,
+ DataEntryReader acceptedDataEntryReader)
+ {
+ this(regularExpressions, acceptedDataEntryReader, null);
+ }
+
+
+ /**
+ * Creates a new NameFilter that delegates to either of the two given
+ * readers, depending on the given list of filters.
+ */
+ public NameFilter(List regularExpressions,
+ DataEntryReader acceptedDataEntryReader,
+ DataEntryReader rejectedDataEntryReader)
+ {
+ super(new DataEntryNameFilter(new ListParser(new FileNameParser()).parse(regularExpressions)),
+ acceptedDataEntryReader,
+ rejectedDataEntryReader);
+ }
+} \ No newline at end of file
diff --git a/src/proguard/io/ParentDataEntryWriter.java b/src/proguard/io/ParentDataEntryWriter.java
new file mode 100644
index 0000000..4f16d12
--- /dev/null
+++ b/src/proguard/io/ParentDataEntryWriter.java
@@ -0,0 +1,75 @@
+/*
+ * 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.io;
+
+import java.io.*;
+
+/**
+ * This DataEntryWriter lets another DataEntryWriter write the parent data
+ * entries.
+ *
+ * @author Eric Lafortune
+ */
+public class ParentDataEntryWriter implements DataEntryWriter
+{
+ private DataEntryWriter dataEntryWriter;
+
+
+ /**
+ * Creates a new ParentDataEntryWriter.
+ * @param dataEntryWriter the DataEntryWriter to which the writing will be
+ * delegated, passing the data entries' parents.
+ */
+ public ParentDataEntryWriter(DataEntryWriter dataEntryWriter)
+ {
+ this.dataEntryWriter = dataEntryWriter;
+ }
+
+
+ // Implementations for DataEntryWriter.
+
+
+ public boolean createDirectory(DataEntry dataEntry) throws IOException
+ {
+ return getOutputStream(dataEntry) != null;
+ }
+
+
+ public OutputStream getOutputStream(DataEntry dataEntry) throws IOException
+ {
+ return getOutputStream(dataEntry, null);
+ }
+
+
+ public OutputStream getOutputStream(DataEntry dataEntry,
+ Finisher finisher) throws IOException
+ {
+ return dataEntryWriter.getOutputStream(dataEntry.getParent(),
+ finisher);
+ }
+
+
+ public void close() throws IOException
+ {
+ dataEntryWriter.close();
+ dataEntryWriter = null;
+ }
+}
diff --git a/src/proguard/io/RenamedDataEntry.java b/src/proguard/io/RenamedDataEntry.java
new file mode 100644
index 0000000..ae2d267
--- /dev/null
+++ b/src/proguard/io/RenamedDataEntry.java
@@ -0,0 +1,83 @@
+/*
+ * 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.io;
+
+import java.io.*;
+
+/**
+ * This DataEntry wraps another data entry, returning a different name instead
+ * of the wrapped data entry's name.
+ *
+ * @author Eric Lafortune
+ */
+public class RenamedDataEntry implements DataEntry
+{
+ private final DataEntry dataEntry;
+ private final String name;
+
+
+ public RenamedDataEntry(DataEntry dataEntry,
+ String name)
+ {
+ this.dataEntry = dataEntry;
+ this.name = name;
+ }
+
+
+ // Implementations for DataEntry.
+
+ public String getName()
+ {
+ return name;
+ }
+
+
+ public boolean isDirectory()
+ {
+ return dataEntry.isDirectory();
+ }
+
+
+ public InputStream getInputStream() throws IOException
+ {
+ return dataEntry.getInputStream();
+ }
+
+
+ public void closeInputStream() throws IOException
+ {
+ dataEntry.closeInputStream();
+ }
+
+
+ public DataEntry getParent()
+ {
+ return dataEntry.getParent();
+ }
+
+
+ // Implementations for Object.
+
+ public String toString()
+ {
+ return name + " == " + dataEntry;
+ }
+}
diff --git a/src/proguard/io/ZipDataEntry.java b/src/proguard/io/ZipDataEntry.java
new file mode 100644
index 0000000..5779fd8
--- /dev/null
+++ b/src/proguard/io/ZipDataEntry.java
@@ -0,0 +1,98 @@
+/*
+ * 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.io;
+
+import proguard.classfile.ClassConstants;
+
+import java.io.*;
+import java.util.zip.*;
+
+/**
+ * This <code>DataEntry</code> represents a ZIP entry.
+ *
+ * @author Eric Lafortune
+ */
+public class ZipDataEntry implements DataEntry
+{
+ private final DataEntry parent;
+ private final ZipEntry zipEntry;
+ private ZipInputStream zipInputStream;
+
+
+ public ZipDataEntry(DataEntry parent,
+ ZipEntry zipEntry,
+ ZipInputStream zipInputStream)
+ {
+ this.parent = parent;
+ this.zipEntry = zipEntry;
+ this.zipInputStream = zipInputStream;
+ }
+
+
+ // Implementations for DataEntry.
+
+ public String getName()
+ {
+ // Get the right separators.
+ String name = zipEntry.getName()
+ .replace(File.separatorChar, ClassConstants.INTERNAL_PACKAGE_SEPARATOR);
+
+ // Chop the trailing directory slash, if any.
+ int length = name.length();
+ return length > 0 &&
+ name.charAt(length-1) == ClassConstants.INTERNAL_PACKAGE_SEPARATOR ?
+ name.substring(0, length -1) :
+ name;
+ }
+
+
+ public boolean isDirectory()
+ {
+ return zipEntry.isDirectory();
+ }
+
+
+ public InputStream getInputStream() throws IOException
+ {
+ return zipInputStream;
+ }
+
+
+ public void closeInputStream() throws IOException
+ {
+ zipInputStream.closeEntry();
+ zipInputStream = null;
+ }
+
+
+ public DataEntry getParent()
+ {
+ return parent;
+ }
+
+
+ // Implementations for Object.
+
+ public String toString()
+ {
+ return parent.toString() + ':' + getName();
+ }
+}
diff --git a/src/proguard/io/package.html b/src/proguard/io/package.html
new file mode 100644
index 0000000..4ad9f41
--- /dev/null
+++ b/src/proguard/io/package.html
@@ -0,0 +1,4 @@
+<body>
+This package contains classes to read and write files, optionally wrapped in
+jars, wars, ears, zips, directories,...
+</body>