Mirror of the BLOAT repository https://www.cs.purdue.edu/homes/hosking/bloat/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
bloat/src/EDU/purdue/cs/bloat/file/JarFileCommitter.java

260 lines
7.0 KiB

/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.file;
import java.io.*;
import java.util.*;
import java.util.jar.*;
import java.util.zip.*;
import EDU.purdue.cs.bloat.reflect.*;
/**
* Does a lot of the same stuff as <tt>ClassFileLoader</tt>, but classes are
* committed to a JAR file instead of regular files.
*/
public class JarFileCommitter extends ClassFileLoader {
private FunkyJar funky;
/**
* Constructor.
*
* @param file
* <tt>File</tt> representing JAR file
* @param compress
* If <tt>true</tt>, contents of JAR file is compressed
* @param version
* Version for the JAR file's manifest
* @param author
* Author string from JAR file's manifest
*/
public JarFileCommitter(final File file, final boolean compress,
final String version, final String author) throws IOException {
funky = new FunkyJar(file, compress, version, author);
}
protected OutputStream outputStreamFor(final String name)
throws IOException {
funky.newEntry(name);
return funky;
}
public OutputStream outputStreamFor(final ClassInfo info)
throws IOException {
// This is funky. Recall that a JarOutputStream is also an output
// stream. So, we just return it. This is why we have to
// override the write, etc. methods.
// Make a new entry based on the class name
final String name = info.name() + ".class";
return outputStreamFor(name);
}
/**
* Signifies that we are finished with this <tt>JarFileCommitter</tt>.
*/
public void done() throws IOException {
funky.done();
}
}
/**
* We subclass JarOutputStream so that we can return an OutputStream to which a
* BLOATed class file will be written. In order to accomodate non-compression,
* we have to perform the checksum along the way. Bletch.
*/
class FunkyJar extends JarOutputStream {
private static final String MANIFEST = JarFile.MANIFEST_NAME;
private static final String MANIFEST_DIR = "META-INF/";
private static final CRC32 crc32 = new CRC32();
private boolean compress;
private JarEntry currEntry;
private Size size;
class Size {
long value = 0;
}
/**
* Constructor.
*/
public FunkyJar(final File file, boolean compress, final String version,
final String author) throws IOException {
super(new FileOutputStream(file));
this.compress = compress;
if (compress) {
this.setMethod(ZipOutputStream.DEFLATED);
} else {
this.setMethod(ZipOutputStream.STORED);
}
final Manifest manifest = new Manifest();
final Attributes global = manifest.getMainAttributes();
if (global.getValue(Attributes.Name.MANIFEST_VERSION) == null) {
global.put(Attributes.Name.MANIFEST_VERSION, version);
}
if (global.getValue(new Attributes.Name("Created-By")) == null) {
global.put(new Attributes.Name("Created-By"), author);
}
// Add directory for manifest
JarEntry entry = new JarEntry(FunkyJar.MANIFEST_DIR);
entry.setTime(System.currentTimeMillis());
entry.setSize(0); // Directories have size 0
entry.setCrc(0); // Checksum is 0
this.putNextEntry(entry);
// Add manifest
entry = new JarEntry(FunkyJar.MANIFEST);
entry.setTime(System.currentTimeMillis());
if (!compress) {
// Have to compute checksum ourselves. Use an ugly anonymous
// inner class. Influenced by CRC32OutputStream in
// sun.tools.jar.Main. Please don't sue me. I have no money.
// Maybe you could give me a job instead. Of course, then I'd
// have money and you would sue me. Hmm.
final Size size = new Size();
FunkyJar.crc32.reset();
manifest.write(new OutputStream() {
public void write(final int r) throws IOException {
FunkyJar.crc32.update(r);
size.value++;
}
public void write(final byte[] b) throws IOException {
FunkyJar.crc32.update(b, 0, b.length);
size.value += b.length;
}
public void write(final byte[] b, final int off, final int len)
throws IOException {
FunkyJar.crc32.update(b, off, len);
size.value += len - off;
}
});
entry.setSize(size.value);
entry.setCrc(FunkyJar.crc32.getValue());
}
this.putNextEntry(entry);
manifest.write(this); // Write the manifest to JAR file
this.closeEntry();
}
public void newEntry(final String name) throws IOException {
makeDirs(name);
currEntry = new JarEntry(name);
currEntry.setTime(System.currentTimeMillis());
if (compress) {
currEntry.setMethod(ZipEntry.DEFLATED);
} else {
currEntry.setMethod(ZipEntry.STORED);
}
this.putNextEntry(currEntry);
FunkyJar.crc32.reset();
this.size = new Size();
}
private Set dirs;
/**
* look at the path name specified by key and create zip entries for each
* directory level not already added.
*/
private void makeDirs(final String key) throws IOException {
if (dirs == null) {
dirs = new HashSet();
}
int idx = 0;
int last = 0;
while ((last = key.indexOf('/', idx + 1)) != -1) {
final String aDir = key.substring(0, last + 1);
if (!dirs.contains(aDir)) {
dirs.add(aDir);
this.putNextEntry(new ZipEntry(aDir));
this.closeEntry();
}
idx = last;
}
}
public void write(final int r) throws IOException {
super.write(r);
if (!compress && (size != null)) {
FunkyJar.crc32.update(r);
size.value++;
}
}
public void write(final byte[] b) throws IOException {
super.write(b);
if (!compress && (size != null)) {
FunkyJar.crc32.update(b, 0, b.length);
size.value += b.length;
}
}
public void write(final byte[] b, final int off, final int len)
throws IOException {
super.write(b, off, len);
if (!compress && (size != null)) {
FunkyJar.crc32.update(b, off, len);
size.value += len - off;
}
}
public void close() throws IOException {
// Okay, everythings is done. Set some values for the entry,
// cross your fingers, and run away.
if (!compress && (size != null)) {
currEntry.setSize(size.value);
currEntry.setCrc(FunkyJar.crc32.getValue());
}
currEntry = null;
size = null;
this.closeEntry();
// Note that we don't invoke the super class method.
}
/**
* Signifies that we are finished with this <tt>JarFileCommitter</tt>.
*/
public void done() throws IOException {
super.close();
}
}