Coverage Report - org.codehaus.plexus.archiver.jar.JarArchiver
 
Classes in this File Line Coverage Branch Coverage Complexity
JarArchiver
62%
143/227
47%
66/140
4.292
JarArchiver$1
75%
3/4
50%
2/4
4.292
JarArchiver$FilesetManifestConfig
0%
0/4
N/A
4.292
 
 1  
 /**
 2  
  *
 3  
  * Copyright 2004 The Apache Software Foundation
 4  
  *
 5  
  * Licensed under the Apache License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  *
 9  
  * http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 package org.codehaus.plexus.archiver.jar;
 18  
 
 19  
 import java.io.ByteArrayInputStream;
 20  
 import java.io.ByteArrayOutputStream;
 21  
 import java.io.File;
 22  
 import java.io.FileInputStream;
 23  
 import java.io.IOException;
 24  
 import java.io.InputStream;
 25  
 import java.io.OutputStreamWriter;
 26  
 import java.io.PrintWriter;
 27  
 import java.util.ArrayList;
 28  
 import java.util.Collections;
 29  
 import java.util.Comparator;
 30  
 import java.util.Enumeration;
 31  
 import java.util.HashSet;
 32  
 import java.util.List;
 33  
 import java.util.Set;
 34  
 import java.util.SortedMap;
 35  
 import java.util.StringTokenizer;
 36  
 import java.util.TreeMap;
 37  
 import java.util.Vector;
 38  
 import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
 39  
 import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
 40  
 import org.apache.commons.compress.parallel.InputStreamSupplier;
 41  
 import org.codehaus.plexus.archiver.ArchiverException;
 42  
 import org.codehaus.plexus.archiver.zip.ConcurrentJarCreator;
 43  
 import org.codehaus.plexus.archiver.zip.ZipArchiver;
 44  
 import org.codehaus.plexus.logging.Logger;
 45  
 import org.codehaus.plexus.logging.console.ConsoleLogger;
 46  
 import org.codehaus.plexus.util.IOUtil;
 47  
 import static org.codehaus.plexus.archiver.util.Streams.bufferedOutputStream;
 48  
 import static org.codehaus.plexus.archiver.util.Streams.fileOutputStream;
 49  
 
 50  
 /**
 51  
  * Base class for tasks that build archives in JAR file format.
 52  
  */
 53  
 @SuppressWarnings(
 54  
 {
 55  
     "NullableProblems"
 56  
 } )
 57  
 public class JarArchiver
 58  
     extends ZipArchiver
 59  
 {
 60  
 
 61  
     /**
 62  
      * the name of the meta-inf dir
 63  
      */
 64  
     private static final String META_INF_NAME = "META-INF";
 65  
 
 66  
     /**
 67  
      * The index file name.
 68  
      */
 69  
     private static final String INDEX_NAME = "META-INF/INDEX.LIST";
 70  
 
 71  
     /**
 72  
      * The manifest file name.
 73  
      */
 74  
     private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
 75  
 
 76  
     /**
 77  
      * merged manifests added through addConfiguredManifest
 78  
      */
 79  
     private Manifest configuredManifest;
 80  
 
 81  
     /**
 82  
      * shadow of the above if upToDate check alters the value
 83  
      */
 84  
     private Manifest savedConfiguredManifest;
 85  
 
 86  
     /**
 87  
      * merged manifests added through filesets
 88  
      */
 89  
     private Manifest filesetManifest;
 90  
 
 91  
     /**
 92  
      * Manifest of original archive, will be set to null if not in
 93  
      * update mode.
 94  
      */
 95  
     private Manifest originalManifest;
 96  
 
 97  
     /**
 98  
      * whether to merge fileset manifests;
 99  
      * value is true if filesetmanifest is 'merge' or 'mergewithoutmain'
 100  
      */
 101  
     private FilesetManifestConfig filesetManifestConfig;
 102  
 
 103  
     /**
 104  
      * whether to merge the main section of fileset manifests;
 105  
      * value is true if filesetmanifest is 'merge'
 106  
      */
 107  31
     private boolean mergeManifestsMain = true;
 108  
 
 109  
     /**
 110  
      * the manifest specified by the 'manifest' attribute *
 111  
      */
 112  
     private Manifest manifest;
 113  
 
 114  
     /**
 115  
      * The file found from the 'manifest' attribute. This can be
 116  
      * either the location of a manifest, or the name of a jar added
 117  
      * through a fileset. If its the name of an added jar, the
 118  
      * manifest is looked for in META-INF/MANIFEST.MF
 119  
      */
 120  
     private File manifestFile;
 121  
 
 122  
     /**
 123  
      * jar index is JDK 1.3+ only
 124  
      */
 125  31
     private boolean index = false;
 126  
 
 127  
     /**
 128  
      * whether to really create the archive in createEmptyZip, will
 129  
      * get set in getResourcesToAdd.
 130  
      */
 131  31
     private boolean createEmpty = false;
 132  
 
 133  
     /**
 134  
      * Stores all files that are in the root of the archive (i.e. that
 135  
      * have a name that doesn't contain a slash) so they can get
 136  
      * listed in the index.
 137  
      * <p/>
 138  
      * Will not be filled unless the user has asked for an index.
 139  
      */
 140  
     private Vector<String> rootEntries;
 141  
 
 142  
     /**
 143  
      * Path containing jars that shall be indexed in addition to this archive.
 144  
      */
 145  
     private ArrayList<String> indexJars;
 146  
 
 147  
     /**
 148  
      * constructor
 149  
      */
 150  
     public JarArchiver()
 151  
     {
 152  31
         super();
 153  31
         archiveType = "jar";
 154  31
         setEncoding( "UTF8" );
 155  31
         rootEntries = new Vector<String>();
 156  31
     }
 157  
 
 158  
     /**
 159  
      * Set whether or not to create an index list for classes.
 160  
      * This may speed up classloading in some cases.
 161  
      *
 162  
      * @param flag true to create an index
 163  
      */
 164  
     public void setIndex( boolean flag )
 165  
     {
 166  2
         index = flag;
 167  2
     }
 168  
 
 169  
     @SuppressWarnings(
 170  
     {
 171  
         "JavaDoc", "UnusedDeclaration"
 172  
     } )
 173  
     @Deprecated // Useless method. Manifests should be UTF-8 by convention. Calling this setter does nothing
 174  
     public void setManifestEncoding( String manifestEncoding )
 175  
     {
 176  0
     }
 177  
 
 178  
     /**
 179  
      * Allows the manifest for the archive file to be provided inline
 180  
      * in the build file rather than in an external file.
 181  
      *
 182  
      * @param newManifest The new manifest
 183  
      *
 184  
      * @throws ManifestException .
 185  
      */
 186  
     public void addConfiguredManifest( Manifest newManifest )
 187  
         throws ManifestException
 188  
     {
 189  3
         if ( configuredManifest == null )
 190  
         {
 191  3
             configuredManifest = newManifest;
 192  
         }
 193  
         else
 194  
         {
 195  0
             JdkManifestFactory.merge( configuredManifest, newManifest, false );
 196  
         }
 197  3
         savedConfiguredManifest = configuredManifest;
 198  3
     }
 199  
 
 200  
     /**
 201  
      * The manifest file to use. This can be either the location of a manifest, or the name of a jar added through a
 202  
      * fileset. If its the name of an added jar, the task expects the manifest to be in the jar at META-INF/MANIFEST.MF.
 203  
      *
 204  
      * @param manifestFile the manifest file to use.
 205  
      *
 206  
      * @throws org.codehaus.plexus.archiver.ArchiverException
 207  
      * .
 208  
      */
 209  
     @SuppressWarnings(
 210  
     {
 211  
         "UnusedDeclaration"
 212  
     } )
 213  
     public void setManifest( File manifestFile )
 214  
         throws ArchiverException
 215  
     {
 216  0
         if ( !manifestFile.exists() )
 217  
         {
 218  0
             throw new ArchiverException( "Manifest file: " + manifestFile + " does not exist." );
 219  
         }
 220  
 
 221  0
         this.manifestFile = manifestFile;
 222  0
     }
 223  
 
 224  
     private Manifest getManifest( File manifestFile )
 225  
         throws ArchiverException
 226  
     {
 227  0
         InputStream in = null;
 228  
         try
 229  
         {
 230  0
             in = new FileInputStream( manifestFile );
 231  0
             final Manifest mf = getManifest( in );
 232  0
             in.close();
 233  0
             in = null;
 234  0
             return mf;
 235  
         }
 236  0
         catch ( IOException e )
 237  
         {
 238  0
             throw new ArchiverException( "Unable to read manifest file: " + manifestFile + " (" + e.getMessage() + ")",
 239  
                                          e );
 240  
         }
 241  
         finally
 242  
         {
 243  0
             IOUtil.close( in );
 244  
         }
 245  
     }
 246  
 
 247  
     private Manifest getManifest( InputStream is )
 248  
         throws ArchiverException
 249  
     {
 250  
         try
 251  
         {
 252  0
             return new Manifest( is );
 253  
         }
 254  0
         catch ( IOException e )
 255  
         {
 256  0
             throw new ArchiverException( "Unable to read manifest file" + " (" + e.getMessage() + ")", e );
 257  
         }
 258  
     }
 259  
 
 260  
     /**
 261  
      * Behavior when a Manifest is found in a zipfileset or zipgroupfileset file.
 262  
      * Valid values are "skip", "merge", and "mergewithoutmain".
 263  
      * "merge" will merge all of manifests together, and merge this into any
 264  
      * other specified manifests.
 265  
      * "mergewithoutmain" merges everything but the Main section of the manifests.
 266  
      * Default value is "skip".
 267  
      * <p>
 268  
      * Note: if this attribute's value is not "skip", the created jar will not
 269  
      * be readable by using java.util.jar.JarInputStream</p>
 270  
      *
 271  
      * @param config setting for found manifest behavior.
 272  
      */
 273  
     @SuppressWarnings(
 274  
     {
 275  
         "UnusedDeclaration"
 276  
     } )
 277  
     public void setFilesetmanifest( FilesetManifestConfig config )
 278  
     {
 279  0
         filesetManifestConfig = config;
 280  0
         mergeManifestsMain = FilesetManifestConfig.merge == config;
 281  
 
 282  0
         if ( ( filesetManifestConfig != null ) && filesetManifestConfig != FilesetManifestConfig.skip )
 283  
         {
 284  
 
 285  0
             doubleFilePass = true;
 286  
         }
 287  0
     }
 288  
 
 289  
     /**
 290  
      * @param indexJar The indexjar
 291  
      */
 292  
     public void addConfiguredIndexJars( File indexJar )
 293  
     {
 294  3
         if ( indexJars == null )
 295  
         {
 296  2
             indexJars = new ArrayList<String>();
 297  
         }
 298  3
         indexJars.add( indexJar.getAbsolutePath() );
 299  3
     }
 300  
 
 301  
     @Override
 302  
     protected void initZipOutputStream( ConcurrentJarCreator zOut )
 303  
         throws ArchiverException, IOException
 304  
     {
 305  17
         if ( !skipWriting )
 306  
         {
 307  17
             Manifest jarManifest = createManifest();
 308  17
             writeManifest( zOut, jarManifest );
 309  
         }
 310  17
     }
 311  
 
 312  
     @Override
 313  
     protected boolean hasVirtualFiles()
 314  
     {
 315  1
         getLogger().debug( "\n\n\nChecking for jar manifest virtual files...\n\n\n" );
 316  1
         System.out.flush();
 317  
 
 318  1
         return ( configuredManifest != null ) || ( manifest != null ) || ( manifestFile != null )
 319  
                    || super.hasVirtualFiles();
 320  
     }
 321  
 
 322  
     /**
 323  
      * Creates the manifest to be added to the JAR archive.
 324  
      * Sub-classes may choose to override this method
 325  
      * in order to inspect or modify the JAR manifest file.
 326  
      *
 327  
      * @return the manifest for the JAR archive.
 328  
      *
 329  
      * @throws ArchiverException
 330  
      */
 331  
     protected Manifest createManifest()
 332  
         throws ArchiverException
 333  
     {
 334  19
         Manifest finalManifest = Manifest.getDefaultManifest();
 335  
 
 336  19
         if ( ( manifest == null ) && ( manifestFile != null ) )
 337  
         {
 338  
             // if we haven't got the manifest yet, attempt to
 339  
             // get it now and have manifest be the final merge
 340  0
             manifest = getManifest( manifestFile );
 341  
         }
 342  
 
 343  
         /*
 344  
          * Precedence: manifestFile wins over inline manifest,
 345  
          * over manifests read from the filesets over the original
 346  
          * manifest.
 347  
          *
 348  
          * merge with null argument is a no-op
 349  
          */
 350  19
         if ( isInUpdateMode() )
 351  
         {
 352  0
             JdkManifestFactory.merge( finalManifest, originalManifest, false );
 353  
         }
 354  19
         JdkManifestFactory.merge( finalManifest, filesetManifest, false );
 355  19
         JdkManifestFactory.merge( finalManifest, configuredManifest, false );
 356  19
         JdkManifestFactory.merge( finalManifest, manifest, !mergeManifestsMain );
 357  
 
 358  19
         return finalManifest;
 359  
     }
 360  
 
 361  
     private void writeManifest( ConcurrentJarCreator zOut, Manifest manifest )
 362  
         throws IOException, ArchiverException
 363  
     {
 364  17
         for ( Enumeration e = manifest.getWarnings(); e.hasMoreElements(); )
 365  
         {
 366  0
             getLogger().warn( "Manifest warning: " + e.nextElement() );
 367  
         }
 368  
 
 369  17
         zipDir( null, zOut, "META-INF/", DEFAULT_DIR_MODE, getEncoding() );
 370  
         // time to write the manifest
 371  17
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 372  17
         manifest.write( baos );
 373  
 
 374  17
         ByteArrayInputStream bais = new ByteArrayInputStream( baos.toByteArray() );
 375  17
         super.zipFile( createInputStreamSupplier( bais ), zOut, MANIFEST_NAME, System.currentTimeMillis(), null,
 376  
                        DEFAULT_FILE_MODE, null, false );
 377  17
         super.initZipOutputStream( zOut );
 378  17
     }
 379  
 
 380  
     @Override
 381  
     protected void finalizeZipOutputStream( ConcurrentJarCreator zOut )
 382  
         throws IOException, ArchiverException
 383  
     {
 384  17
         if ( index )
 385  
         {
 386  2
             createIndexList( zOut );
 387  
         }
 388  17
     }
 389  
 
 390  
     /**
 391  
      * Create the index list to speed up classloading.
 392  
      * This is a JDK 1.3+ specific feature and is enabled by default. See
 393  
      * <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#JAR%20Index">
 394  
      * the JAR index specification</a> for more details.
 395  
      *
 396  
      * @param zOut the zip stream representing the jar being built.
 397  
      *
 398  
      * @throws IOException thrown if there is an error while creating the
 399  
      * index and adding it to the zip stream.
 400  
      * @throws org.codehaus.plexus.archiver.ArchiverException
 401  
      * .
 402  
      */
 403  
     private void createIndexList( ConcurrentJarCreator zOut )
 404  
         throws IOException, ArchiverException
 405  
     {
 406  2
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 407  
         // encoding must be UTF8 as specified in the specs.
 408  2
         PrintWriter writer = new PrintWriter( new OutputStreamWriter( baos, "UTF8" ) );
 409  
 
 410  
         // version-info blankline
 411  2
         writer.println( "JarIndex-Version: 1.0" );
 412  2
         writer.println();
 413  
 
 414  
         // header newline
 415  2
         writer.println( getDestFile().getName() );
 416  
 
 417  
         // filter out META-INF if it doesn't contain anything other than the index and manifest.
 418  
         // this is what sun.misc.JarIndex does, guess we ought to be consistent.
 419  2
         Set<String> filteredDirs = addedDirs.allAddedDirs();
 420  
         // our added dirs always have a trailing slash
 421  2
         if ( filteredDirs.contains( META_INF_NAME + '/' ) )
 422  
         {
 423  2
             boolean add = false;
 424  2
             for ( String entry : entries.keySet() )
 425  
             {
 426  4
                 if ( entry.startsWith( META_INF_NAME + '/' ) && !entry.equals( INDEX_NAME )
 427  
                          && !entry.equals( MANIFEST_NAME ) )
 428  
                 {
 429  0
                     add = true;
 430  0
                     break;
 431  
                 }
 432  4
             }
 433  2
             if ( !add )
 434  
             {
 435  2
                 filteredDirs.remove( META_INF_NAME + '/' );
 436  
             }
 437  
         }
 438  2
         writeIndexLikeList( new ArrayList<String>( filteredDirs ), rootEntries, writer );
 439  2
         writer.println();
 440  
 
 441  2
         if ( indexJars != null )
 442  
         {
 443  2
             java.util.jar.Manifest mf = createManifest();
 444  2
             String classpath = mf.getMainAttributes().getValue( ManifestConstants.ATTRIBUTE_CLASSPATH );
 445  2
             String[] cpEntries = null;
 446  2
             if ( classpath != null )
 447  
             {
 448  2
                 StringTokenizer tok = new StringTokenizer( classpath, " " );
 449  2
                 cpEntries = new String[ tok.countTokens() ];
 450  2
                 int c = 0;
 451  5
                 while ( tok.hasMoreTokens() )
 452  
                 {
 453  3
                     cpEntries[c++] = tok.nextToken();
 454  
                 }
 455  
             }
 456  
 
 457  2
             for ( String indexJar : indexJars )
 458  
             {
 459  3
                 String name = findJarName( indexJar, cpEntries );
 460  3
                 if ( name != null )
 461  
                 {
 462  3
                     ArrayList<String> dirs = new ArrayList<String>();
 463  3
                     ArrayList<String> files = new ArrayList<String>();
 464  3
                     grabFilesAndDirs( indexJar, dirs, files );
 465  3
                     if ( dirs.size() + files.size() > 0 )
 466  
                     {
 467  3
                         writer.println( name );
 468  3
                         writeIndexLikeList( dirs, files, writer );
 469  3
                         writer.println();
 470  
                     }
 471  
                 }
 472  3
             }
 473  
         }
 474  
 
 475  2
         writer.flush();
 476  
 
 477  2
         ByteArrayInputStream bais = new ByteArrayInputStream( baos.toByteArray() );
 478  
 
 479  2
         super.zipFile( createInputStreamSupplier( bais ), zOut, INDEX_NAME, System.currentTimeMillis(), null,
 480  
                        DEFAULT_FILE_MODE, null, true );
 481  2
     }
 482  
 
 483  
     /**
 484  
      * Overridden from Zip class to deal with manifests and index lists.
 485  
      */
 486  
     @Override
 487  
     protected void zipFile( InputStreamSupplier is, ConcurrentJarCreator zOut, String vPath,
 488  
                             long lastModified, File fromArchive,
 489  
                             int mode, String symlinkDestination, boolean addInParallel )
 490  
         throws IOException, ArchiverException
 491  
     {
 492  45032
         if ( MANIFEST_NAME.equalsIgnoreCase( vPath ) )
 493  
         {
 494  0
             if ( !doubleFilePass || skipWriting )
 495  
             {
 496  0
                 try ( InputStream manifestInputStream = is.get() )
 497  
                 {
 498  0
                     filesetManifest( fromArchive, manifestInputStream );
 499  0
                 }
 500  
             }
 501  
         }
 502  45032
         else if ( INDEX_NAME.equalsIgnoreCase( vPath ) && index )
 503  
         {
 504  0
             getLogger().warn( "Warning: selected " + archiveType + " files include a META-INF/INDEX.LIST which will"
 505  
                                   + " be replaced by a newly generated one." );
 506  
         }
 507  
         else
 508  
         {
 509  45032
             if ( index && ( !vPath.contains( "/" ) ) )
 510  
             {
 511  2
                 rootEntries.addElement( vPath );
 512  
             }
 513  45032
             super.zipFile( is, zOut, vPath, lastModified, fromArchive, mode, symlinkDestination, addInParallel );
 514  
         }
 515  45032
     }
 516  
 
 517  
     private void filesetManifest( File file, InputStream is )
 518  
         throws ArchiverException
 519  
     {
 520  0
         if ( ( manifestFile != null ) && manifestFile.equals( file ) )
 521  
         {
 522  
             // If this is the same name specified in 'manifest', this
 523  
             // is the manifest to use
 524  0
             getLogger().debug( "Found manifest " + file );
 525  0
             if ( is != null )
 526  
             {
 527  0
                 manifest = getManifest( is );
 528  
             }
 529  
             else
 530  
             {
 531  0
                 manifest = getManifest( file );
 532  
             }
 533  
         }
 534  0
         else if ( ( filesetManifestConfig != null ) && filesetManifestConfig != FilesetManifestConfig.skip )
 535  
         {
 536  
             // we add this to our group of fileset manifests
 537  0
             getLogger().debug( "Found manifest to merge in file " + file );
 538  
 
 539  
             Manifest newManifest;
 540  0
             if ( is != null )
 541  
             {
 542  0
                 newManifest = getManifest( is );
 543  
             }
 544  
             else
 545  
             {
 546  0
                 newManifest = getManifest( file );
 547  
             }
 548  
 
 549  0
             if ( filesetManifest == null )
 550  
             {
 551  0
                 filesetManifest = newManifest;
 552  
             }
 553  
             else
 554  
             {
 555  0
                 JdkManifestFactory.merge( filesetManifest, newManifest, false );
 556  
             }
 557  
         }
 558  0
     }
 559  
 
 560  
     @Override
 561  
     protected boolean createEmptyZip( File zipFile )
 562  
         throws ArchiverException
 563  
     {
 564  0
         if ( !createEmpty )
 565  
         {
 566  0
             return true;
 567  
         }
 568  
 
 569  
         try
 570  
         {
 571  0
             getLogger().debug( "Building MANIFEST-only jar: " + getDestFile().getAbsolutePath() );
 572  0
             zipArchiveOutputStream =
 573  
                 new ZipArchiveOutputStream( bufferedOutputStream( fileOutputStream( getDestFile(), "jar" ) ) );
 574  
 
 575  0
             zipArchiveOutputStream.setEncoding( getEncoding() );
 576  0
             if ( isCompress() )
 577  
             {
 578  0
                 zipArchiveOutputStream.setMethod( ZipArchiveOutputStream.DEFLATED );
 579  
             }
 580  
             else
 581  
             {
 582  0
                 zipArchiveOutputStream.setMethod( ZipArchiveOutputStream.STORED );
 583  
             }
 584  0
             ConcurrentJarCreator ps =
 585  
                 new ConcurrentJarCreator( isRecompressAddedZips(), Runtime.getRuntime().availableProcessors() );
 586  0
             initZipOutputStream( ps );
 587  0
             finalizeZipOutputStream( ps );
 588  
         }
 589  0
         catch ( IOException ioe )
 590  
         {
 591  0
             throw new ArchiverException( "Could not create almost empty JAR archive (" + ioe.getMessage() + ")", ioe );
 592  
         }
 593  
         finally
 594  
         {
 595  
             // Close the output stream.
 596  
             //IOUtil.close( zOut );
 597  0
             createEmpty = false;
 598  0
         }
 599  0
         return true;
 600  
     }
 601  
 
 602  
     /**
 603  
      * Make sure we don't think we already have a MANIFEST next time this task
 604  
      * gets executed.
 605  
      *
 606  
      * @see ZipArchiver#cleanUp
 607  
      */
 608  
     @Override
 609  
     protected void cleanUp()
 610  
         throws IOException
 611  
     {
 612  18
         super.cleanUp();
 613  
 
 614  
         // we want to save this info if we are going to make another pass
 615  18
         if ( !doubleFilePass || !skipWriting )
 616  
         {
 617  18
             manifest = null;
 618  18
             configuredManifest = savedConfiguredManifest;
 619  18
             filesetManifest = null;
 620  18
             originalManifest = null;
 621  
         }
 622  18
         rootEntries.removeAllElements();
 623  18
     }
 624  
 
 625  
     /**
 626  
      * reset to default values.
 627  
      *
 628  
      * @see ZipArchiver#reset
 629  
      */
 630  
     @Override
 631  
     public void reset()
 632  
     {
 633  0
         super.reset();
 634  0
         configuredManifest = null;
 635  0
         filesetManifestConfig = null;
 636  0
         mergeManifestsMain = false;
 637  0
         manifestFile = null;
 638  0
         index = false;
 639  0
     }
 640  
 
 641  0
     public enum FilesetManifestConfig
 642  
     {
 643  
 
 644  0
         skip,
 645  0
         merge,
 646  0
         mergewithoutmain
 647  
 
 648  
     }
 649  
 
 650  
     /**
 651  
      * Writes the directory entries from the first and the filenames
 652  
      * from the second list to the given writer, one entry per line.
 653  
      *
 654  
      * @param dirs The directories
 655  
      * @param files The files
 656  
      * @param writer The printwriter ;)
 657  
      */
 658  
     protected final void writeIndexLikeList( List<String> dirs, List<String> files, PrintWriter writer )
 659  
     {
 660  
         // JarIndex is sorting the directories by ascending order.
 661  
         // it has no value but cosmetic since it will be read into a
 662  
         // hashtable by the classloader, but we'll do so anyway.
 663  5
         Collections.sort( dirs );
 664  5
         Collections.sort( files );
 665  5
         for ( String dir : dirs )
 666  
         {
 667  
             // try to be smart, not to be fooled by a weird directory name
 668  4
             dir = dir.replace( '\\', '/' );
 669  4
             if ( dir.startsWith( "./" ) )
 670  
             {
 671  0
                 dir = dir.substring( 2 );
 672  
             }
 673  4
             while ( dir.startsWith( "/" ) )
 674  
             {
 675  0
                 dir = dir.substring( 1 );
 676  
             }
 677  4
             int pos = dir.lastIndexOf( '/' );
 678  4
             if ( pos != -1 )
 679  
             {
 680  4
                 dir = dir.substring( 0, pos );
 681  
             }
 682  
 
 683  
             // name newline
 684  4
             writer.println( dir );
 685  4
         }
 686  
 
 687  5
         for ( String file : files )
 688  
         {
 689  4
             writer.println( file );
 690  4
         }
 691  5
     }
 692  
 
 693  
     /**
 694  
      * try to guess the name of the given file.
 695  
      * <p>
 696  
      * If this jar has a classpath attribute in its manifest, we
 697  
      * can assume that it will only require an index of jars listed
 698  
      * there. try to find which classpath entry is most likely the
 699  
      * one the given file name points to.</p>
 700  
      * <p>
 701  
      * In the absence of a classpath attribute, assume the other
 702  
      * files will be placed inside the same directory as this jar and
 703  
      * use their basename.</p>
 704  
      * <p>
 705  
      * if there is a classpath and the given file doesn't match any
 706  
      * of its entries, return null.</p>
 707  
      *
 708  
      * @param fileName .
 709  
      * @param classpath .
 710  
      *
 711  
      * @return The guessed name
 712  
      */
 713  
     protected static String findJarName( String fileName, String[] classpath )
 714  
     {
 715  3
         if ( classpath == null )
 716  
         {
 717  0
             return new File( fileName ).getName();
 718  
         }
 719  3
         fileName = fileName.replace( File.separatorChar, '/' );
 720  3
         SortedMap<String, String> matches = new TreeMap<String, String>( new Comparator<String>()
 721  9
         {
 722  
 
 723  
             // longest match comes first
 724  
             @Override
 725  
             public int compare( String o1, String o2 )
 726  
             {
 727  6
                 if ( ( o1 != null ) && ( o2 != null ) )
 728  
                 {
 729  6
                     return o2.length() - o1.length();
 730  
                 }
 731  0
                 return 0;
 732  
             }
 733  
 
 734  
         } );
 735  
 
 736  8
         for ( String aClasspath : classpath )
 737  
         {
 738  5
             if ( fileName.endsWith( aClasspath ) )
 739  
             {
 740  3
                 matches.put( aClasspath, aClasspath );
 741  
             }
 742  
             else
 743  
             {
 744  2
                 int slash = aClasspath.indexOf( "/" );
 745  2
                 String candidate = aClasspath;
 746  2
                 while ( slash > -1 )
 747  
                 {
 748  0
                     candidate = candidate.substring( slash + 1 );
 749  0
                     if ( fileName.endsWith( candidate ) )
 750  
                     {
 751  0
                         matches.put( candidate, aClasspath );
 752  0
                         break;
 753  
                     }
 754  0
                     slash = candidate.indexOf( "/" );
 755  
                 }
 756  
             }
 757  
         }
 758  
 
 759  3
         return matches.size() == 0 ? null : matches.get( matches.firstKey() );
 760  
     }
 761  
 
 762  
     /**
 763  
      * Grab lists of all root-level files and all directories
 764  
      * contained in the given archive.
 765  
      *
 766  
      * @param file .
 767  
      * @param files .
 768  
      * @param dirs .
 769  
      *
 770  
      * @throws java.io.IOException .
 771  
      */
 772  
     protected static void grabFilesAndDirs( String file, List<String> dirs, List<String> files )
 773  
         throws IOException
 774  
     {
 775  3
         File zipFile = new File( file );
 776  3
         if ( !zipFile.exists() )
 777  
         {
 778  0
             Logger logger = new ConsoleLogger( Logger.LEVEL_INFO, "console" );
 779  0
             logger.error( "JarArchive skipping non-existing file: " + zipFile.getAbsolutePath() );
 780  0
         }
 781  3
         else if ( zipFile.isDirectory() )
 782  
         {
 783  0
             Logger logger = new ConsoleLogger( Logger.LEVEL_INFO, "console" );
 784  0
             logger.info( "JarArchiver skipping indexJar " + zipFile + " because it is not a jar" );
 785  0
         }
 786  
         else
 787  
         {
 788  3
             org.apache.commons.compress.archivers.zip.ZipFile zf = null;
 789  
             try
 790  
             {
 791  3
                 zf = new org.apache.commons.compress.archivers.zip.ZipFile( file, "utf-8" );
 792  3
                 Enumeration<ZipArchiveEntry> entries = zf.getEntries();
 793  3
                 HashSet<String> dirSet = new HashSet<String>();
 794  17
                 while ( entries.hasMoreElements() )
 795  
                 {
 796  14
                     ZipArchiveEntry ze = entries.nextElement();
 797  14
                     String name = ze.getName();
 798  
                     // avoid index for manifest-only jars.
 799  14
                     if ( !name.equals( META_INF_NAME ) && !name.equals( META_INF_NAME + '/' ) && !name.equals(
 800  
                         INDEX_NAME ) && !name.equals( MANIFEST_NAME ) )
 801  
                     {
 802  7
                         if ( ze.isDirectory() )
 803  
                         {
 804  3
                             dirSet.add( name );
 805  
                         }
 806  4
                         else if ( !name.contains( "/" ) )
 807  
                         {
 808  2
                             files.add( name );
 809  
                         }
 810  
                         else
 811  
                         {
 812  
                             // a file, not in the root
 813  
                             // since the jar may be one without directory
 814  
                             // entries, add the parent dir of this file as
 815  
                             // well.
 816  2
                             dirSet.add( name.substring( 0, name.lastIndexOf( "/" ) + 1 ) );
 817  
                         }
 818  
                     }
 819  14
                 }
 820  3
                 dirs.addAll( dirSet );
 821  
             }
 822  
             finally
 823  
             {
 824  3
                 if ( zf != null )
 825  
                 {
 826  3
                     zf.close();
 827  
                 }
 828  
             }
 829  
         }
 830  3
     }
 831  
 
 832  
 }