ant-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bode...@locus.apache.org
Subject cvs commit: jakarta-ant/src/main/org/apache/tools/ant/taskdefs/optional/jlink ClassNameReader.java JlinkTask.java jlink.java
Date Mon, 11 Sep 2000 11:47:13 GMT
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 <jlink>.
  
  Many thanks to Patrick C. Beard <beard@netscape.com> for allowing his
  original jlink code to be placed under the Apache Software License.
  
  Submitted by:	Matthew Kuperus Heun <matthew.k.heun@gaerospace.com>
  
  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 @@
   <ul>
     <li><a href="#cab">Cab</a></li>
     <li><a href="#ftp">FTP</a></li>
  +  <li><a href="jlink.html">Jlink</a></li>
     <li><a href="junit.html">JUnit</a></li>
     <li><a href="#netrexxc">NetRexxC</a></li>
     <li><a href="#renameexts">RenameExtensions</a></li>
  
  
  
  1.1                  jakarta-ant/docs/jlink.html
  
  Index: jlink.html
  ===================================================================
  <html>
  <head>
  </head>
  <body>
  
  <h2><a name="jlink">Jlink</a></h2>
  <h3><b>Description:</b></h3>
  <p>Links entries from sub-builds and libraries.</p>
  
  <p>The jlink task can be used to build jar and zip files, similar to 
  the <i>jar</i> 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.</p>
  
  <p>If a mergefile is specified directly (eg. at the top level of a 
  <i>mergefiles</i>
  pathelement) <i>and</i> the mergefile ends in &quot;.zip&quot; or 
  &quot;.jar&quot;,
  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).
  </p>
  
  <p>
  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.
  </p>
  
  <p>
  jlink ignores META-INF directories in mergefiles. Users should supply their
  own manifest information for the output file.
  </p>
  
  <p>It is possible to refine the set of files that are being jlinked. 
  This can be
  done with the <i>includes</i>, <i>includesfile</i>, <i>excludes</i>,

  <i>excludesfile</i>,
  and <i>defaultexcludes</i> attributes on the <i>addfiles</i> and

  <i>mergefiles</i>
  nested elements. With the <i>includes</i> or <i>includesfile</i>
  attribute you specify the files you want to have included by using patterns.
  The <i>exclude</i> or <i>excludesfile</i> attribute is used to specify
  the files you want to have excluded. This is also done with patterns. And
  finally with the <i>defaultexcludes</i> attribute, you can specify whether you
  want to use default exclusions or not. See the section on <a
  href="#directorybasedtasks">directory based tasks</a>, on how the
  inclusion/exclusion of files works, and how to write patterns. The patterns are
  relative to the <i>base</i> directory.</p>
  
  
  
  <h3>Parameters:</h3>
  <table border="1" cellpadding="2" cellspacing="0">
     <tr>
       <td valign="top"><b>Attribute</b></td>
       <td valign="top"><b>Description</b></td>
       <td align="center" valign="top"><b>Required</b></td>
     </tr>
     <tr>
       <td valign="top">outfile</td>
       <td valign="top">the path of the output file.</td>
       <td valign="top" align="center">Yes</td>
     </tr>
     <tr>
       <td valign="top">compress</td>
       <td valign="top">whether or not the output should be compressed. 
  <i>true</i>,
                        <i>yes</i>, or <i>on</i> result in compressed
output.
                        If omitted, output will be uncompressed (inflated).</td>
       <td valign="top" align="center">No</td>
     </tr>
     <tr>
       <td valign="top">mergefiles</td>
       <td valign="top">files to be merged into the output, if possible.</td>
       <td valign="middle" align="middle" rowspan="2">At least one of 
  mergefiles or addfiles</td>
     </tr>
     <tr>
       <td valign="top">addfiles</td>
       <td valign="top">files to be added to the output.</td>
     </tr>
  </table>
  
  <h3>Examples</h3>
  
  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.
  <pre>
  &lt;jlink compress=&quot;false&quot; outfile=&quot;out.jar&quot;/&gt;
     &lt;mergefiles&gt;
       &lt;pathelement path=&quot;${build.dir}/mergefoo.jar&quot;/&gt;
       &lt;pathelement path=&quot;${build.dir}/mergebar.jar&quot;/&gt;
     &lt;/mergefiles&gt;
     &lt;addfiles&gt;
       &lt;pathelement path=&quot;${build.dir}/mac.jar&quot;/&gt;
       &lt;pathelement path=&quot;${build.dir}/pc.zip&quot;/&gt;
     &lt;/addfiles&gt;
  &lt;/jlink&gt;
  </pre>
  
  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.
  <pre>
  &lt;jlink compress=&quot;false&quot; outfile=&quot;out.jar&quot;/&gt;
     &lt;mergefiles&gt;
       &lt;pathelement path=&quot;build/tempbuild&quot;/&gt;
     &lt;/mergefiles&gt;
  &lt;/jlink&gt;
  </pre>
  
  However, the next example would result in two top-level entries in out.jar,
  namely bar.class and barnone/myClass.zip
  <pre>
  &lt;jlink compress=&quot;false&quot; outfile=&quot;out.jar&quot;/&gt;
     &lt;mergefiles&gt;
       &lt;pathelement path=&quot;build/tempbuild/foo.jar&quot;/&gt;
     &lt;/mergefiles&gt;
  &lt;/jlink&gt;
  </pre>
  
  </body>
  
  </html>
  
  
  
  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
   * <http://www.apache.org/>.
   */
  
  package org.apache.tools.ant.taskdefs.optional.jlink;
  
  import java.io .*;
  
  /**
   * 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{
  
  	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
   * <http://www.apache.org/>.
   */
  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. 
   *
   * <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>
   * &lt;jlink compress=&quot;false&quot; outfile=&quot;out.jar&quot;/&gt;
   *   &lt;mergefiles&gt;
   *     &lt;pathelement path=&quot;${build.dir}/mergefoo.jar&quot;/&gt;
   *     &lt;pathelement path=&quot;${build.dir}/mergebar.jar&quot;/&gt;
   *   &lt;/mergefiles&gt;
   *   &lt;addfiles&gt;
   *     &lt;pathelement path=&quot;${build.dir}/mac.jar&quot;/&gt;
   *     &lt;pathelement path=&quot;${build.dir}/pc.zip&quot;/&gt;
   *   &lt;/addfiles&gt;
   * &lt;/jlink&gt;
   * </pre>
   * </code>
   *
   * @author <a href="mailto:matthew.k.heun@gaerospace.com">Matthew Kuperus Heun</a>
*/
  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
   * <http://www.apache.org/>.
   */
  
  /**
   * jlink.java
   * links together multiple .jar files
   * 
   * Original code by Patrick Beard. Modifications to work
   * with ANT by Matthew Kuperus Heun.
   *
   * @author <a href="mailto:beard@netscape.com>Patrick C. Beard</a>.
   * @author <a href="mailto:matthew.k.heun@gaerospace.com>Matthew Kuperus Heun</a>
   */
  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];
  
  }
  
  
  
  
  

Mime
View raw message