git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@270446 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -1,116 +0,0 @@ | |||
| /* | |||
| * Copyright (C) The Apache Software Foundation. All rights reserved. | |||
| * | |||
| * This software is published under the terms of the Apache Software License | |||
| * version 1.1, a copy of which has been included with this distribution in | |||
| * the LICENSE.txt file. | |||
| */ | |||
| package org.apache.tools.ant.taskdefs.optional.jlink; | |||
| import java.io.DataInput; | |||
| import java.io.DataInputStream; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| /** | |||
| * 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. | |||
| * | |||
| * @author RT | |||
| */ | |||
| 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; | |||
| } | |||
| } | |||
| /** | |||
| * Reads just enough of a class file to determine the class' full name. <p> | |||
| * | |||
| * Extremely minimal constant pool implementation, mainly to support extracting | |||
| * strings from a class file. | |||
| * | |||
| * @author <a href="mailto:beard@netscape.com">Patrick C. Beard</a> . | |||
| */ | |||
| class ConstantPool extends Object | |||
| { | |||
| final static 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; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -1,210 +0,0 @@ | |||
| /* | |||
| * Copyright (C) The Apache Software Foundation. All rights reserved. | |||
| * | |||
| * This software is published under the terms of the Apache Software License | |||
| * version 1.1, a copy of which has been included with this distribution in | |||
| * the LICENSE.txt file. | |||
| */ | |||
| package org.apache.tools.ant.taskdefs.optional.jlink; | |||
| import java.io.File; | |||
| import org.apache.myrmidon.api.TaskException; | |||
| import org.apache.tools.ant.taskdefs.MatchingTask; | |||
| import org.apache.tools.ant.types.Path; | |||
| /** | |||
| * This class defines objects that can link together various jar and zip files. | |||
| * <p> | |||
| * | |||
| * It is basically a wrapper for the jlink code written originally by <a | |||
| * href="mailto:beard@netscape.com">Patrick Beard</a> . The classes | |||
| * org.apache.tools.ant.taskdefs.optional.jlink.Jlink and | |||
| * org.apache.tools.ant.taskdefs.optional.jlink.ClassNameReader support this | |||
| * class.</p> <p> | |||
| * | |||
| * For example: <code> | |||
| * <pre> | |||
| * <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> | |||
| * </pre> </code> | |||
| * | |||
| * @author <a href="mailto:matthew.k.heun@gaerospace.com">Matthew Kuperus Heun | |||
| * </a> | |||
| */ | |||
| public class JlinkTask extends MatchingTask | |||
| { | |||
| private File outfile = null; | |||
| private Path mergefiles = null; | |||
| private Path addfiles = null; | |||
| private boolean compress = false; | |||
| private String ps = System.getProperty( "path.separator" ); | |||
| /** | |||
| * Sets the files to be added into the output. | |||
| * | |||
| * @param addfiles The new Addfiles value | |||
| */ | |||
| public void setAddfiles( Path addfiles ) | |||
| throws TaskException | |||
| { | |||
| if( this.addfiles == null ) | |||
| { | |||
| this.addfiles = addfiles; | |||
| } | |||
| else | |||
| { | |||
| this.addfiles.append( addfiles ); | |||
| } | |||
| } | |||
| /** | |||
| * Defines whether or not the output should be compacted. | |||
| * | |||
| * @param compress The new Compress value | |||
| */ | |||
| public void setCompress( boolean compress ) | |||
| { | |||
| this.compress = compress; | |||
| } | |||
| /** | |||
| * Sets the files to be merged into the output. | |||
| * | |||
| * @param mergefiles The new Mergefiles value | |||
| */ | |||
| public void setMergefiles( Path mergefiles ) | |||
| throws TaskException | |||
| { | |||
| if( this.mergefiles == null ) | |||
| { | |||
| this.mergefiles = mergefiles; | |||
| } | |||
| else | |||
| { | |||
| this.mergefiles.append( mergefiles ); | |||
| } | |||
| } | |||
| /** | |||
| * The output file for this run of jlink. Usually a jar or zip file. | |||
| * | |||
| * @param outfile The new Outfile value | |||
| */ | |||
| public void setOutfile( File outfile ) | |||
| { | |||
| this.outfile = outfile; | |||
| } | |||
| /** | |||
| * Establishes the object that contains the files to be added to the output. | |||
| * | |||
| * @return Description of the Returned Value | |||
| */ | |||
| public Path createAddfiles() | |||
| throws TaskException | |||
| { | |||
| if( this.addfiles == null ) | |||
| { | |||
| this.addfiles = new Path(); | |||
| } | |||
| return this.addfiles.createPath(); | |||
| } | |||
| /** | |||
| * Establishes the object that contains the files to be merged into the | |||
| * output. | |||
| * | |||
| * @return Description of the Returned Value | |||
| */ | |||
| public Path createMergefiles() | |||
| throws TaskException | |||
| { | |||
| if( this.mergefiles == null ) | |||
| { | |||
| this.mergefiles = new Path(); | |||
| } | |||
| return this.mergefiles.createPath(); | |||
| } | |||
| /** | |||
| * Does the adding and merging. | |||
| * | |||
| * @exception TaskException Description of Exception | |||
| */ | |||
| public void execute() | |||
| throws TaskException | |||
| { | |||
| //Be sure everything has been set. | |||
| if( outfile == null ) | |||
| { | |||
| throw new TaskException( "outfile attribute is required! Please set." ); | |||
| } | |||
| if( !haveAddFiles() && !haveMergeFiles() ) | |||
| { | |||
| throw new TaskException( "addfiles or mergefiles required! Please set." ); | |||
| } | |||
| getLogger().info( "linking: " + outfile.getPath() ); | |||
| getLogger().debug( "compression: " + compress ); | |||
| jlink linker = new jlink(); | |||
| linker.setOutfile( outfile.getPath() ); | |||
| linker.setCompression( compress ); | |||
| if( haveMergeFiles() ) | |||
| { | |||
| getLogger().debug( "merge files: " + mergefiles.toString() ); | |||
| linker.addMergeFiles( mergefiles.list() ); | |||
| } | |||
| if( haveAddFiles() ) | |||
| { | |||
| getLogger().debug( "add files: " + addfiles.toString() ); | |||
| linker.addAddFiles( addfiles.list() ); | |||
| } | |||
| try | |||
| { | |||
| linker.link(); | |||
| } | |||
| catch( Exception ex ) | |||
| { | |||
| throw new TaskException( "Error", ex ); | |||
| } | |||
| } | |||
| private boolean haveAddFiles() | |||
| throws TaskException | |||
| { | |||
| return haveEntries( addfiles ); | |||
| } | |||
| private boolean haveEntries( Path p ) | |||
| throws TaskException | |||
| { | |||
| if( p == null ) | |||
| { | |||
| return false; | |||
| } | |||
| if( p.size() > 0 ) | |||
| { | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| private boolean haveMergeFiles() | |||
| throws TaskException | |||
| { | |||
| return haveEntries( mergefiles ); | |||
| } | |||
| } | |||
| @@ -1,469 +0,0 @@ | |||
| /* | |||
| * Copyright (C) The Apache Software Foundation. All rights reserved. | |||
| * | |||
| * This software is published under the terms of the Apache Software License | |||
| * version 1.1, a copy of which has been included with this distribution in | |||
| * the LICENSE.txt file. | |||
| */ | |||
| package org.apache.tools.ant.taskdefs.optional.jlink; | |||
| import java.io.BufferedInputStream; | |||
| import java.io.File; | |||
| import java.io.FileInputStream; | |||
| import java.io.FileOutputStream; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| import java.util.ArrayList; | |||
| import java.util.Enumeration; | |||
| import java.util.Iterator; | |||
| import java.util.zip.CRC32; | |||
| import java.util.zip.Deflater; | |||
| import java.util.zip.ZipEntry; | |||
| import java.util.zip.ZipException; | |||
| import java.util.zip.ZipFile; | |||
| import java.util.zip.ZipOutputStream; | |||
| public class jlink extends Object | |||
| { | |||
| private String outfile = null; | |||
| private ArrayList mergefiles = new ArrayList( 10 ); | |||
| private ArrayList addfiles = new ArrayList( 10 ); | |||
| private boolean compression = false; | |||
| byte[] buffer = new byte[ 8192 ]; | |||
| 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() ); | |||
| } | |||
| } | |||
| /** | |||
| * Determines whether output will be compressed. | |||
| * | |||
| * @param compress The new Compression value | |||
| */ | |||
| public void setCompression( boolean compress ) | |||
| { | |||
| this.compression = compress; | |||
| } | |||
| /** | |||
| * The file that will be created by this instance of jlink. | |||
| * | |||
| * @param outfile The new Outfile value | |||
| */ | |||
| public void setOutfile( String outfile ) | |||
| { | |||
| if( outfile == null ) | |||
| { | |||
| return; | |||
| } | |||
| this.outfile = outfile; | |||
| } | |||
| /** | |||
| * Adds a file to be added into the output. | |||
| * | |||
| * @param addfile The feature to be added to the AddFile attribute | |||
| */ | |||
| public void addAddFile( String addfile ) | |||
| { | |||
| if( addfile == null ) | |||
| { | |||
| return; | |||
| } | |||
| addfiles.add( addfile ); | |||
| } | |||
| /** | |||
| * Adds several file to be added into the output. | |||
| * | |||
| * @param addfiles The feature to be added to the AddFiles attribute | |||
| */ | |||
| public void addAddFiles( String[] addfiles ) | |||
| { | |||
| if( addfiles == null ) | |||
| { | |||
| return; | |||
| } | |||
| for( int i = 0; i < addfiles.length; i++ ) | |||
| { | |||
| addAddFile( addfiles[ i ] ); | |||
| } | |||
| } | |||
| /** | |||
| * Adds a file to be merged into the output. | |||
| * | |||
| * @param mergefile The feature to be added to the MergeFile attribute | |||
| */ | |||
| public void addMergeFile( String mergefile ) | |||
| { | |||
| if( mergefile == null ) | |||
| { | |||
| return; | |||
| } | |||
| mergefiles.add( mergefile ); | |||
| } | |||
| /** | |||
| * Adds several files to be merged into the output. | |||
| * | |||
| * @param mergefiles The feature to be added to the MergeFiles attribute | |||
| */ | |||
| public void addMergeFiles( String[] mergefiles ) | |||
| { | |||
| if( mergefiles == null ) | |||
| { | |||
| return; | |||
| } | |||
| for( int i = 0; i < mergefiles.length; i++ ) | |||
| { | |||
| addMergeFile( mergefiles[ i ] ); | |||
| } | |||
| } | |||
| /** | |||
| * 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. | |||
| * | |||
| * @exception Exception Description of Exception | |||
| */ | |||
| 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 ); | |||
| } | |||
| Iterator merges = mergefiles.iterator(); | |||
| while( merges.hasNext() ) | |||
| { | |||
| String path = (String)merges.next(); | |||
| 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 ArrayList and add it | |||
| //later at the top level of the output file. | |||
| addAddFile( path ); | |||
| } | |||
| } | |||
| Iterator adds = addfiles.iterator(); | |||
| while( adds.hasNext() ) | |||
| { | |||
| String name = (String)adds.next(); | |||
| 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 ) | |||
| { | |||
| } | |||
| } | |||
| } | |||
| /* | |||
| * 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 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 ); | |||
| } | |||
| } | |||
| } | |||
| /* | |||
| * 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(); | |||
| } | |||
| /* | |||
| * 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(); | |||
| } | |||
| /* | |||
| * 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(); | |||
| } | |||
| /* | |||
| * 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; | |||
| } | |||
| } | |||
| @@ -1,116 +0,0 @@ | |||
| /* | |||
| * Copyright (C) The Apache Software Foundation. All rights reserved. | |||
| * | |||
| * This software is published under the terms of the Apache Software License | |||
| * version 1.1, a copy of which has been included with this distribution in | |||
| * the LICENSE.txt file. | |||
| */ | |||
| package org.apache.tools.ant.taskdefs.optional.jlink; | |||
| import java.io.DataInput; | |||
| import java.io.DataInputStream; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| /** | |||
| * 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. | |||
| * | |||
| * @author RT | |||
| */ | |||
| 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; | |||
| } | |||
| } | |||
| /** | |||
| * Reads just enough of a class file to determine the class' full name. <p> | |||
| * | |||
| * Extremely minimal constant pool implementation, mainly to support extracting | |||
| * strings from a class file. | |||
| * | |||
| * @author <a href="mailto:beard@netscape.com">Patrick C. Beard</a> . | |||
| */ | |||
| class ConstantPool extends Object | |||
| { | |||
| final static 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; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -1,210 +0,0 @@ | |||
| /* | |||
| * Copyright (C) The Apache Software Foundation. All rights reserved. | |||
| * | |||
| * This software is published under the terms of the Apache Software License | |||
| * version 1.1, a copy of which has been included with this distribution in | |||
| * the LICENSE.txt file. | |||
| */ | |||
| package org.apache.tools.ant.taskdefs.optional.jlink; | |||
| import java.io.File; | |||
| import org.apache.myrmidon.api.TaskException; | |||
| import org.apache.tools.ant.taskdefs.MatchingTask; | |||
| import org.apache.tools.ant.types.Path; | |||
| /** | |||
| * This class defines objects that can link together various jar and zip files. | |||
| * <p> | |||
| * | |||
| * It is basically a wrapper for the jlink code written originally by <a | |||
| * href="mailto:beard@netscape.com">Patrick Beard</a> . The classes | |||
| * org.apache.tools.ant.taskdefs.optional.jlink.Jlink and | |||
| * org.apache.tools.ant.taskdefs.optional.jlink.ClassNameReader support this | |||
| * class.</p> <p> | |||
| * | |||
| * For example: <code> | |||
| * <pre> | |||
| * <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> | |||
| * </pre> </code> | |||
| * | |||
| * @author <a href="mailto:matthew.k.heun@gaerospace.com">Matthew Kuperus Heun | |||
| * </a> | |||
| */ | |||
| public class JlinkTask extends MatchingTask | |||
| { | |||
| private File outfile = null; | |||
| private Path mergefiles = null; | |||
| private Path addfiles = null; | |||
| private boolean compress = false; | |||
| private String ps = System.getProperty( "path.separator" ); | |||
| /** | |||
| * Sets the files to be added into the output. | |||
| * | |||
| * @param addfiles The new Addfiles value | |||
| */ | |||
| public void setAddfiles( Path addfiles ) | |||
| throws TaskException | |||
| { | |||
| if( this.addfiles == null ) | |||
| { | |||
| this.addfiles = addfiles; | |||
| } | |||
| else | |||
| { | |||
| this.addfiles.append( addfiles ); | |||
| } | |||
| } | |||
| /** | |||
| * Defines whether or not the output should be compacted. | |||
| * | |||
| * @param compress The new Compress value | |||
| */ | |||
| public void setCompress( boolean compress ) | |||
| { | |||
| this.compress = compress; | |||
| } | |||
| /** | |||
| * Sets the files to be merged into the output. | |||
| * | |||
| * @param mergefiles The new Mergefiles value | |||
| */ | |||
| public void setMergefiles( Path mergefiles ) | |||
| throws TaskException | |||
| { | |||
| if( this.mergefiles == null ) | |||
| { | |||
| this.mergefiles = mergefiles; | |||
| } | |||
| else | |||
| { | |||
| this.mergefiles.append( mergefiles ); | |||
| } | |||
| } | |||
| /** | |||
| * The output file for this run of jlink. Usually a jar or zip file. | |||
| * | |||
| * @param outfile The new Outfile value | |||
| */ | |||
| public void setOutfile( File outfile ) | |||
| { | |||
| this.outfile = outfile; | |||
| } | |||
| /** | |||
| * Establishes the object that contains the files to be added to the output. | |||
| * | |||
| * @return Description of the Returned Value | |||
| */ | |||
| public Path createAddfiles() | |||
| throws TaskException | |||
| { | |||
| if( this.addfiles == null ) | |||
| { | |||
| this.addfiles = new Path(); | |||
| } | |||
| return this.addfiles.createPath(); | |||
| } | |||
| /** | |||
| * Establishes the object that contains the files to be merged into the | |||
| * output. | |||
| * | |||
| * @return Description of the Returned Value | |||
| */ | |||
| public Path createMergefiles() | |||
| throws TaskException | |||
| { | |||
| if( this.mergefiles == null ) | |||
| { | |||
| this.mergefiles = new Path(); | |||
| } | |||
| return this.mergefiles.createPath(); | |||
| } | |||
| /** | |||
| * Does the adding and merging. | |||
| * | |||
| * @exception TaskException Description of Exception | |||
| */ | |||
| public void execute() | |||
| throws TaskException | |||
| { | |||
| //Be sure everything has been set. | |||
| if( outfile == null ) | |||
| { | |||
| throw new TaskException( "outfile attribute is required! Please set." ); | |||
| } | |||
| if( !haveAddFiles() && !haveMergeFiles() ) | |||
| { | |||
| throw new TaskException( "addfiles or mergefiles required! Please set." ); | |||
| } | |||
| getLogger().info( "linking: " + outfile.getPath() ); | |||
| getLogger().debug( "compression: " + compress ); | |||
| jlink linker = new jlink(); | |||
| linker.setOutfile( outfile.getPath() ); | |||
| linker.setCompression( compress ); | |||
| if( haveMergeFiles() ) | |||
| { | |||
| getLogger().debug( "merge files: " + mergefiles.toString() ); | |||
| linker.addMergeFiles( mergefiles.list() ); | |||
| } | |||
| if( haveAddFiles() ) | |||
| { | |||
| getLogger().debug( "add files: " + addfiles.toString() ); | |||
| linker.addAddFiles( addfiles.list() ); | |||
| } | |||
| try | |||
| { | |||
| linker.link(); | |||
| } | |||
| catch( Exception ex ) | |||
| { | |||
| throw new TaskException( "Error", ex ); | |||
| } | |||
| } | |||
| private boolean haveAddFiles() | |||
| throws TaskException | |||
| { | |||
| return haveEntries( addfiles ); | |||
| } | |||
| private boolean haveEntries( Path p ) | |||
| throws TaskException | |||
| { | |||
| if( p == null ) | |||
| { | |||
| return false; | |||
| } | |||
| if( p.size() > 0 ) | |||
| { | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| private boolean haveMergeFiles() | |||
| throws TaskException | |||
| { | |||
| return haveEntries( mergefiles ); | |||
| } | |||
| } | |||
| @@ -1,469 +0,0 @@ | |||
| /* | |||
| * Copyright (C) The Apache Software Foundation. All rights reserved. | |||
| * | |||
| * This software is published under the terms of the Apache Software License | |||
| * version 1.1, a copy of which has been included with this distribution in | |||
| * the LICENSE.txt file. | |||
| */ | |||
| package org.apache.tools.ant.taskdefs.optional.jlink; | |||
| import java.io.BufferedInputStream; | |||
| import java.io.File; | |||
| import java.io.FileInputStream; | |||
| import java.io.FileOutputStream; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| import java.util.ArrayList; | |||
| import java.util.Enumeration; | |||
| import java.util.Iterator; | |||
| import java.util.zip.CRC32; | |||
| import java.util.zip.Deflater; | |||
| import java.util.zip.ZipEntry; | |||
| import java.util.zip.ZipException; | |||
| import java.util.zip.ZipFile; | |||
| import java.util.zip.ZipOutputStream; | |||
| public class jlink extends Object | |||
| { | |||
| private String outfile = null; | |||
| private ArrayList mergefiles = new ArrayList( 10 ); | |||
| private ArrayList addfiles = new ArrayList( 10 ); | |||
| private boolean compression = false; | |||
| byte[] buffer = new byte[ 8192 ]; | |||
| 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() ); | |||
| } | |||
| } | |||
| /** | |||
| * Determines whether output will be compressed. | |||
| * | |||
| * @param compress The new Compression value | |||
| */ | |||
| public void setCompression( boolean compress ) | |||
| { | |||
| this.compression = compress; | |||
| } | |||
| /** | |||
| * The file that will be created by this instance of jlink. | |||
| * | |||
| * @param outfile The new Outfile value | |||
| */ | |||
| public void setOutfile( String outfile ) | |||
| { | |||
| if( outfile == null ) | |||
| { | |||
| return; | |||
| } | |||
| this.outfile = outfile; | |||
| } | |||
| /** | |||
| * Adds a file to be added into the output. | |||
| * | |||
| * @param addfile The feature to be added to the AddFile attribute | |||
| */ | |||
| public void addAddFile( String addfile ) | |||
| { | |||
| if( addfile == null ) | |||
| { | |||
| return; | |||
| } | |||
| addfiles.add( addfile ); | |||
| } | |||
| /** | |||
| * Adds several file to be added into the output. | |||
| * | |||
| * @param addfiles The feature to be added to the AddFiles attribute | |||
| */ | |||
| public void addAddFiles( String[] addfiles ) | |||
| { | |||
| if( addfiles == null ) | |||
| { | |||
| return; | |||
| } | |||
| for( int i = 0; i < addfiles.length; i++ ) | |||
| { | |||
| addAddFile( addfiles[ i ] ); | |||
| } | |||
| } | |||
| /** | |||
| * Adds a file to be merged into the output. | |||
| * | |||
| * @param mergefile The feature to be added to the MergeFile attribute | |||
| */ | |||
| public void addMergeFile( String mergefile ) | |||
| { | |||
| if( mergefile == null ) | |||
| { | |||
| return; | |||
| } | |||
| mergefiles.add( mergefile ); | |||
| } | |||
| /** | |||
| * Adds several files to be merged into the output. | |||
| * | |||
| * @param mergefiles The feature to be added to the MergeFiles attribute | |||
| */ | |||
| public void addMergeFiles( String[] mergefiles ) | |||
| { | |||
| if( mergefiles == null ) | |||
| { | |||
| return; | |||
| } | |||
| for( int i = 0; i < mergefiles.length; i++ ) | |||
| { | |||
| addMergeFile( mergefiles[ i ] ); | |||
| } | |||
| } | |||
| /** | |||
| * 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. | |||
| * | |||
| * @exception Exception Description of Exception | |||
| */ | |||
| 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 ); | |||
| } | |||
| Iterator merges = mergefiles.iterator(); | |||
| while( merges.hasNext() ) | |||
| { | |||
| String path = (String)merges.next(); | |||
| 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 ArrayList and add it | |||
| //later at the top level of the output file. | |||
| addAddFile( path ); | |||
| } | |||
| } | |||
| Iterator adds = addfiles.iterator(); | |||
| while( adds.hasNext() ) | |||
| { | |||
| String name = (String)adds.next(); | |||
| 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 ) | |||
| { | |||
| } | |||
| } | |||
| } | |||
| /* | |||
| * 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 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 ); | |||
| } | |||
| } | |||
| } | |||
| /* | |||
| * 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(); | |||
| } | |||
| /* | |||
| * 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(); | |||
| } | |||
| /* | |||
| * 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(); | |||
| } | |||
| /* | |||
| * 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; | |||
| } | |||
| } | |||