Coverage Report - org.codehaus.plexus.archiver.jar.JarArchiver
 
Classes in this File Line Coverage Branch Coverage Complexity
JarArchiver
63%
143/225
48%
66/136
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  19
     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  19
     private boolean index = false;
 126  
 
 127  
     /**
 128  
      * whether to really create the archive in createEmptyZip, will
 129  
      * get set in getResourcesToAdd.
 130  
      */
 131  19
     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  19
         super();
 153  19
         archiveType = "jar";
 154  19
         setEncoding( "UTF8" );
 155  19
         rootEntries = new Vector<String>();
 156  19
     }
 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
 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  14
         if ( !skipWriting )
 306  
         {
 307  14
             Manifest jarManifest = createManifest();
 308  14
             writeManifest( zOut, jarManifest );
 309  
         }
 310  14
     }
 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  
     private Manifest createManifest()
 323  
         throws ArchiverException
 324  
     {
 325  16
         Manifest finalManifest = Manifest.getDefaultManifest();
 326  
 
 327  16
         if ( ( manifest == null ) && ( manifestFile != null ) )
 328  
         {
 329  
             // if we haven't got the manifest yet, attempt to
 330  
             // get it now and have manifest be the final merge
 331  0
             manifest = getManifest( manifestFile );
 332  
         }
 333  
 
 334  
         /*
 335  
          * Precedence: manifestFile wins over inline manifest,
 336  
          * over manifests read from the filesets over the original
 337  
          * manifest.
 338  
          *
 339  
          * merge with null argument is a no-op
 340  
          */
 341  16
         if ( isInUpdateMode() )
 342  
         {
 343  0
             JdkManifestFactory.merge( finalManifest, originalManifest, false );
 344  
         }
 345  16
         JdkManifestFactory.merge( finalManifest, filesetManifest, false );
 346  16
         JdkManifestFactory.merge( finalManifest, configuredManifest, false );
 347  16
         JdkManifestFactory.merge( finalManifest, manifest, !mergeManifestsMain );
 348  
 
 349  16
         return finalManifest;
 350  
     }
 351  
 
 352  
     private void writeManifest( ConcurrentJarCreator zOut, Manifest manifest )
 353  
         throws IOException, ArchiverException
 354  
     {
 355  14
         for ( Enumeration e = manifest.getWarnings(); e.hasMoreElements(); )
 356  
         {
 357  0
             getLogger().warn( "Manifest warning: " + e.nextElement() );
 358  
         }
 359  
 
 360  14
         zipDir( null, zOut, "META-INF/", DEFAULT_DIR_MODE, getEncoding() );
 361  
         // time to write the manifest
 362  14
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 363  14
         manifest.write( baos );
 364  
 
 365  14
         ByteArrayInputStream bais = new ByteArrayInputStream( baos.toByteArray() );
 366  14
         super.zipFile( createInputStreamSupplier( bais ), zOut, MANIFEST_NAME, System.currentTimeMillis(), null,
 367  
                        DEFAULT_FILE_MODE, null, false );
 368  14
         super.initZipOutputStream( zOut );
 369  14
     }
 370  
 
 371  
     @Override
 372  
     protected void finalizeZipOutputStream( ConcurrentJarCreator zOut )
 373  
         throws IOException, ArchiverException
 374  
     {
 375  14
         if ( index )
 376  
         {
 377  2
             createIndexList( zOut );
 378  
         }
 379  14
     }
 380  
 
 381  
     /**
 382  
      * Create the index list to speed up classloading.
 383  
      * This is a JDK 1.3+ specific feature and is enabled by default. See
 384  
      * <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#JAR%20Index">
 385  
      * the JAR index specification</a> for more details.
 386  
      *
 387  
      * @param zOut the zip stream representing the jar being built.
 388  
      *
 389  
      * @throws IOException thrown if there is an error while creating the
 390  
      * index and adding it to the zip stream.
 391  
      * @throws org.codehaus.plexus.archiver.ArchiverException
 392  
      * .
 393  
      */
 394  
     private void createIndexList( ConcurrentJarCreator zOut )
 395  
         throws IOException, ArchiverException
 396  
     {
 397  2
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 398  
         // encoding must be UTF8 as specified in the specs.
 399  2
         PrintWriter writer = new PrintWriter( new OutputStreamWriter( baos, "UTF8" ) );
 400  
 
 401  
         // version-info blankline
 402  2
         writer.println( "JarIndex-Version: 1.0" );
 403  2
         writer.println();
 404  
 
 405  
         // header newline
 406  2
         writer.println( getDestFile().getName() );
 407  
 
 408  
         // filter out META-INF if it doesn't contain anything other than the index and manifest.
 409  
         // this is what sun.misc.JarIndex does, guess we ought to be consistent.
 410  2
         Set<String> filteredDirs = addedDirs.allAddedDirs();
 411  
         // our added dirs always have a trailing slash
 412  2
         if ( filteredDirs.contains( META_INF_NAME + '/' ) )
 413  
         {
 414  2
             boolean add = false;
 415  2
             for ( String entry : entries.keySet() )
 416  
             {
 417  4
                 if ( entry.startsWith( META_INF_NAME + '/' ) && !entry.equals( INDEX_NAME )
 418  
                          && !entry.equals( MANIFEST_NAME ) )
 419  
                 {
 420  0
                     add = true;
 421  0
                     break;
 422  
                 }
 423  4
             }
 424  2
             if ( !add )
 425  
             {
 426  2
                 filteredDirs.remove( META_INF_NAME + '/' );
 427  
             }
 428  
         }
 429  2
         writeIndexLikeList( new ArrayList<String>( filteredDirs ), rootEntries, writer );
 430  2
         writer.println();
 431  
 
 432  2
         if ( indexJars != null )
 433  
         {
 434  2
             java.util.jar.Manifest mf = createManifest();
 435  2
             String classpath = mf.getMainAttributes().getValue( ManifestConstants.ATTRIBUTE_CLASSPATH );
 436  2
             String[] cpEntries = null;
 437  2
             if ( classpath != null )
 438  
             {
 439  2
                 StringTokenizer tok = new StringTokenizer( classpath, " " );
 440  2
                 cpEntries = new String[ tok.countTokens() ];
 441  2
                 int c = 0;
 442  5
                 while ( tok.hasMoreTokens() )
 443  
                 {
 444  3
                     cpEntries[c++] = tok.nextToken();
 445  
                 }
 446  
             }
 447  
 
 448  2
             for ( String indexJar : indexJars )
 449  
             {
 450  3
                 String name = findJarName( indexJar, cpEntries );
 451  3
                 if ( name != null )
 452  
                 {
 453  3
                     ArrayList<String> dirs = new ArrayList<String>();
 454  3
                     ArrayList<String> files = new ArrayList<String>();
 455  3
                     grabFilesAndDirs( indexJar, dirs, files );
 456  3
                     if ( dirs.size() + files.size() > 0 )
 457  
                     {
 458  3
                         writer.println( name );
 459  3
                         writeIndexLikeList( dirs, files, writer );
 460  3
                         writer.println();
 461  
                     }
 462  
                 }
 463  3
             }
 464  
         }
 465  
 
 466  2
         writer.flush();
 467  
 
 468  2
         ByteArrayInputStream bais = new ByteArrayInputStream( baos.toByteArray() );
 469  
 
 470  2
         super.zipFile( createInputStreamSupplier( bais ), zOut, INDEX_NAME, System.currentTimeMillis(), null,
 471  
                        DEFAULT_FILE_MODE, null, true );
 472  2
     }
 473  
 
 474  
     /**
 475  
      * Overridden from Zip class to deal with manifests and index lists.
 476  
      */
 477  
     @Override
 478  
     protected void zipFile( InputStreamSupplier is, ConcurrentJarCreator zOut, String vPath,
 479  
                             long lastModified, File fromArchive,
 480  
                             int mode, String symlinkDestination, boolean addInParallel )
 481  
         throws IOException, ArchiverException
 482  
     {
 483  45025
         if ( MANIFEST_NAME.equalsIgnoreCase( vPath ) )
 484  
         {
 485  0
             if ( !doubleFilePass || skipWriting )
 486  
             {
 487  0
                 filesetManifest( fromArchive, is.get() );
 488  
             }
 489  
         }
 490  45025
         else if ( INDEX_NAME.equalsIgnoreCase( vPath ) && index )
 491  
         {
 492  0
             getLogger().warn( "Warning: selected " + archiveType + " files include a META-INF/INDEX.LIST which will"
 493  
                                   + " be replaced by a newly generated one." );
 494  
         }
 495  
         else
 496  
         {
 497  45025
             if ( index && ( !vPath.contains( "/" ) ) )
 498  
             {
 499  2
                 rootEntries.addElement( vPath );
 500  
             }
 501  45025
             super.zipFile( is, zOut, vPath, lastModified, fromArchive, mode, symlinkDestination, addInParallel );
 502  
         }
 503  45025
     }
 504  
 
 505  
     private void filesetManifest( File file, InputStream is )
 506  
         throws ArchiverException
 507  
     {
 508  0
         if ( ( manifestFile != null ) && manifestFile.equals( file ) )
 509  
         {
 510  
             // If this is the same name specified in 'manifest', this
 511  
             // is the manifest to use
 512  0
             getLogger().debug( "Found manifest " + file );
 513  0
             if ( is != null )
 514  
             {
 515  0
                 manifest = getManifest( is );
 516  
             }
 517  
             else
 518  
             {
 519  0
                 manifest = getManifest( file );
 520  
             }
 521  
         }
 522  0
         else if ( ( filesetManifestConfig != null ) && filesetManifestConfig != FilesetManifestConfig.skip )
 523  
         {
 524  
             // we add this to our group of fileset manifests
 525  0
             getLogger().debug( "Found manifest to merge in file " + file );
 526  
 
 527  
             Manifest newManifest;
 528  0
             if ( is != null )
 529  
             {
 530  0
                 newManifest = getManifest( is );
 531  
             }
 532  
             else
 533  
             {
 534  0
                 newManifest = getManifest( file );
 535  
             }
 536  
 
 537  0
             if ( filesetManifest == null )
 538  
             {
 539  0
                 filesetManifest = newManifest;
 540  
             }
 541  
             else
 542  
             {
 543  0
                 JdkManifestFactory.merge( filesetManifest, newManifest, false );
 544  
             }
 545  
         }
 546  0
     }
 547  
 
 548  
     @Override
 549  
     protected boolean createEmptyZip( File zipFile )
 550  
         throws ArchiverException
 551  
     {
 552  0
         if ( !createEmpty )
 553  
         {
 554  0
             return true;
 555  
         }
 556  
 
 557  
         try
 558  
         {
 559  0
             getLogger().debug( "Building MANIFEST-only jar: " + getDestFile().getAbsolutePath() );
 560  0
             zipArchiveOutputStream =
 561  
                 new ZipArchiveOutputStream( bufferedOutputStream( fileOutputStream( getDestFile(), "jar" ) ) );
 562  
 
 563  0
             zipArchiveOutputStream.setEncoding( getEncoding() );
 564  0
             if ( isCompress() )
 565  
             {
 566  0
                 zipArchiveOutputStream.setMethod( ZipArchiveOutputStream.DEFLATED );
 567  
             }
 568  
             else
 569  
             {
 570  0
                 zipArchiveOutputStream.setMethod( ZipArchiveOutputStream.STORED );
 571  
             }
 572  0
             ConcurrentJarCreator ps =
 573  
                 new ConcurrentJarCreator( isRecompressAddedZips(), Runtime.getRuntime().availableProcessors() );
 574  0
             initZipOutputStream( ps );
 575  0
             finalizeZipOutputStream( ps );
 576  
         }
 577  0
         catch ( IOException ioe )
 578  
         {
 579  0
             throw new ArchiverException( "Could not create almost empty JAR archive (" + ioe.getMessage() + ")", ioe );
 580  
         }
 581  
         finally
 582  
         {
 583  
             // Close the output stream.
 584  
             //IOUtil.close( zOut );
 585  0
             createEmpty = false;
 586  0
         }
 587  0
         return true;
 588  
     }
 589  
 
 590  
     /**
 591  
      * Make sure we don't think we already have a MANIFEST next time this task
 592  
      * gets executed.
 593  
      *
 594  
      * @see ZipArchiver#cleanUp
 595  
      */
 596  
     @Override
 597  
     protected void cleanUp()
 598  
         throws IOException
 599  
     {
 600  15
         super.cleanUp();
 601  
 
 602  
         // we want to save this info if we are going to make another pass
 603  15
         if ( !doubleFilePass || !skipWriting )
 604  
         {
 605  15
             manifest = null;
 606  15
             configuredManifest = savedConfiguredManifest;
 607  15
             filesetManifest = null;
 608  15
             originalManifest = null;
 609  
         }
 610  15
         rootEntries.removeAllElements();
 611  15
     }
 612  
 
 613  
     /**
 614  
      * reset to default values.
 615  
      *
 616  
      * @see ZipArchiver#reset
 617  
      */
 618  
     @Override
 619  
     public void reset()
 620  
     {
 621  0
         super.reset();
 622  0
         configuredManifest = null;
 623  0
         filesetManifestConfig = null;
 624  0
         mergeManifestsMain = false;
 625  0
         manifestFile = null;
 626  0
         index = false;
 627  0
     }
 628  
 
 629  0
     public enum FilesetManifestConfig
 630  
     {
 631  
 
 632  0
         skip,
 633  0
         merge,
 634  0
         mergewithoutmain
 635  
 
 636  
     }
 637  
 
 638  
     /**
 639  
      * Writes the directory entries from the first and the filenames
 640  
      * from the second list to the given writer, one entry per line.
 641  
      *
 642  
      * @param dirs The directories
 643  
      * @param files The files
 644  
      * @param writer The printwriter ;)
 645  
      */
 646  
     protected final void writeIndexLikeList( List<String> dirs, List<String> files, PrintWriter writer )
 647  
     {
 648  
         // JarIndex is sorting the directories by ascending order.
 649  
         // it has no value but cosmetic since it will be read into a
 650  
         // hashtable by the classloader, but we'll do so anyway.
 651  5
         Collections.sort( dirs );
 652  5
         Collections.sort( files );
 653  5
         for ( String dir : dirs )
 654  
         {
 655  
             // try to be smart, not to be fooled by a weird directory name
 656  4
             dir = dir.replace( '\\', '/' );
 657  4
             if ( dir.startsWith( "./" ) )
 658  
             {
 659  0
                 dir = dir.substring( 2 );
 660  
             }
 661  4
             while ( dir.startsWith( "/" ) )
 662  
             {
 663  0
                 dir = dir.substring( 1 );
 664  
             }
 665  4
             int pos = dir.lastIndexOf( '/' );
 666  4
             if ( pos != -1 )
 667  
             {
 668  4
                 dir = dir.substring( 0, pos );
 669  
             }
 670  
 
 671  
             // name newline
 672  4
             writer.println( dir );
 673  4
         }
 674  
 
 675  5
         for ( String file : files )
 676  
         {
 677  4
             writer.println( file );
 678  4
         }
 679  5
     }
 680  
 
 681  
     /**
 682  
      * try to guess the name of the given file.
 683  
      * <p/>
 684  
      * <p>
 685  
      * If this jar has a classpath attribute in its manifest, we
 686  
      * can assume that it will only require an index of jars listed
 687  
      * there. try to find which classpath entry is most likely the
 688  
      * one the given file name points to.</p>
 689  
      * <p/>
 690  
      * <p>
 691  
      * In the absence of a classpath attribute, assume the other
 692  
      * files will be placed inside the same directory as this jar and
 693  
      * use their basename.</p>
 694  
      * <p/>
 695  
      * <p>
 696  
      * if there is a classpath and the given file doesn't match any
 697  
      * of its entries, return null.</p>
 698  
      *
 699  
      * @param fileName .
 700  
      * @param classpath .
 701  
      *
 702  
      * @return The guessed name
 703  
      */
 704  
     protected static String findJarName( String fileName, String[] classpath )
 705  
     {
 706  3
         if ( classpath == null )
 707  
         {
 708  0
             return new File( fileName ).getName();
 709  
         }
 710  3
         fileName = fileName.replace( File.separatorChar, '/' );
 711  3
         SortedMap<String, String> matches = new TreeMap<String, String>( new Comparator<String>()
 712  9
         {
 713  
 
 714  
             // longest match comes first
 715  
             @Override
 716  
             public int compare( String o1, String o2 )
 717  
             {
 718  6
                 if ( ( o1 != null ) && ( o2 != null ) )
 719  
                 {
 720  6
                     return o2.length() - o1.length();
 721  
                 }
 722  0
                 return 0;
 723  
             }
 724  
 
 725  
         } );
 726  
 
 727  8
         for ( String aClasspath : classpath )
 728  
         {
 729  5
             if ( fileName.endsWith( aClasspath ) )
 730  
             {
 731  3
                 matches.put( aClasspath, aClasspath );
 732  
             }
 733  
             else
 734  
             {
 735  2
                 int slash = aClasspath.indexOf( "/" );
 736  2
                 String candidate = aClasspath;
 737  2
                 while ( slash > -1 )
 738  
                 {
 739  0
                     candidate = candidate.substring( slash + 1 );
 740  0
                     if ( fileName.endsWith( candidate ) )
 741  
                     {
 742  0
                         matches.put( candidate, aClasspath );
 743  0
                         break;
 744  
                     }
 745  0
                     slash = candidate.indexOf( "/" );
 746  
                 }
 747  
             }
 748  
         }
 749  
 
 750  3
         return matches.size() == 0 ? null : matches.get( matches.firstKey() );
 751  
     }
 752  
 
 753  
     /**
 754  
      * Grab lists of all root-level files and all directories
 755  
      * contained in the given archive.
 756  
      *
 757  
      * @param file .
 758  
      * @param files .
 759  
      * @param dirs .
 760  
      *
 761  
      * @throws java.io.IOException .
 762  
      */
 763  
     protected static void grabFilesAndDirs( String file, List<String> dirs, List<String> files )
 764  
         throws IOException
 765  
     {
 766  3
         File zipFile = new File( file );
 767  3
         if ( !zipFile.exists() )
 768  
         {
 769  0
             Logger logger = new ConsoleLogger( Logger.LEVEL_INFO, "console" );
 770  0
             logger.error( "JarArchive skipping non-existing file: " + zipFile.getAbsolutePath() );
 771  0
         }
 772  3
         else if ( zipFile.isDirectory() )
 773  
         {
 774  0
             Logger logger = new ConsoleLogger( Logger.LEVEL_INFO, "console" );
 775  0
             logger.info( "JarArchiver skipping indexJar " + zipFile + " because it is not a jar" );
 776  0
         }
 777  
         else
 778  
         {
 779  3
             org.apache.commons.compress.archivers.zip.ZipFile zf = null;
 780  
             try
 781  
             {
 782  3
                 zf = new org.apache.commons.compress.archivers.zip.ZipFile( file, "utf-8" );
 783  3
                 Enumeration<ZipArchiveEntry> entries = zf.getEntries();
 784  3
                 HashSet<String> dirSet = new HashSet<String>();
 785  17
                 while ( entries.hasMoreElements() )
 786  
                 {
 787  14
                     ZipArchiveEntry ze = entries.nextElement();
 788  14
                     String name = ze.getName();
 789  
                     // avoid index for manifest-only jars.
 790  14
                     if ( !name.equals( META_INF_NAME ) && !name.equals( META_INF_NAME + '/' ) && !name.equals(
 791  
                         INDEX_NAME ) && !name.equals( MANIFEST_NAME ) )
 792  
                     {
 793  7
                         if ( ze.isDirectory() )
 794  
                         {
 795  3
                             dirSet.add( name );
 796  
                         }
 797  4
                         else if ( !name.contains( "/" ) )
 798  
                         {
 799  2
                             files.add( name );
 800  
                         }
 801  
                         else
 802  
                         {
 803  
                             // a file, not in the root
 804  
                             // since the jar may be one without directory
 805  
                             // entries, add the parent dir of this file as
 806  
                             // well.
 807  2
                             dirSet.add( name.substring( 0, name.lastIndexOf( "/" ) + 1 ) );
 808  
                         }
 809  
                     }
 810  14
                 }
 811  3
                 dirs.addAll( dirSet );
 812  
             }
 813  
             finally
 814  
             {
 815  3
                 if ( zf != null )
 816  
                 {
 817  3
                     zf.close();
 818  
                 }
 819  
             }
 820  
         }
 821  3
     }
 822  
 
 823  
 }