summaryrefslogtreecommitdiff
path: root/src/proguard/io/JarWriter.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/proguard/io/JarWriter.java')
-rw-r--r--src/proguard/io/JarWriter.java235
1 files changed, 235 insertions, 0 deletions
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;
+ }
+ }
+}