bodewig 00/09/11 04:47:12 Modified: docs index.html src/main/org/apache/tools/ant/taskdefs defaults.properties Added: docs jlink.html src/main/org/apache/tools/ant/taskdefs/optional/jlink ClassNameReader.java JlinkTask.java jlink.java Log: New task . Many thanks to Patrick C. Beard for allowing his original jlink code to be placed under the Apache Software License. Submitted by: Matthew Kuperus Heun Revision Changes Path 1.96 +1 -0 jakarta-ant/docs/index.html Index: index.html =================================================================== RCS file: /home/cvs/jakarta-ant/docs/index.html,v retrieving revision 1.95 retrieving revision 1.96 diff -u -r1.95 -r1.96 --- index.html 2000/09/11 11:11:10 1.95 +++ index.html 2000/09/11 11:47:09 1.96 @@ -3865,6 +3865,7 @@
  • Cab
  • FTP
  • +
  • Jlink
  • JUnit
  • NetRexxC
  • RenameExtensions
  • 1.1 jakarta-ant/docs/jlink.html Index: jlink.html ===================================================================

    Jlink

    Description:

    Links entries from sub-builds and libraries.

    The jlink task can be used to build jar and zip files, similar to the jar task. However, jlink provides options for controlling the way entries from input files are added to the output file. Specifically, capabilities for merging entries from multiple zip or jar files is available.

    If a mergefile is specified directly (eg. at the top level of a mergefiles pathelement) and the mergefile ends in ".zip" or ".jar", entries in the mergefile will be merged into the outfile. A file with any other extension will be added to the output file, even if it is specified in the mergefiles element. Directories specified in either the mergefiles or addfiles element are added to the output file as you would expect: all files in subdirectories are recursively added to the output file with appropriate prefixes in the output file (without merging).

    In the case where duplicate entries and/or files are found among the files to be merged or added, jlink merges or adds the first entry and ignores all subsequent entries.

    jlink ignores META-INF directories in mergefiles. Users should supply their own manifest information for the output file.

    It is possible to refine the set of files that are being jlinked. This can be done with the includes, includesfile, excludes, excludesfile, and defaultexcludes attributes on the addfiles and mergefiles nested elements. With the includes or includesfile attribute you specify the files you want to have included by using patterns. The exclude or excludesfile attribute is used to specify the files you want to have excluded. This is also done with patterns. And finally with the defaultexcludes attribute, you can specify whether you want to use default exclusions or not. See the section on directory based tasks, on how the inclusion/exclusion of files works, and how to write patterns. The patterns are relative to the base directory.

    Parameters:

    Attribute Description Required
    outfile the path of the output file. Yes
    compress whether or not the output should be compressed. true, yes, or on result in compressed output. If omitted, output will be uncompressed (inflated). No
    mergefiles files to be merged into the output, if possible. At least one of mergefiles or addfiles
    addfiles files to be added to the output.

    Examples

    The following will merge the entries in mergefoo.jar and mergebar.jar into out.jar. mac.jar and pc.jar will be added as single entries to out.jar.
      <jlink compress="false" outfile="out.jar"/>
         <mergefiles>
           <pathelement path="${build.dir}/mergefoo.jar"/>
           <pathelement path="${build.dir}/mergebar.jar"/>
         </mergefiles>
         <addfiles>
           <pathelement path="${build.dir}/mac.jar"/>
           <pathelement path="${build.dir}/pc.zip"/>
         </addfiles>
      </jlink>
      
    Suppose the file foo.jar contains two entries: bar.class and barnone/myClass.zip. Suppose the path for file foo.jar is build/tempbuild/foo.jar. The following example will provide the entry tempbuild/foo.jar in the out.jar.
      <jlink compress="false" outfile="out.jar"/>
         <mergefiles>
           <pathelement path="build/tempbuild"/>
         </mergefiles>
      </jlink>
      
    However, the next example would result in two top-level entries in out.jar, namely bar.class and barnone/myClass.zip
      <jlink compress="false" outfile="out.jar"/>
         <mergefiles>
           <pathelement path="build/tempbuild/foo.jar"/>
         </mergefiles>
      </jlink>
      
    1.38 +1 -0 jakarta-ant/src/main/org/apache/tools/ant/taskdefs/defaults.properties Index: defaults.properties =================================================================== RCS file: /home/cvs/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/defaults.properties,v retrieving revision 1.37 retrieving revision 1.38 diff -u -r1.37 -r1.38 --- defaults.properties 2000/09/07 11:09:03 1.37 +++ defaults.properties 2000/09/11 11:47:11 1.38 @@ -60,6 +60,7 @@ javacc=org.apache.tools.ant.taskdefs.optional.javacc.JavaCC jjtree=org.apache.tools.ant.taskdefs.optional.javacc.JJTree starteam=org.apache.tools.ant.taskdefs.optional.scm.AntStarTeamCheckOut +jlink=org.apache.tools.ant.taskdefs.optional.jlink.JlinkTask # deprecated ant tasks (kept for back compatibility) javadoc2=org.apache.tools.ant.taskdefs.Javadoc 1.1 jakarta-ant/src/main/org/apache/tools/ant/taskdefs/optional/jlink/ClassNameReader.java Index: ClassNameReader.java =================================================================== /* * The Apache Software License, Version 1.1 * * Copyright (c) 2000 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . */ package org.apache.tools.ant.taskdefs.optional.jlink; import java.io .*; /** * Reads just enough of a class file to determine the class' full name. * *

    Extremely minimal constant pool implementation, mainly to support extracting * strings from a class file. * @author Patrick C. Beard. */ class ConstantPool extends Object{ static final byte UTF8 = 1, UNUSED = 2, INTEGER = 3, FLOAT = 4, LONG = 5, DOUBLE = 6, CLASS = 7, STRING = 8, FIELDREF = 9, METHODREF = 10, INTERFACEMETHODREF = 11, NAMEANDTYPE = 12; byte[] types; Object[] values; ConstantPool( DataInput data ) throws IOException { super(); int count = data .readUnsignedShort(); types = new byte [ count ]; values = new Object [ count ]; // read in all constant pool entries. for ( int i = 1; i < count; i++ ) { byte type = data .readByte(); types[i] = type; switch (type) { case UTF8 : values[i] = data .readUTF(); break; case UNUSED : break; case INTEGER : values[i] = new Integer( data .readInt() ); break; case FLOAT : values[i] = new Float( data .readFloat() ); break; case LONG : values[i] = new Long( data .readLong() ); ++i; break; case DOUBLE : values[i] = new Double( data .readDouble() ); ++i; break; case CLASS : case STRING : values[i] = new Integer( data .readUnsignedShort() ); break; case FIELDREF : case METHODREF : case INTERFACEMETHODREF : case NAMEANDTYPE : values[i] = new Integer( data .readInt() ); break; } } } } /** * Provides a quick and dirty way to determine the true name of a class * given just an InputStream. Reads in just enough to perform this * minimal task only. */ public class ClassNameReader extends Object{ public static String getClassName( InputStream input ) throws IOException { DataInputStream data = new DataInputStream( input ); // verify this is a valid class file. int cookie = data .readInt(); if ( cookie != 0xCAFEBABE ) { return null; } int version = data .readInt(); // read the constant pool. ConstantPool constants = new ConstantPool( data ); Object[] values = constants .values; // read access flags and class index. int accessFlags = data .readUnsignedShort(); int classIndex = data .readUnsignedShort(); Integer stringIndex = (Integer) values[classIndex]; String className = (String) values[stringIndex .intValue()]; return className; } } 1.1 jakarta-ant/src/main/org/apache/tools/ant/taskdefs/optional/jlink/JlinkTask.java Index: JlinkTask.java =================================================================== /* * The Apache Software License, Version 1.1 * * Copyright (c) 2000 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . */ package org.apache.tools.ant.taskdefs.optional.jlink; import org.apache.tools.ant .*; import org.apache.tools.ant.taskdefs.MatchingTask; import org.apache.tools.ant.types .*; import java.io.File; /** * This class defines objects that can link together various jar and * zip files. * *

    It is basically a wrapper for the jlink code written originally * by Patrick Beard. The * classes org.apache.tools.ant.taskdefs.optional.jlink.Jlink and * org.apache.tools.ant.taskdefs.optional.jlink.ClassNameReader * support this class.

    * *

    For example: * *

       * <jlink compress="false" outfile="out.jar"/>
       *   <mergefiles>
       *     <pathelement path="${build.dir}/mergefoo.jar"/>
       *     <pathelement path="${build.dir}/mergebar.jar"/>
       *   </mergefiles>
       *   <addfiles>
       *     <pathelement path="${build.dir}/mac.jar"/>
       *     <pathelement path="${build.dir}/pc.zip"/>
       *   </addfiles>
       * </jlink>
       * 
    * * * @author Matthew Kuperus Heun */ public class JlinkTask extends MatchingTask { /** * The output file for this run of jlink. Usually a jar or zip file. */ public void setOutfile( File outfile ) { this.outfile = outfile; } /** * Establishes the object that contains the files to * be merged into the output. */ public Path createMergefiles() { if ( this .mergefiles == null ) { this .mergefiles = new Path(getProject()); } return this .mergefiles.createPath(); } /** * Sets the files to be merged into the output. */ public void setMergefiles( Path mergefiles ) { if ( this .mergefiles == null ) { this .mergefiles = mergefiles; } else { this .mergefiles .append( mergefiles ); } } /** * Establishes the object that contains the files to * be added to the output. */ public Path createAddfiles() { if ( this .addfiles == null ) { this .addfiles = new Path(getProject()); } return this .addfiles .createPath(); } /** * Sets the files to be added into the output. */ public void setAddfiles( Path addfiles ) { if ( this .addfiles == null ) { this .addfiles = addfiles; } else { this .addfiles .append( addfiles ); } } /** * Defines whether or not the output should be compacted. */ public void setCompress( boolean compress ) { this .compress = compress; } /** * Does the adding and merging. */ public void execute() throws BuildException { //Be sure everything has been set. if ( outfile == null ) { throw new BuildException( "outfile attribute is required! Please set." ); } if (!haveAddFiles() && !haveMergeFiles()) { throw new BuildException( "addfiles or mergefiles required! Please set." ); } log( "linking: " + outfile.getPath() ); log( "compression: " + compress, Project.MSG_VERBOSE ); jlink linker = new jlink(); linker .setOutfile( outfile.getPath() ); linker .setCompression( compress ); if (haveMergeFiles()){ log( "merge files: " + mergefiles .toString(), Project .MSG_VERBOSE ); linker .addMergeFiles( mergefiles .list() ); } if (haveAddFiles()){ log( "add files: " + addfiles .toString(), Project .MSG_VERBOSE ); linker .addAddFiles( addfiles .list() ); } try { linker .link(); } catch( Exception ex ) { throw new BuildException( ex, location ); } } private boolean haveAddFiles(){ return haveEntries(addfiles); } private boolean haveMergeFiles(){ return haveEntries(mergefiles); } private boolean haveEntries(Path p){ if (p == null){ return false; } if (p.size() > 0){ return true; } return false; } private File outfile = null; private Path mergefiles = null; private Path addfiles = null; private boolean compress = false; private String ps = System .getProperty( "path.separator" ); } 1.1 jakarta-ant/src/main/org/apache/tools/ant/taskdefs/optional/jlink/jlink.java Index: jlink.java =================================================================== /* * The Apache Software License, Version 1.1 * * Copyright (c) 2000 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . */ /** * jlink.java * links together multiple .jar files * * Original code by Patrick Beard. Modifications to work * with ANT by Matthew Kuperus Heun. * * @author Matthew Kuperus Heun */ package org.apache.tools.ant.taskdefs.optional.jlink; import java.io .*; import java.util.zip .*; import java.util .Vector; import java.util .Enumeration; public class jlink extends Object{ /** * The file that will be created by this instance of jlink. */ public void setOutfile( String outfile ) { if ( outfile == null ) { return ; } this .outfile = outfile; } /** * Adds a file to be merged into the output. */ public void addMergeFile( String mergefile ) { if ( mergefile == null ) { return ; } mergefiles .addElement( mergefile ); } /** * Adds a file to be added into the output. */ public void addAddFile( String addfile ) { if ( addfile == null ) { return ; } addfiles .addElement( addfile ); } /** * Adds several files to be merged into the output. */ public void addMergeFiles( String[] mergefiles ) { if ( mergefiles == null ) { return ; } for ( int i = 0; i < mergefiles .length; i++ ) { addMergeFile( mergefiles[i] ); } } /** * Adds several file to be added into the output. */ public void addAddFiles( String[] addfiles ) { if ( addfiles == null ) { return ; } for ( int i = 0; i < addfiles .length; i++ ) { addAddFile( addfiles[i] ); } } /** * Determines whether output will be compressed. */ public void setCompression( boolean compress ) { this .compression = compress; } /** * Performs the linking of files. * Addfiles are added to the output as-is. For example, a * jar file is added to the output as a jar file. * However, mergefiles are first examined for their type. * If it is a jar or zip file, the contents will be extracted * from the mergefile and entered into the output. * If a zip or jar file is encountered in a subdirectory * it will be added, not merged. * If a directory is encountered, it becomes the root * entry of all the files below it. Thus, you can * provide multiple, disjoint directories, as * addfiles: they will all be added in a rational * manner to outfile. */ public void link() throws Exception { ZipOutputStream output = new ZipOutputStream( new FileOutputStream( outfile ) ); if ( compression ) { output .setMethod( ZipOutputStream .DEFLATED ); output .setLevel( Deflater .DEFAULT_COMPRESSION ); } else { output .setMethod( ZipOutputStream .STORED ); } Enumeration merges = mergefiles .elements(); while ( merges .hasMoreElements() ) { String path = (String) merges .nextElement(); File f = new File( path ); if ( f.getName().endsWith( ".jar" ) || f.getName().endsWith( ".zip" ) ) { //Do the merge mergeZipJarContents( output, f ); } else { //Add this file to the addfiles Vector and add it later at the top level of the output file. addAddFile( path ); } } Enumeration adds = addfiles .elements(); while ( adds .hasMoreElements() ) { String name = (String) adds .nextElement(); File f = new File( name ); if ( f .isDirectory() ) { //System.out.println("in jlink: adding directory contents of " + f.getPath()); addDirContents( output, f, f.getName() + '/', compression ); } else { addFile( output, f, "", compression ); } } if ( output != null ) { try { output .close(); } catch( IOException ioe ) {} } } public static void main( String[] args ) { // jlink output input1 ... inputN if ( args .length < 2 ) { System .out .println( "usage: jlink output input1 ... inputN" ); System .exit( 1 ); } jlink linker = new jlink(); linker .setOutfile( args[0] ); //To maintain compatibility with the command-line version, we will only add files to be merged. for ( int i = 1; i < args .length; i++ ) { linker .addMergeFile( args[i] ); } try { linker .link(); } catch( Exception ex ) { System .err .print( ex .getMessage() ); } } /* * Actually performs the merging of f into the output. * f should be a zip or jar file. */ private void mergeZipJarContents( ZipOutputStream output, File f ) throws IOException { //Check to see that the file with name "name" exists. if ( ! f .exists() ) { return ; } ZipFile zipf = new ZipFile( f ); Enumeration entries = zipf.entries(); while (entries.hasMoreElements()){ ZipEntry inputEntry = (ZipEntry) entries.nextElement(); //Ignore manifest entries. They're bound to cause conflicts between //files that are being merged. User should supply their own //manifest file when doing the merge. String inputEntryName = inputEntry.getName(); int index = inputEntryName.indexOf("META-INF"); if (index < 0){ //META-INF not found in the name of the entry. Go ahead and process it. try { output.putNextEntry(processEntry(zipf, inputEntry)); } catch (ZipException ex){ //If we get here, it could be because we are trying to put a //directory entry that already exists. //For example, we're trying to write "com", but a previous //entry from another mergefile was called "com". //In that case, just ignore the error and go on to the //next entry. String mess = ex.getMessage(); if (mess.indexOf("duplicate") > 0){ //It was the duplicate entry. continue; } else { //I hate to admit it, but we don't know what happened here. Throw the Exception. throw ex; } } InputStream in = zipf.getInputStream(inputEntry); int len = buffer.length; int count = -1; while ((count = in.read(buffer, 0, len)) > 0){ output.write(buffer, 0, count); } in.close(); output.closeEntry(); } } zipf .close(); } /* * Adds contents of a directory to the output. */ private void addDirContents( ZipOutputStream output, File dir, String prefix, boolean compress ) throws IOException { String[] contents = dir .list(); for ( int i = 0; i < contents .length; ++i ) { String name = contents[i]; File file = new File( dir, name ); if ( file .isDirectory() ) { addDirContents( output, file, prefix + name + '/', compress ); } else { addFile( output, file, prefix, compress ); } } } /* * Gets the name of an entry in the file. This is the real name * which for a class is the name of the package with the class * name appended. */ private String getEntryName( File file, String prefix ) { String name = file .getName(); if ( ! name .endsWith( ".class" ) ) { // see if the file is in fact a .class file, and determine its actual name. try { InputStream input = new FileInputStream( file ); String className = ClassNameReader .getClassName( input ); input .close(); if ( className != null ) { return className .replace( '.', '/' ) + ".class"; } } catch( IOException ioe ) {} } System.out.println("From " + file.getPath() + " and prefix " + prefix + ", creating entry " + prefix+name); return (prefix + name); } /* * Adds a file to the output stream. */ private void addFile( ZipOutputStream output, File file, String prefix, boolean compress) throws IOException { //Make sure file exists long checksum = 0; if ( ! file .exists() ) { return ; } ZipEntry entry = new ZipEntry( getEntryName( file, prefix ) ); entry .setTime( file .lastModified() ); entry .setSize( file .length() ); if (! compress){ entry.setCrc(calcChecksum(file)); } FileInputStream input = new FileInputStream( file ); addToOutputStream(output, input, entry); } /* * A convenience method that several other methods might call. */ private void addToOutputStream(ZipOutputStream output, InputStream input, ZipEntry ze) throws IOException{ try { output.putNextEntry(ze); } catch (ZipException zipEx) { //This entry already exists. So, go with the first one. input.close(); return; } int numBytes = -1; while((numBytes = input.read(buffer)) > 0){ output.write(buffer, 0, numBytes); } output.closeEntry(); input.close(); } /* * A method that does the work on a given entry in a mergefile. * The big deal is to set the right parameters in the ZipEntry * on the output stream. */ private ZipEntry processEntry( ZipFile zip, ZipEntry inputEntry ) throws IOException{ /* First, some notes. On MRJ 2.2.2, getting the size, compressed size, and CRC32 from the ZipInputStream does not work for compressed (deflated) files. Those calls return -1. For uncompressed (stored) files, those calls do work. However, using ZipFile.getEntries() works for both compressed and uncompressed files. Now, from some simple testing I did, it seems that the value of CRC-32 is independent of the compression setting. So, it should be easy to pass this information on to the output entry. */ String name = inputEntry .getName(); if ( ! (inputEntry .isDirectory() || name .endsWith( ".class" )) ) { try { InputStream input = zip.getInputStream( zip .getEntry( name ) ); String className = ClassNameReader .getClassName( input ); input .close(); if ( className != null ) { name = className .replace( '.', '/' ) + ".class"; } } catch( IOException ioe ) {} } ZipEntry outputEntry = new ZipEntry( name ); outputEntry.setTime(inputEntry .getTime() ); outputEntry.setExtra(inputEntry.getExtra()); outputEntry.setComment(inputEntry.getComment()); outputEntry.setTime(inputEntry.getTime()); if (compression){ outputEntry.setMethod(ZipEntry.DEFLATED); //Note, don't need to specify size or crc for compressed files. } else { outputEntry.setMethod(ZipEntry.STORED); outputEntry.setCrc(inputEntry.getCrc()); outputEntry.setSize(inputEntry.getSize()); } return outputEntry; } /* * Necessary in the case where you add a entry that * is not compressed. */ private long calcChecksum(File f) throws IOException { BufferedInputStream in = new BufferedInputStream(new FileInputStream(f)); return calcChecksum(in, f.length()); } /* * Necessary in the case where you add a entry that * is not compressed. */ private long calcChecksum(InputStream in, long size) throws IOException{ CRC32 crc = new CRC32(); int len = buffer.length; int count = -1; int haveRead = 0; while((count=in.read(buffer, 0, len)) > 0){ haveRead += count; crc.update(buffer, 0, count); } in.close(); return crc.getValue(); } private String outfile = null; private Vector mergefiles = new Vector( 10 ); private Vector addfiles = new Vector( 10 ); private boolean compression = false; byte[] buffer = new byte[8192]; }