diff options
Diffstat (limited to 'src/proguard/io/JarWriter.java')
-rw-r--r-- | src/proguard/io/JarWriter.java | 235 |
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; + } + } +} |