Coverage Report - org.apache.maven.plugin.javadoc.JavadocUtil
 
Classes in this File Line Coverage Branch Coverage Complexity
JavadocUtil
77 %
327/427
56 %
181/326
6,242
JavadocUtil$PathTokenizer
50 %
19/38
31 %
13/42
6,242
 
 1  
 package org.apache.maven.plugin.javadoc;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *   http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 import java.io.ByteArrayOutputStream;
 23  
 import java.io.File;
 24  
 import java.io.FileInputStream;
 25  
 import java.io.FileNotFoundException;
 26  
 import java.io.FileOutputStream;
 27  
 import java.io.IOException;
 28  
 import java.io.InputStream;
 29  
 import java.io.OutputStream;
 30  
 import java.io.OutputStreamWriter;
 31  
 import java.io.PrintStream;
 32  
 import java.io.UnsupportedEncodingException;
 33  
 import java.lang.reflect.Modifier;
 34  
 import java.net.URL;
 35  
 import java.net.URLClassLoader;
 36  
 import java.util.ArrayList;
 37  
 import java.util.Arrays;
 38  
 import java.util.Iterator;
 39  
 import java.util.List;
 40  
 import java.util.Locale;
 41  
 import java.util.NoSuchElementException;
 42  
 import java.util.Properties;
 43  
 import java.util.Set;
 44  
 import java.util.StringTokenizer;
 45  
 import java.util.jar.JarEntry;
 46  
 import java.util.jar.JarInputStream;
 47  
 import java.util.regex.Matcher;
 48  
 import java.util.regex.Pattern;
 49  
 import java.util.regex.PatternSyntaxException;
 50  
 
 51  
 import org.apache.commons.httpclient.Credentials;
 52  
 import org.apache.commons.httpclient.HttpClient;
 53  
 import org.apache.commons.httpclient.HttpStatus;
 54  
 import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
 55  
 import org.apache.commons.httpclient.UsernamePasswordCredentials;
 56  
 import org.apache.commons.httpclient.auth.AuthScope;
 57  
 import org.apache.commons.httpclient.methods.GetMethod;
 58  
 import org.apache.commons.httpclient.params.HttpClientParams;
 59  
 import org.apache.commons.httpclient.params.HttpMethodParams;
 60  
 import org.apache.commons.lang.SystemUtils;
 61  
 import org.apache.maven.artifact.Artifact;
 62  
 import org.apache.maven.plugin.logging.Log;
 63  
 import org.apache.maven.project.MavenProject;
 64  
 import org.apache.maven.settings.Proxy;
 65  
 import org.apache.maven.settings.Settings;
 66  
 import org.apache.maven.shared.invoker.DefaultInvocationRequest;
 67  
 import org.apache.maven.shared.invoker.DefaultInvoker;
 68  
 import org.apache.maven.shared.invoker.InvocationOutputHandler;
 69  
 import org.apache.maven.shared.invoker.InvocationRequest;
 70  
 import org.apache.maven.shared.invoker.InvocationResult;
 71  
 import org.apache.maven.shared.invoker.Invoker;
 72  
 import org.apache.maven.shared.invoker.MavenInvocationException;
 73  
 import org.apache.maven.shared.invoker.PrintStreamHandler;
 74  
 import org.apache.maven.wagon.proxy.ProxyInfo;
 75  
 import org.apache.maven.wagon.proxy.ProxyUtils;
 76  
 import org.codehaus.plexus.util.FileUtils;
 77  
 import org.codehaus.plexus.util.IOUtil;
 78  
 import org.codehaus.plexus.util.Os;
 79  
 import org.codehaus.plexus.util.StringUtils;
 80  
 import org.codehaus.plexus.util.cli.CommandLineException;
 81  
 import org.codehaus.plexus.util.cli.CommandLineUtils;
 82  
 import org.codehaus.plexus.util.cli.Commandline;
 83  
 
 84  
 /**
 85  
  * Set of utilities methods for Javadoc.
 86  
  *
 87  
  * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
 88  
  * @version $Id$
 89  
  * @since 2.4
 90  
  */
 91  0
 public class JavadocUtil
 92  
 {
 93  
     /** The default timeout used when fetching url, i.e. 2000. */
 94  
     public static final int DEFAULT_TIMEOUT = 2000;
 95  
 
 96  
     /** Error message when VM could not be started using invoker. */
 97  
     protected static final String ERROR_INIT_VM =
 98  
         "Error occurred during initialization of VM, try to reduce the Java heap size for the MAVEN_OPTS " +
 99  
         "environnement variable using -Xms:<size> and -Xmx:<size>.";
 100  
 
 101  
     /**
 102  
      * Method that removes the invalid directories in the specified directories.
 103  
      * <b>Note</b>: All elements in <code>dirs</code> could be an absolute or relative against the project's base
 104  
      * directory <code>String</code> path.
 105  
      *
 106  
      * @param project the current Maven project not null
 107  
      * @param dirs the list of <code>String</code> directories path that will be validated.
 108  
      * @return a List of valid <code>String</code> directories absolute paths.
 109  
      */
 110  
     protected static List pruneDirs( MavenProject project, List dirs )
 111  
     {
 112  97
         List pruned = new ArrayList( dirs.size() );
 113  97
         for ( Iterator i = dirs.iterator(); i.hasNext(); )
 114  
         {
 115  106
             String dir = (String) i.next();
 116  
 
 117  106
             if ( dir == null )
 118  
             {
 119  0
                 continue;
 120  
             }
 121  
 
 122  106
             File directory = new File( dir );
 123  106
             if ( !directory.isAbsolute() )
 124  
             {
 125  0
                 directory = new File( project.getBasedir(), directory.getPath() );
 126  
             }
 127  
 
 128  106
             if ( directory.isDirectory() && !pruned.contains( directory.getAbsolutePath() ) )
 129  
             {
 130  96
                 pruned.add( directory.getAbsolutePath() );
 131  
             }
 132  106
         }
 133  
 
 134  97
         return pruned;
 135  
     }
 136  
 
 137  
     /**
 138  
      * Method that removes the invalid files in the specified files.
 139  
      * <b>Note</b>: All elements in <code>files</code> should be an absolute <code>String</code> path.
 140  
      *
 141  
      * @param files the list of <code>String</code> files paths that will be validated.
 142  
      * @return a List of valid <code>File</code> objects.
 143  
      */
 144  
     protected static List pruneFiles( List files )
 145  
     {
 146  2
         List pruned = new ArrayList( files.size() );
 147  2
         for ( Iterator i = files.iterator(); i.hasNext(); )
 148  
         {
 149  94
             String f = (String) i.next();
 150  
 
 151  94
             if ( f == null )
 152  
             {
 153  0
                 continue;
 154  
             }
 155  
 
 156  94
             File file = new File( f );
 157  94
             if ( file.isFile() && !pruned.contains( f ) )
 158  
             {
 159  68
                 pruned.add( f );
 160  
             }
 161  94
         }
 162  
 
 163  2
         return pruned;
 164  
     }
 165  
 
 166  
     /**
 167  
      * Method that gets all the source files to be excluded from the javadoc on the given
 168  
      * source paths.
 169  
      *
 170  
      * @param sourcePaths      the path to the source files
 171  
      * @param subpackagesList  list of subpackages to be included in the javadoc
 172  
      * @param excludedPackages the package names to be excluded in the javadoc
 173  
      * @return a List of the source files to be excluded in the generated javadoc
 174  
      */
 175  
     protected static List getExcludedNames( List sourcePaths, String[] subpackagesList, String[] excludedPackages )
 176  
     {
 177  1
         List excludedNames = new ArrayList();
 178  1
         for ( Iterator i = sourcePaths.iterator(); i.hasNext(); )
 179  
         {
 180  1
             String path = (String) i.next();
 181  2
             for ( int j = 0; j < subpackagesList.length; j++ )
 182  
             {
 183  1
                 List excludes = getExcludedPackages( path, excludedPackages );
 184  1
                 excludedNames.addAll( excludes );
 185  
             }
 186  1
         }
 187  
 
 188  1
         return excludedNames;
 189  
     }
 190  
 
 191  
     /**
 192  
      * Copy from {@link org.apache.maven.project.MavenProject#getCompileArtifacts()}
 193  
      * @param artifacts not null
 194  
      * @return list of compile artifacts with compile scope
 195  
      * @deprecated since 2.5, using {@link #getCompileArtifacts(Set, boolean)} instead of.
 196  
      */
 197  
     protected static List getCompileArtifacts( Set artifacts )
 198  
     {
 199  0
         return getCompileArtifacts( artifacts, false );
 200  
     }
 201  
 
 202  
     /**
 203  
      * Copy from {@link org.apache.maven.project.MavenProject#getCompileArtifacts()}
 204  
      * @param artifacts not null
 205  
      * @param withTestScope flag to include or not the artifacts with test scope
 206  
      * @return list of compile artifacts with or without test scope.
 207  
      */
 208  
     protected static List getCompileArtifacts( Set artifacts, boolean withTestScope )
 209  
     {
 210  0
         List list = new ArrayList( artifacts.size() );
 211  
 
 212  0
         for ( Iterator i = artifacts.iterator(); i.hasNext(); )
 213  
         {
 214  0
             Artifact a = (Artifact) i.next();
 215  
 
 216  
             // TODO: classpath check doesn't belong here - that's the other method
 217  0
             if ( a.getArtifactHandler().isAddedToClasspath() )
 218  
             {
 219  
                 // TODO: let the scope handler deal with this
 220  0
                 if ( withTestScope )
 221  
                 {
 222  0
                     if ( Artifact.SCOPE_COMPILE.equals( a.getScope() )
 223  
                         || Artifact.SCOPE_PROVIDED.equals( a.getScope() )
 224  
                         || Artifact.SCOPE_SYSTEM.equals( a.getScope() )
 225  
                         || Artifact.SCOPE_TEST.equals( a.getScope() ) )
 226  
                     {
 227  0
                         list.add( a );
 228  
                     }
 229  
                 }
 230  
                 else
 231  
                 {
 232  0
                     if ( Artifact.SCOPE_COMPILE.equals( a.getScope() ) || Artifact.SCOPE_PROVIDED.equals( a.getScope() )
 233  
                         || Artifact.SCOPE_SYSTEM.equals( a.getScope() ) )
 234  
                     {
 235  0
                         list.add( a );
 236  
                     }
 237  
                 }
 238  
             }
 239  0
         }
 240  
 
 241  0
         return list;
 242  
     }
 243  
 
 244  
     /**
 245  
      * Convenience method to wrap an argument value in single quotes (i.e. <code>'</code>). Intended for values
 246  
      * which may contain whitespaces.
 247  
      * <br/>
 248  
      * To prevent javadoc error, the line separator (i.e. <code>\n</code>) are skipped.
 249  
      *
 250  
      * @param value the argument value.
 251  
      * @return argument with quote
 252  
      */
 253  
     protected static String quotedArgument( String value )
 254  
     {
 255  506
         String arg = value;
 256  
 
 257  506
         if ( StringUtils.isNotEmpty( arg ) )
 258  
         {
 259  177
             if ( arg.indexOf( "'" ) != -1 )
 260  
             {
 261  0
                 arg = StringUtils.replace( arg, "'", "\\'" );
 262  
             }
 263  177
             arg = "'" + arg + "'";
 264  
 
 265  
             // To prevent javadoc error
 266  177
             arg = StringUtils.replace( arg, "\n", " " );
 267  
         }
 268  
 
 269  506
         return arg;
 270  
     }
 271  
 
 272  
     /**
 273  
      * Convenience method to format a path argument so that it is properly interpreted by the javadoc tool. Intended
 274  
      * for path values which may contain whitespaces.
 275  
      *
 276  
      * @param value the argument value.
 277  
      * @return path argument with quote
 278  
      */
 279  
     protected static String quotedPathArgument( String value )
 280  
     {
 281  384
         String path = value;
 282  
 
 283  384
         if ( StringUtils.isNotEmpty( path ) )
 284  
         {
 285  176
             path = path.replace( '\\', '/' );
 286  176
             if ( path.indexOf( "\'" ) != -1 )
 287  
             {
 288  4
                 String split[] = path.split( "\'" );
 289  4
                 path = "";
 290  
 
 291  12
                 for ( int i = 0; i < split.length; i++ )
 292  
                 {
 293  8
                     if ( i != split.length - 1 )
 294  
                     {
 295  4
                         path = path + split[i] + "\\'";
 296  
                     }
 297  
                     else
 298  
                     {
 299  4
                         path = path + split[i];
 300  
                     }
 301  
                 }
 302  
             }
 303  176
             path = "'" + path + "'";
 304  
         }
 305  
 
 306  384
         return path;
 307  
     }
 308  
 
 309  
     /**
 310  
      * Convenience method that copy all <code>doc-files</code> directories from <code>javadocDir</code>
 311  
      * to the <code>outputDirectory</code>.
 312  
      *
 313  
      * @param outputDirectory the output directory
 314  
      * @param javadocDir the javadoc directory
 315  
      * @throws IOException if any
 316  
      * @deprecated since 2.5, using {@link #copyJavadocResources(File, File, String)} instead of.
 317  
      */
 318  
     protected static void copyJavadocResources( File outputDirectory, File javadocDir )
 319  
         throws IOException
 320  
     {
 321  0
         copyJavadocResources( outputDirectory, javadocDir, null );
 322  0
     }
 323  
 
 324  
     /**
 325  
      * Convenience method that copy all <code>doc-files</code> directories from <code>javadocDir</code>
 326  
      * to the <code>outputDirectory</code>.
 327  
      *
 328  
      * @param outputDirectory the output directory
 329  
      * @param javadocDir the javadoc directory
 330  
      * @param excludedocfilessubdir the excludedocfilessubdir parameter
 331  
      * @throws IOException if any
 332  
      * @since 2.5
 333  
      */
 334  
     protected static void copyJavadocResources( File outputDirectory, File javadocDir, String excludedocfilessubdir )
 335  
         throws IOException
 336  
     {
 337  8
         if ( !javadocDir.isDirectory() )
 338  
         {
 339  1
             return;
 340  
         }
 341  
 
 342  7
         List excludes = new ArrayList();
 343  7
         excludes.addAll( Arrays.asList( FileUtils.getDefaultExcludes() ) );
 344  
 
 345  7
         if ( StringUtils.isNotEmpty( excludedocfilessubdir ) )
 346  
         {
 347  2
             StringTokenizer st = new StringTokenizer( excludedocfilessubdir, ":" );
 348  
             String current;
 349  5
             while ( st.hasMoreTokens() )
 350  
             {
 351  3
                 current = st.nextToken();
 352  3
                 excludes.add( "**/" + current + "/**" );
 353  
             }
 354  
         }
 355  
 
 356  7
         List docFiles =
 357  
             FileUtils.getDirectoryNames( javadocDir, "resources,**/doc-files",
 358  
                                          StringUtils.join( excludes.iterator(), "," ), false, true );
 359  7
         for ( Iterator it = docFiles.iterator(); it.hasNext(); )
 360  
         {
 361  10
             String docFile = (String) it.next();
 362  
 
 363  10
             File docFileOutput = new File( outputDirectory, docFile );
 364  10
             FileUtils.mkdir( docFileOutput.getAbsolutePath() );
 365  10
             FileUtils.copyDirectoryStructure( new File( javadocDir, docFile ), docFileOutput );
 366  10
             List files =
 367  
                 FileUtils.getFileAndDirectoryNames( docFileOutput, StringUtils.join( excludes.iterator(), "," ),
 368  
                                                     null, true, true, true, true );
 369  10
             for ( Iterator it2 = files.iterator(); it2.hasNext(); )
 370  
             {
 371  307
                 File file = new File( it2.next().toString() );
 372  
 
 373  307
                 if ( file.isDirectory() )
 374  
                 {
 375  25
                     FileUtils.deleteDirectory( file );
 376  
                 }
 377  
                 else
 378  
                 {
 379  282
                     file.delete();
 380  
                 }
 381  307
             }
 382  10
         }
 383  7
     }
 384  
 
 385  
     /**
 386  
      * Method that gets the files or classes that would be included in the javadocs using the subpackages
 387  
      * parameter.
 388  
      *
 389  
      * @param sourceDirectory the directory where the source files are located
 390  
      * @param fileList        the list of all files found in the sourceDirectory
 391  
      * @param excludePackages package names to be excluded in the javadoc
 392  
      * @return a StringBuffer that contains the appended file names of the files to be included in the javadoc
 393  
      */
 394  
     protected static List getIncludedFiles( File sourceDirectory, String[] fileList, String[] excludePackages )
 395  
     {
 396  42
         List files = new ArrayList();
 397  
 
 398  113
         for ( int j = 0; j < fileList.length; j++ )
 399  
         {
 400  71
             boolean include = true;
 401  80
             for ( int k = 0; k < excludePackages.length && include; k++ )
 402  
             {
 403  
                 // handle wildcards (*) in the excludePackageNames
 404  9
                 String[] excludeName = excludePackages[k].split( "[*]" );
 405  
 
 406  9
                 if ( excludeName.length == 0 )
 407  
                 {
 408  0
                     continue;
 409  
                 }
 410  
 
 411  9
                 if ( excludeName.length > 1 )
 412  
                 {
 413  5
                     int u = 0;
 414  15
                     while ( include && u < excludeName.length )
 415  
                     {
 416  10
                         if ( !"".equals( excludeName[u].trim() ) && fileList[j].indexOf( excludeName[u] ) != -1 )
 417  
                         {
 418  1
                             include = false;
 419  
                         }
 420  10
                         u++;
 421  
                     }
 422  5
                 }
 423  
                 else
 424  
                 {
 425  4
                     if ( fileList[j].startsWith( sourceDirectory.toString() + File.separatorChar + excludeName[0] ) )
 426  
                     {
 427  2
                         if ( excludeName[0].endsWith( String.valueOf( File.separatorChar ) ) )
 428  
                         {
 429  2
                             int i = fileList[j].lastIndexOf( File.separatorChar );
 430  2
                             String packageName = fileList[j].substring( 0, i + 1 );
 431  2
                             File currentPackage = new File( packageName );
 432  2
                             File excludedPackage = new File( sourceDirectory, excludeName[0] );
 433  2
                             if ( currentPackage.equals( excludedPackage )
 434  
                                 && fileList[j].substring( i ).indexOf( ".java" ) != -1 )
 435  
                             {
 436  1
                                 include = true;
 437  
                             }
 438  
                             else
 439  
                             {
 440  1
                                 include = false;
 441  
                             }
 442  2
                         }
 443  
                         else
 444  
                         {
 445  0
                             include = false;
 446  
                         }
 447  
                     }
 448  
                 }
 449  
             }
 450  
 
 451  71
             if ( include )
 452  
             {
 453  69
                 files.add( quotedPathArgument( fileList[j] ) );
 454  
             }
 455  
         }
 456  
 
 457  42
         return files;
 458  
     }
 459  
 
 460  
     /**
 461  
      * Method that gets the complete package names (including subpackages) of the packages that were defined
 462  
      * in the excludePackageNames parameter.
 463  
      *
 464  
      * @param sourceDirectory     the directory where the source files are located
 465  
      * @param excludePackagenames package names to be excluded in the javadoc
 466  
      * @return a List of the packagenames to be excluded
 467  
      */
 468  
     protected static List getExcludedPackages( String sourceDirectory, String[] excludePackagenames )
 469  
     {
 470  1
         List files = new ArrayList();
 471  3
         for ( int i = 0; i < excludePackagenames.length; i++ )
 472  
         {
 473  2
             String[] fileList = FileUtils.getFilesFromExtension( sourceDirectory, new String[] { "java" } );
 474  14
             for ( int j = 0; j < fileList.length; j++ )
 475  
             {
 476  12
                 String[] excludeName = excludePackagenames[i].split( "[*]" );
 477  12
                 int u = 0;
 478  24
                 while ( u < excludeName.length )
 479  
                 {
 480  12
                     if ( !"".equals( excludeName[u].trim() ) && fileList[j].indexOf( excludeName[u] ) != -1
 481  
                         && sourceDirectory.indexOf( excludeName[u] ) == -1 )
 482  
                     {
 483  2
                         files.add( fileList[j] );
 484  
                     }
 485  12
                     u++;
 486  
                 }
 487  
             }
 488  
         }
 489  
 
 490  1
         List excluded = new ArrayList();
 491  1
         for ( Iterator it = files.iterator(); it.hasNext(); )
 492  
         {
 493  2
             String file = (String) it.next();
 494  2
             int idx = file.lastIndexOf( File.separatorChar );
 495  2
             String tmpStr = file.substring( 0, idx );
 496  2
             tmpStr = tmpStr.replace( '\\', '/' );
 497  2
             String[] srcSplit = tmpStr.split( sourceDirectory.replace( '\\', '/' ) + '/' );
 498  2
             String excludedPackage = srcSplit[1].replace( '/', '.' );
 499  
 
 500  2
             if ( !excluded.contains( excludedPackage ) )
 501  
             {
 502  2
                 excluded.add( excludedPackage );
 503  
             }
 504  2
         }
 505  
 
 506  1
         return excluded;
 507  
     }
 508  
 
 509  
     /**
 510  
      * Convenience method that gets the files to be included in the javadoc.
 511  
      *
 512  
      * @param sourceDirectory the directory where the source files are located
 513  
      * @param files the variable that contains the appended filenames of the files to be included in the javadoc
 514  
      * @param excludePackages the packages to be excluded in the javadocs
 515  
      */
 516  
     protected static void addFilesFromSource( List files, File sourceDirectory, String[] excludePackages )
 517  
     {
 518  46
         String[] fileList = FileUtils.getFilesFromExtension( sourceDirectory.getPath(), new String[] { "java" } );
 519  46
         if ( fileList != null && fileList.length != 0 )
 520  
         {
 521  42
             List tmpFiles = getIncludedFiles( sourceDirectory, fileList, excludePackages );
 522  42
             files.addAll( tmpFiles );
 523  
         }
 524  46
     }
 525  
 
 526  
     /**
 527  
      * Call the Javadoc tool and parse its output to find its version, i.e.:
 528  
      * <pre>
 529  
      * javadoc.exe(or .sh) -J-version
 530  
      * </pre>
 531  
      *
 532  
      * @param javadocExe not null file
 533  
      * @return the javadoc version as float
 534  
      * @throws IOException if javadocExe is null, doesn't exist or is not a file
 535  
      * @throws CommandLineException if any
 536  
      * @throws IllegalArgumentException if no output was found in the command line
 537  
      * @throws PatternSyntaxException if the output contains a syntax error in the regular-expression pattern.
 538  
      * @see #parseJavadocVersion(String)
 539  
      */
 540  
     protected static float getJavadocVersion( File javadocExe )
 541  
         throws IOException, CommandLineException, IllegalArgumentException, PatternSyntaxException
 542  
     {
 543  41
         if ( ( javadocExe == null ) || ( !javadocExe.exists() ) || ( !javadocExe.isFile() ) )
 544  
         {
 545  0
             throw new IOException( "The javadoc executable '" + javadocExe + "' doesn't exist or is not a file. " );
 546  
         }
 547  
 
 548  41
         Commandline cmd = new Commandline();
 549  41
         cmd.setExecutable( javadocExe.getAbsolutePath() );
 550  41
         cmd.setWorkingDirectory( javadocExe.getParentFile() );
 551  41
         cmd.createArg().setValue( "-J-version" );
 552  
 
 553  41
         CommandLineUtils.StringStreamConsumer out = new CommandLineUtils.StringStreamConsumer();
 554  41
         CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
 555  
 
 556  41
         int exitCode = CommandLineUtils.executeCommandLine( cmd, out, err );
 557  
 
 558  41
         if ( exitCode != 0 )
 559  
         {
 560  0
             StringBuffer msg = new StringBuffer( "Exit code: " + exitCode + " - " + err.getOutput() );
 561  0
             msg.append( '\n' );
 562  0
             msg.append( "Command line was:" + CommandLineUtils.toString( cmd.getCommandline() ) );
 563  0
             throw new CommandLineException( msg.toString() );
 564  
         }
 565  
 
 566  41
         if ( StringUtils.isNotEmpty( err.getOutput() ) )
 567  
         {
 568  41
             return parseJavadocVersion( err.getOutput() );
 569  
         }
 570  0
         else if ( StringUtils.isNotEmpty( out.getOutput() ) )
 571  
         {
 572  0
             return parseJavadocVersion( out.getOutput() );
 573  
         }
 574  
 
 575  0
         throw new IllegalArgumentException( "No output found from the command line 'javadoc -J-version'" );
 576  
     }
 577  
 
 578  
     /**
 579  
      * Parse the output for 'javadoc -J-version' and return the javadoc version recognized.
 580  
      * <br/>
 581  
      * Here are some output for 'javadoc -J-version' depending the JDK used:
 582  
      * <table>
 583  
      * <tr>
 584  
      *   <th>JDK</th>
 585  
      *   <th>Output for 'javadoc -J-version'</th>
 586  
      * </tr>
 587  
      * <tr>
 588  
      *   <td>Sun 1.4</td>
 589  
      *   <td>java full version "1.4.2_12-b03"</td>
 590  
      * </tr>
 591  
      * <tr>
 592  
      *   <td>Sun 1.5</td>
 593  
      *   <td>java full version "1.5.0_07-164"</td>
 594  
      * </tr>
 595  
      * <tr>
 596  
      *   <td>IBM 1.4</td>
 597  
      *   <td>javadoc full version "J2RE 1.4.2 IBM Windows 32 build cn1420-20040626"</td>
 598  
      * </tr>
 599  
      * <tr>
 600  
      *   <td>IBM 1.5 (French JVM)</td>
 601  
      *   <td>javadoc version complète de "J2RE 1.5.0 IBM Windows 32 build pwi32pdev-20070426a"</td>
 602  
      * </tr>
 603  
      * <tr>
 604  
      *   <td>FreeBSD 1.5</td>
 605  
      *   <td>java full version "diablo-1.5.0-b01"</td>
 606  
      * </tr>
 607  
      * <tr>
 608  
      *   <td>BEA jrockit 1.5</td>
 609  
      *   <td>java full version "1.5.0_11-b03"</td>
 610  
      * </tr>
 611  
      * </table>
 612  
      *
 613  
      * @param output for 'javadoc -J-version'
 614  
      * @return the version of the javadoc for the output.
 615  
      * @throws PatternSyntaxException if the output doesn't match with the output pattern
 616  
      * <tt>(?s).*?([0-9]+\\.[0-9]+)(\\.([0-9]+))?.*</tt>.
 617  
      * @throws IllegalArgumentException if the output is null
 618  
      */
 619  
     protected static float parseJavadocVersion( String output )
 620  
         throws IllegalArgumentException, PatternSyntaxException
 621  
     {
 622  58
         if ( StringUtils.isEmpty( output ) )
 623  
         {
 624  1
             throw new IllegalArgumentException( "The output could not be null." );
 625  
         }
 626  
 
 627  57
         Pattern pattern = Pattern.compile( "(?s).*?([0-9]+\\.[0-9]+)(\\.([0-9]+))?.*" );
 628  
 
 629  57
         Matcher matcher = pattern.matcher( output );
 630  57
         if ( !matcher.matches() )
 631  
         {
 632  1
             throw new PatternSyntaxException( "Unrecognized version of Javadoc: '" + output + "'", pattern.pattern(),
 633  
                                               pattern.toString().length() - 1 );
 634  
         }
 635  
 
 636  56
         String version = matcher.group( 3 );
 637  56
         if ( version == null )
 638  
         {
 639  1
             version = matcher.group( 1 );
 640  
         }
 641  
         else
 642  
         {
 643  55
             version = matcher.group( 1 ) + version;
 644  
         }
 645  
 
 646  56
         return Float.parseFloat( version );
 647  
     }
 648  
 
 649  
     /**
 650  
      * Parse a memory string which be used in the JVM arguments <code>-Xms</code> or <code>-Xmx</code>.
 651  
      * <br/>
 652  
      * Here are some supported memory string depending the JDK used:
 653  
      * <table>
 654  
      * <tr>
 655  
      *   <th>JDK</th>
 656  
      *   <th>Memory argument support for <code>-Xms</code> or <code>-Xmx</code></th>
 657  
      * </tr>
 658  
      * <tr>
 659  
      *   <td>SUN</td>
 660  
      *   <td>1024k | 128m | 1g | 1t</td>
 661  
      * </tr>
 662  
      * <tr>
 663  
      *   <td>IBM</td>
 664  
      *   <td>1024k | 1024b | 128m | 128mb | 1g | 1gb</td>
 665  
      * </tr>
 666  
      * <tr>
 667  
      *   <td>BEA</td>
 668  
      *   <td>1024k | 1024kb | 128m | 128mb | 1g | 1gb</td>
 669  
      * </tr>
 670  
      * </table>
 671  
      *
 672  
      * @param memory the memory to be parsed, not null.
 673  
      * @return the memory parsed with a supported unit. If no unit specified in the <code>memory</code> parameter,
 674  
      * the default unit is <code>m</code>. The units <code>g | gb</code> or <code>t | tb</code> will be converted
 675  
      * in <code>m</code>.
 676  
      * @throws IllegalArgumentException if the <code>memory</code> parameter is null or doesn't match any pattern.
 677  
      */
 678  
     protected static String parseJavadocMemory( String memory )
 679  
         throws IllegalArgumentException
 680  
     {
 681  18
         if ( StringUtils.isEmpty( memory ) )
 682  
         {
 683  1
             throw new IllegalArgumentException( "The memory could not be null." );
 684  
         }
 685  
 
 686  17
         Pattern p = Pattern.compile( "^\\s*(\\d+)\\s*?\\s*$" );
 687  17
         Matcher m = p.matcher( memory );
 688  17
         if ( m.matches() )
 689  
         {
 690  2
             return m.group( 1 ) + "m";
 691  
         }
 692  
 
 693  15
         p = Pattern.compile( "^\\s*(\\d+)\\s*k(b)?\\s*$", Pattern.CASE_INSENSITIVE );
 694  15
         m = p.matcher( memory );
 695  15
         if ( m.matches() )
 696  
         {
 697  2
             return m.group( 1 ) + "k";
 698  
         }
 699  
 
 700  13
         p = Pattern.compile( "^\\s*(\\d+)\\s*m(b)?\\s*$", Pattern.CASE_INSENSITIVE );
 701  13
         m = p.matcher( memory );
 702  13
         if ( m.matches() )
 703  
         {
 704  7
             return m.group( 1 ) + "m";
 705  
         }
 706  
 
 707  6
         p = Pattern.compile( "^\\s*(\\d+)\\s*g(b)?\\s*$", Pattern.CASE_INSENSITIVE );
 708  6
         m = p.matcher( memory );
 709  6
         if ( m.matches() )
 710  
         {
 711  2
             return ( Integer.parseInt( m.group( 1 ) ) * 1024 ) + "m";
 712  
         }
 713  
 
 714  4
         p = Pattern.compile( "^\\s*(\\d+)\\s*t(b)?\\s*$", Pattern.CASE_INSENSITIVE );
 715  4
         m = p.matcher( memory );
 716  4
         if ( m.matches() )
 717  
         {
 718  2
             return ( Integer.parseInt( m.group( 1 ) ) * 1024 * 1024 ) + "m";
 719  
         }
 720  
 
 721  2
         throw new IllegalArgumentException( "Could convert not to a memory size: " + memory );
 722  
     }
 723  
 
 724  
     /**
 725  
      * Fetch an URL
 726  
      *
 727  
      * @param settings the user settings used to fetch the url with an active proxy, if defined.
 728  
      * @param url the url to fetch
 729  
      * @throws IOException if any
 730  
      * @see #DEFAULT_TIMEOUT
 731  
      */
 732  
     protected static void fetchURL( Settings settings, URL url )
 733  
         throws IOException
 734  
     {
 735  28
         if ( url == null )
 736  
         {
 737  1
             throw new IllegalArgumentException( "The url is null" );
 738  
         }
 739  
 
 740  27
         if ( "file".equals( url.getProtocol() ) )
 741  
         {
 742  3
             InputStream in = null;
 743  
             try
 744  
             {
 745  3
                 in = url.openStream();
 746  
             }
 747  
             finally
 748  
             {
 749  3
                 IOUtil.close( in );
 750  3
             }
 751  
 
 752  3
             return;
 753  
         }
 754  
 
 755  
         // http, https...
 756  24
         HttpClient httpClient = new HttpClient( new MultiThreadedHttpConnectionManager() );
 757  24
         httpClient.getHttpConnectionManager().getParams().setConnectionTimeout( DEFAULT_TIMEOUT );
 758  24
         httpClient.getHttpConnectionManager().getParams().setSoTimeout( DEFAULT_TIMEOUT );
 759  24
         httpClient.getParams().setBooleanParameter( HttpClientParams.ALLOW_CIRCULAR_REDIRECTS, true );
 760  
 
 761  
         // Some web servers don't allow the default user-agent sent by httpClient
 762  24
         httpClient.getParams().setParameter( HttpMethodParams.USER_AGENT,
 763  
                                              "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)" );
 764  
 
 765  24
         if ( settings != null && settings.getActiveProxy() != null )
 766  
         {
 767  3
             Proxy activeProxy = settings.getActiveProxy();
 768  
 
 769  3
             ProxyInfo proxyInfo = new ProxyInfo();
 770  3
             proxyInfo.setNonProxyHosts( activeProxy.getNonProxyHosts() );
 771  
 
 772  3
             if ( StringUtils.isNotEmpty( activeProxy.getHost() )
 773  
                 && !ProxyUtils.validateNonProxyHosts( proxyInfo, url.getHost() ) )
 774  
             {
 775  3
                 httpClient.getHostConfiguration().setProxy( activeProxy.getHost(), activeProxy.getPort() );
 776  
 
 777  3
                 if ( StringUtils.isNotEmpty( activeProxy.getUsername() ) && activeProxy.getPassword() != null )
 778  
                 {
 779  2
                     Credentials credentials =
 780  
                         new UsernamePasswordCredentials( activeProxy.getUsername(), activeProxy.getPassword() );
 781  
 
 782  2
                     httpClient.getState().setProxyCredentials( AuthScope.ANY, credentials );
 783  
                 }
 784  
             }
 785  
         }
 786  
 
 787  24
         GetMethod getMethod = new GetMethod( url.toString() );
 788  
         try
 789  
         {
 790  24
             int status = httpClient.executeMethod( getMethod );
 791  
 
 792  22
             if ( status != HttpStatus.SC_OK )
 793  
             {
 794  0
                 throw new FileNotFoundException( url.toString() );
 795  
             }
 796  
         }
 797  
         finally
 798  
         {
 799  24
             getMethod.releaseConnection();
 800  22
         }
 801  22
     }
 802  
 
 803  
     /**
 804  
      * Validate if a charset is supported on this platform.
 805  
      *
 806  
      * @param charsetName the charsetName to be check.
 807  
      * @return <code>true</code> if the given charset is supported by the JVM, <code>false</code> otherwise.
 808  
      */
 809  
     protected static boolean validateEncoding( String charsetName )
 810  
     {
 811  119
         if ( StringUtils.isEmpty( charsetName ) )
 812  
         {
 813  1
             return false;
 814  
         }
 815  
 
 816  118
         OutputStream ost = new ByteArrayOutputStream();
 817  118
         OutputStreamWriter osw = null;
 818  
         try
 819  
         {
 820  118
             osw = new OutputStreamWriter( ost, charsetName );
 821  
         }
 822  5
         catch ( UnsupportedEncodingException exc )
 823  
         {
 824  5
             return false;
 825  
         }
 826  
         finally
 827  
         {
 828  118
             IOUtil.close( osw );
 829  113
         }
 830  
 
 831  113
         return true;
 832  
     }
 833  
 
 834  
     /**
 835  
      * For security reasons, if an active proxy is defined and needs an authentication by
 836  
      * username/password, hide the proxy password in the command line.
 837  
      *
 838  
      * @param cmdLine a command line, not null
 839  
      * @param settings the user settings
 840  
      * @return the cmdline with '*' for the http.proxyPassword JVM property
 841  
      */
 842  
     protected static String hideProxyPassword( String cmdLine, Settings settings )
 843  
     {
 844  38
         if ( cmdLine == null )
 845  
         {
 846  0
             throw new IllegalArgumentException( "cmdLine could not be null" );
 847  
         }
 848  
 
 849  38
         if ( settings == null )
 850  
         {
 851  34
             return cmdLine;
 852  
         }
 853  
 
 854  4
         Proxy activeProxy = settings.getActiveProxy();
 855  4
         if ( activeProxy != null && StringUtils.isNotEmpty( activeProxy.getHost() )
 856  
             && StringUtils.isNotEmpty( activeProxy.getUsername() )
 857  
             && StringUtils.isNotEmpty( activeProxy.getPassword() ) )
 858  
         {
 859  3
             String pass = "-J-Dhttp.proxyPassword=\"" + activeProxy.getPassword() + "\"";
 860  3
             String hidepass =
 861  
                 "-J-Dhttp.proxyPassword=\"" + StringUtils.repeat( "*", activeProxy.getPassword().length() ) + "\"";
 862  
 
 863  3
             return StringUtils.replace( cmdLine, pass, hidepass );
 864  
         }
 865  
 
 866  1
         return cmdLine;
 867  
     }
 868  
 
 869  
     /**
 870  
      * Auto-detect the class names of the implementation of <code>com.sun.tools.doclets.Taglet</code> class from a
 871  
      * given jar file.
 872  
      * <br/>
 873  
      * <b>Note</b>: <code>JAVA_HOME/lib/tools.jar</code> is a requirement to find
 874  
      * <code>com.sun.tools.doclets.Taglet</code> class.
 875  
      *
 876  
      * @param jarFile not null
 877  
      * @return the list of <code>com.sun.tools.doclets.Taglet</code> class names from a given jarFile.
 878  
      * @throws IOException if jarFile is invalid or not found, or if the <code>JAVA_HOME/lib/tools.jar</code>
 879  
      * is not found.
 880  
      * @throws ClassNotFoundException if any
 881  
      * @throws NoClassDefFoundError if any
 882  
      */
 883  
     protected static List getTagletClassNames( File jarFile )
 884  
         throws IOException, ClassNotFoundException, NoClassDefFoundError
 885  
     {
 886  2
         List classes = getClassNamesFromJar( jarFile );
 887  
         ClassLoader cl;
 888  
 
 889  
         // Needed to find com.sun.tools.doclets.Taglet class
 890  2
         File tools = new File( System.getProperty( "java.home" ), "../lib/tools.jar" );
 891  2
         if ( tools.exists() && tools.isFile() )
 892  
         {
 893  2
             cl = new URLClassLoader( new URL[] { jarFile.toURI().toURL(), tools.toURI().toURL() }, null );
 894  
         }
 895  
         else
 896  
         {
 897  0
             cl = new URLClassLoader( new URL[] { jarFile.toURI().toURL() }, null );
 898  
         }
 899  
 
 900  2
         List tagletClasses = new ArrayList();
 901  
 
 902  2
         Class tagletClass = cl.loadClass( "com.sun.tools.doclets.Taglet" );
 903  2
         for ( Iterator it = classes.iterator(); it.hasNext(); )
 904  
         {
 905  24
             String s = (String) it.next();
 906  
 
 907  24
             Class c = cl.loadClass( s );
 908  
 
 909  24
             if ( tagletClass.isAssignableFrom( c ) && !Modifier.isAbstract( c.getModifiers() ) )
 910  
             {
 911  20
                 tagletClasses.add( c.getName() );
 912  
             }
 913  24
         }
 914  
 
 915  2
         return tagletClasses;
 916  
     }
 917  
 
 918  
     /**
 919  
      * Copy the given url to the given file.
 920  
      *
 921  
      * @param url not null url
 922  
      * @param file not null file where the url will be created
 923  
      * @throws IOException if any
 924  
      * @since 2.6
 925  
      */
 926  
     protected static void copyResource( URL url, File file )
 927  
         throws IOException
 928  
     {
 929  4
         if ( file == null )
 930  
         {
 931  0
             throw new IOException( "The file " + file + " can't be null." );
 932  
         }
 933  4
         if ( url == null )
 934  
         {
 935  0
             throw new IOException( "The url " + url + " could not be null." );
 936  
         }
 937  
 
 938  4
         InputStream is = url.openStream();
 939  4
         if ( is == null )
 940  
         {
 941  0
             throw new IOException( "The resource " + url + " doesn't exists." );
 942  
         }
 943  
 
 944  4
         if ( !file.getParentFile().exists() )
 945  
         {
 946  0
             file.getParentFile().mkdirs();
 947  
         }
 948  
 
 949  4
         FileOutputStream os = null;
 950  
         try
 951  
         {
 952  4
             os = new FileOutputStream( file );
 953  
 
 954  4
             IOUtil.copy( is, os );
 955  
         }
 956  
         finally
 957  
         {
 958  4
             IOUtil.close( is );
 959  
 
 960  4
             IOUtil.close( os );
 961  4
         }
 962  4
     }
 963  
 
 964  
     /**
 965  
      * Invoke Maven for the given project file with a list of goals and properties, the output will be in the
 966  
      * invokerlog file.
 967  
      * <br/>
 968  
      * <b>Note</b>: the Maven Home should be defined in the <code>maven.home</code> Java system property or defined in
 969  
      * <code>M2_HOME</code> system env variables.
 970  
      *
 971  
      * @param log a logger could be null.
 972  
      * @param localRepositoryDir the localRepository not null.
 973  
      * @param projectFile a not null project file.
 974  
      * @param goals a not null goals list.
 975  
      * @param properties the properties for the goals, could be null.
 976  
      * @param invokerLog the log file where the invoker will be written, if null using <code>System.out</code>.
 977  
      * @throws MavenInvocationException if any
 978  
      * @since 2.6
 979  
      */
 980  
     protected static void invokeMaven( Log log, File localRepositoryDir, File projectFile, List goals,
 981  
                                        Properties properties, File invokerLog )
 982  
         throws MavenInvocationException
 983  
     {
 984  4
         if ( projectFile == null )
 985  
         {
 986  0
             throw new IllegalArgumentException( "projectFile should be not null." );
 987  
         }
 988  4
         if ( !projectFile.isFile() )
 989  
         {
 990  0
             throw new IllegalArgumentException( projectFile.getAbsolutePath() + " is not a file." );
 991  
         }
 992  4
         if ( goals == null || goals.size() == 0 )
 993  
         {
 994  0
             throw new IllegalArgumentException( "goals should be not empty." );
 995  
         }
 996  4
         if ( localRepositoryDir == null || !localRepositoryDir.isDirectory() )
 997  
         {
 998  0
             throw new IllegalArgumentException( "localRepositoryDir '" + localRepositoryDir
 999  
                 + "' should be a directory." );
 1000  
         }
 1001  
 
 1002  4
         String mavenHome = getMavenHome( log );
 1003  4
         if ( StringUtils.isEmpty( mavenHome ) )
 1004  
         {
 1005  0
             String msg =
 1006  
                 "Could NOT invoke Maven because no Maven Home is defined. You need to have set the M2_HOME "
 1007  
                     + "system env variable or a maven.home Java system properties.";
 1008  0
             if ( log != null )
 1009  
             {
 1010  0
                 log.error( msg );
 1011  
             }
 1012  
             else
 1013  
             {
 1014  0
                 System.err.println( msg );
 1015  
             }
 1016  0
             return;
 1017  
         }
 1018  
 
 1019  4
         Invoker invoker = new DefaultInvoker();
 1020  4
         invoker.setMavenHome( new File( mavenHome ) );
 1021  4
         invoker.setLocalRepositoryDirectory( localRepositoryDir );
 1022  
 
 1023  4
         InvocationRequest request = new DefaultInvocationRequest();
 1024  4
         request.setBaseDirectory( projectFile.getParentFile() );
 1025  4
         request.setPomFile( projectFile );
 1026  4
         if ( log != null )
 1027  
         {
 1028  4
             request.setDebug( log.isDebugEnabled() );
 1029  
         }
 1030  
         else
 1031  
         {
 1032  0
             request.setDebug( true );
 1033  
         }
 1034  4
         request.setGoals( goals );
 1035  4
         if ( properties != null )
 1036  
         {
 1037  2
             request.setProperties( properties );
 1038  
         }
 1039  4
         File javaHome = getJavaHome( log );
 1040  4
         if ( javaHome != null )
 1041  
         {
 1042  4
             request.setJavaHome( javaHome );
 1043  
         }
 1044  
 
 1045  4
         if ( log != null && log.isDebugEnabled() )
 1046  
         {
 1047  0
             log.debug( "Invoking Maven for the goals: " + goals + " with "
 1048  
                 + ( properties == null ? "no properties" : "properties=" + properties ) );
 1049  
         }
 1050  4
         InvocationResult result = invoke( log, invoker, request, invokerLog, goals, properties, null );
 1051  
 
 1052  4
         if ( result.getExitCode() != 0 )
 1053  
         {
 1054  0
             String invokerLogContent = readFile( invokerLog, "UTF-8" );
 1055  
 
 1056  
             // see DefaultMaven
 1057  0
             if ( invokerLogContent != null && ( invokerLogContent.indexOf( "Scanning for projects..." ) == -1
 1058  0
                 || invokerLogContent.indexOf( OutOfMemoryError.class.getName() ) != -1 ) )
 1059  
             {
 1060  0
                 if ( log != null )
 1061  
                 {
 1062  0
                     log.error( "Error occurred during initialization of VM, trying to use an empty MAVEN_OPTS..." );
 1063  
 
 1064  0
                     if ( log.isDebugEnabled() )
 1065  
                     {
 1066  0
                         log.debug( "Reinvoking Maven for the goals: " + goals + " with an empty MAVEN_OPTS..." );
 1067  
                     }
 1068  
                 }
 1069  0
                 result = invoke( log, invoker, request, invokerLog, goals, properties, "" );
 1070  
             }
 1071  
         }
 1072  
 
 1073  4
         if ( result.getExitCode() != 0 )
 1074  
         {
 1075  0
             String invokerLogContent = readFile( invokerLog, "UTF-8" );
 1076  
 
 1077  
             // see DefaultMaven
 1078  0
             if ( invokerLogContent != null && ( invokerLogContent.indexOf( "Scanning for projects..." ) == -1
 1079  
                 || invokerLogContent.indexOf( OutOfMemoryError.class.getName() ) != -1 ) )
 1080  
             {
 1081  0
                 throw new MavenInvocationException( ERROR_INIT_VM );
 1082  
             }
 1083  
 
 1084  0
             throw new MavenInvocationException( "Error when invoking Maven, consult the invoker log file: "
 1085  
                 + invokerLog.getAbsolutePath() );
 1086  
         }
 1087  4
     }
 1088  
 
 1089  
     /**
 1090  
      * Read the given file and return the content or null if an IOException occurs.
 1091  
      *
 1092  
      * @param javaFile not null
 1093  
      * @param encoding could be null
 1094  
      * @return the content with unified line separator of the given javaFile using the given encoding.
 1095  
      * @see FileUtils#fileRead(File, String)
 1096  
      * @since 2.6.1
 1097  
      */
 1098  
     protected static String readFile( final File javaFile, final String encoding )
 1099  
     {
 1100  
         try
 1101  
         {
 1102  0
             return FileUtils.fileRead( javaFile, encoding );
 1103  
         }
 1104  0
         catch (IOException e )
 1105  
         {
 1106  0
             return null;
 1107  
         }
 1108  
     }
 1109  
 
 1110  
     /**
 1111  
      * Split the given path with colon and semi-colon, to support Solaris and Windows path.
 1112  
      * Examples:
 1113  
      * <pre>
 1114  
      * splitPath( "/home:/tmp" )     = ["/home", "/tmp"]
 1115  
      * splitPath( "/home;/tmp" )     = ["/home", "/tmp"]
 1116  
      * splitPath( "C:/home:C:/tmp" ) = ["C:/home", "C:/tmp"]
 1117  
      * splitPath( "C:/home;C:/tmp" ) = ["C:/home", "C:/tmp"]
 1118  
      * </pre>
 1119  
      *
 1120  
      * @param path which can contain multiple paths separated with a colon (<code>:</code>) or a
 1121  
      * semi-colon (<code>;</code>), plateform independent. Could be null.
 1122  
      * @return the path splitted by colon or semi-colon or <code>null</code> if path was <code>null</code>.
 1123  
      * @since 2.6.1
 1124  
      */
 1125  
     protected static String[] splitPath( final String path )
 1126  
     {
 1127  9
         if ( path == null )
 1128  
         {
 1129  0
             return null;
 1130  
         }
 1131  
 
 1132  9
         List subpaths = new ArrayList();
 1133  9
         PathTokenizer pathTokenizer = new PathTokenizer( path );
 1134  30
         while ( pathTokenizer.hasMoreTokens() )
 1135  
         {
 1136  21
             subpaths.add( pathTokenizer.nextToken() );
 1137  
         }
 1138  
 
 1139  9
         return (String[]) subpaths.toArray( new String[0] );
 1140  
     }
 1141  
 
 1142  
     /**
 1143  
      * Unify the given path with the current System path separator, to be plateform independent.
 1144  
      * Examples:
 1145  
      * <pre>
 1146  
      * unifyPathSeparator( "/home:/tmp" ) = "/home:/tmp" (Solaris box)
 1147  
      * unifyPathSeparator( "/home:/tmp" ) = "/home;/tmp" (Windows box)
 1148  
      * </pre>
 1149  
      *
 1150  
      * @param path which can contain multiple paths by separating them with a colon (<code>:</code>) or a
 1151  
      * semi-colon (<code>;</code>), plateform independent. Could be null.
 1152  
      * @return the same path but separated with the current System path separator or <code>null</code> if path was
 1153  
      * <code>null</code>.
 1154  
      * @since 2.6.1
 1155  
      * @see #splitPath(String)
 1156  
      * @see File#pathSeparator
 1157  
      */
 1158  
     protected static String unifyPathSeparator( final String path )
 1159  
     {
 1160  48
         if ( path == null )
 1161  
         {
 1162  40
             return null;
 1163  
         }
 1164  
 
 1165  8
         return StringUtils.join( splitPath( path ), File.pathSeparator );
 1166  
     }
 1167  
 
 1168  
     // ----------------------------------------------------------------------
 1169  
     // private methods
 1170  
     // ----------------------------------------------------------------------
 1171  
 
 1172  
     /**
 1173  
      * @param jarFile not null
 1174  
      * @return all class names from the given jar file.
 1175  
      * @throws IOException if any or if the jarFile is null or doesn't exist.
 1176  
      */
 1177  
     private static List getClassNamesFromJar( File jarFile )
 1178  
         throws IOException
 1179  
     {
 1180  2
         if ( jarFile == null || !jarFile.exists() || !jarFile.isFile() )
 1181  
         {
 1182  0
             throw new IOException( "The jar '" + jarFile + "' doesn't exist or is not a file." );
 1183  
         }
 1184  
 
 1185  2
         List classes = new ArrayList();
 1186  2
         JarInputStream jarStream = null;
 1187  
 
 1188  
         try
 1189  
         {
 1190  2
             jarStream = new JarInputStream( new FileInputStream( jarFile ) );
 1191  2
             JarEntry jarEntry = jarStream.getNextJarEntry();
 1192  48
             while ( jarEntry != null )
 1193  
             {
 1194  46
                 if ( jarEntry == null )
 1195  
                 {
 1196  0
                     break;
 1197  
                 }
 1198  
 
 1199  46
                 if ( jarEntry.getName().toLowerCase( Locale.ENGLISH ).endsWith( ".class" ) )
 1200  
                 {
 1201  24
                     String name = jarEntry.getName().substring( 0, jarEntry.getName().indexOf( "." ) );
 1202  
 
 1203  24
                     classes.add( name.replaceAll( "/", "\\." ) );
 1204  
                 }
 1205  
 
 1206  46
                 jarStream.closeEntry();
 1207  46
                 jarEntry = jarStream.getNextJarEntry();
 1208  
             }
 1209  
         }
 1210  
         finally
 1211  
         {
 1212  2
             IOUtil.close( jarStream );
 1213  2
         }
 1214  
 
 1215  2
         return classes;
 1216  
     }
 1217  
 
 1218  
     /**
 1219  
      * @param log could be null
 1220  
      * @param invoker not null
 1221  
      * @param request not null
 1222  
      * @param invokerLog not null
 1223  
      * @param goals not null
 1224  
      * @param properties could be null
 1225  
      * @param mavenOpts could be null
 1226  
      * @return the invocation result
 1227  
      * @throws MavenInvocationException if any
 1228  
      * @since 2.6
 1229  
      */
 1230  
     private static InvocationResult invoke( Log log, Invoker invoker, InvocationRequest request, File invokerLog,
 1231  
                                             List goals, Properties properties, String mavenOpts )
 1232  
         throws MavenInvocationException
 1233  
     {
 1234  
         PrintStream ps;
 1235  4
         OutputStream os = null;
 1236  4
         if ( invokerLog != null )
 1237  
         {
 1238  4
             if ( log != null && log.isDebugEnabled() )
 1239  
             {
 1240  0
                 log.debug( "Using " + invokerLog.getAbsolutePath() + " to log the invoker" );
 1241  
             }
 1242  
 
 1243  
             try
 1244  
             {
 1245  4
                 if ( !invokerLog.exists() )
 1246  
                 {
 1247  4
                     invokerLog.getParentFile().mkdirs();
 1248  
                 }
 1249  4
                 os = new FileOutputStream( invokerLog );
 1250  4
                 ps = new PrintStream( os, true, "UTF-8" );
 1251  
             }
 1252  0
             catch ( FileNotFoundException e )
 1253  
             {
 1254  0
                 if ( log != null && log.isErrorEnabled() )
 1255  
                 {
 1256  0
                     log.error( "FileNotFoundException: " + e.getMessage() + ". Using System.out to log the invoker." );
 1257  
                 }
 1258  0
                 ps = System.out;
 1259  
             }
 1260  0
             catch ( UnsupportedEncodingException e )
 1261  
             {
 1262  0
                 if ( log != null && log.isErrorEnabled() )
 1263  
                 {
 1264  0
                     log.error( "UnsupportedEncodingException: " + e.getMessage()
 1265  
                         + ". Using System.out to log the invoker." );
 1266  
                 }
 1267  0
                 ps = System.out;
 1268  4
             }
 1269  
         }
 1270  
         else
 1271  
         {
 1272  0
             if ( log != null && log.isDebugEnabled() )
 1273  
             {
 1274  0
                 log.debug( "Using System.out to log the invoker." );
 1275  
             }
 1276  
 
 1277  0
             ps = System.out;
 1278  
         }
 1279  
 
 1280  4
         if ( mavenOpts != null )
 1281  
         {
 1282  0
             request.setMavenOpts( mavenOpts );
 1283  
         }
 1284  
 
 1285  4
         InvocationOutputHandler outputHandler = new PrintStreamHandler( ps, false );
 1286  4
         request.setOutputHandler( outputHandler );
 1287  
 
 1288  4
         outputHandler.consumeLine( "Invoking Maven for the goals: " + goals + " with "
 1289  
             + ( properties == null ? "no properties" : "properties=" + properties ) );
 1290  4
         outputHandler.consumeLine( "" );
 1291  4
         outputHandler.consumeLine( "M2_HOME=" + getMavenHome( log ) );
 1292  4
         outputHandler.consumeLine( "MAVEN_OPTS=" + getMavenOpts( log ) );
 1293  4
         outputHandler.consumeLine( "JAVA_HOME=" + getJavaHome( log ) );
 1294  4
         outputHandler.consumeLine( "JAVA_OPTS=" + getJavaOpts( log ) );
 1295  4
         outputHandler.consumeLine( "" );
 1296  
 
 1297  
         try
 1298  
         {
 1299  4
             return invoker.execute( request );
 1300  
         }
 1301  
         finally
 1302  
         {
 1303  4
             IOUtil.close( os );
 1304  4
             ps = null;
 1305  
         }
 1306  
     }
 1307  
 
 1308  
     /**
 1309  
      * @param log a logger could be null
 1310  
      * @return the Maven home defined in the <code>maven.home</code> system property or defined
 1311  
      * in <code>M2_HOME</code> system env variables or null if never setted.
 1312  
      * @since 2.6
 1313  
      */
 1314  
     private static String getMavenHome( Log log )
 1315  
     {
 1316  8
         String mavenHome = System.getProperty( "maven.home" );
 1317  8
         if ( mavenHome == null )
 1318  
         {
 1319  
             try
 1320  
             {
 1321  0
                 mavenHome = CommandLineUtils.getSystemEnvVars().getProperty( "M2_HOME" );
 1322  
             }
 1323  0
             catch ( IOException e )
 1324  
             {
 1325  0
                 if ( log != null && log.isDebugEnabled() )
 1326  
                 {
 1327  0
                     log.debug( "IOException: " + e.getMessage() );
 1328  
                 }
 1329  0
             }
 1330  
         }
 1331  
 
 1332  8
         File m2Home = new File( mavenHome );
 1333  8
         if ( !m2Home.exists() )
 1334  
         {
 1335  0
             if ( log != null && log.isErrorEnabled() )
 1336  
             {
 1337  0
                 log
 1338  
                    .error( "Cannot find Maven application directory. Either specify \'maven.home\' system property, or "
 1339  
                        + "M2_HOME environment variable." );
 1340  
             }
 1341  
         }
 1342  
 
 1343  8
         return mavenHome;
 1344  
     }
 1345  
 
 1346  
     /**
 1347  
      * @param log a logger could be null
 1348  
      * @return the <code>MAVEN_OPTS</code> env variable value
 1349  
      * @since 2.6
 1350  
      */
 1351  
     private static String getMavenOpts( Log log )
 1352  
     {
 1353  4
         String mavenOpts = null;
 1354  
         try
 1355  
         {
 1356  4
             mavenOpts = CommandLineUtils.getSystemEnvVars().getProperty( "MAVEN_OPTS" );
 1357  
         }
 1358  0
         catch ( IOException e )
 1359  
         {
 1360  0
             if ( log != null && log.isDebugEnabled() )
 1361  
             {
 1362  0
                 log.debug( "IOException: " + e.getMessage() );
 1363  
             }
 1364  4
         }
 1365  
 
 1366  4
         return mavenOpts;
 1367  
     }
 1368  
 
 1369  
     /**
 1370  
      * @param log a logger could be null
 1371  
      * @return the <code>JAVA_HOME</code> from System.getProperty( "java.home" )
 1372  
      * By default, <code>System.getProperty( "java.home" ) = JRE_HOME</code> and <code>JRE_HOME</code>
 1373  
      * should be in the <code>JDK_HOME</code>
 1374  
      * @since 2.6
 1375  
      */
 1376  
     private static File getJavaHome( Log log )
 1377  
     {
 1378  
         File javaHome;
 1379  8
         if ( SystemUtils.IS_OS_MAC_OSX )
 1380  
         {
 1381  0
             javaHome = SystemUtils.getJavaHome();
 1382  
         }
 1383  
         else
 1384  
         {
 1385  8
             javaHome = new File( SystemUtils.getJavaHome(), ".." );
 1386  
         }
 1387  
 
 1388  8
         if ( javaHome == null || !javaHome.exists() )
 1389  
         {
 1390  
             try
 1391  
             {
 1392  0
                 javaHome = new File( CommandLineUtils.getSystemEnvVars().getProperty( "JAVA_HOME" ) );
 1393  
             }
 1394  0
             catch ( IOException e )
 1395  
             {
 1396  0
                 if ( log != null && log.isDebugEnabled() )
 1397  
                 {
 1398  0
                     log.debug( "IOException: " + e.getMessage() );
 1399  
                 }
 1400  0
             }
 1401  
         }
 1402  
 
 1403  8
         if ( javaHome == null || !javaHome.exists() )
 1404  
         {
 1405  0
             if ( log != null && log.isErrorEnabled() )
 1406  
             {
 1407  0
                 log.error( "Cannot find Java application directory. Either specify \'java.home\' system property, or "
 1408  
                     + "JAVA_HOME environment variable." );
 1409  
             }
 1410  
         }
 1411  
 
 1412  8
         return javaHome;
 1413  
     }
 1414  
 
 1415  
     /**
 1416  
      * @param log a logger could be null
 1417  
      * @return the <code>JAVA_OPTS</code> env variable value
 1418  
      * @since 2.6
 1419  
      */
 1420  
     private static String getJavaOpts( Log log )
 1421  
     {
 1422  4
         String javaOpts = null;
 1423  
         try
 1424  
         {
 1425  4
             javaOpts = CommandLineUtils.getSystemEnvVars().getProperty( "JAVA_OPTS" );
 1426  
         }
 1427  0
         catch ( IOException e )
 1428  
         {
 1429  0
             if ( log != null && log.isDebugEnabled() )
 1430  
             {
 1431  0
                 log.debug( "IOException: " + e.getMessage() );
 1432  
             }
 1433  4
         }
 1434  
 
 1435  4
         return javaOpts;
 1436  
     }
 1437  
 
 1438  
     /**
 1439  
      * A Path tokenizer takes a path and returns the components that make up
 1440  
      * that path.
 1441  
      *
 1442  
      * The path can use path separators of either ':' or ';' and file separators
 1443  
      * of either '/' or '\'.
 1444  
      *
 1445  
      * @version revision 439418 taken on 2009-09-12 from Ant Project
 1446  
      * (see http://svn.apache.org/repos/asf/ant/core/trunk/src/main/org/apache/tools/ant/PathTokenizer.java)
 1447  
      */
 1448  0
     private static class PathTokenizer
 1449  
     {
 1450  
         /**
 1451  
          * A tokenizer to break the string up based on the ':' or ';' separators.
 1452  
          */
 1453  
         private StringTokenizer tokenizer;
 1454  
 
 1455  
         /**
 1456  
          * A String which stores any path components which have been read ahead
 1457  
          * due to DOS filesystem compensation.
 1458  
          */
 1459  9
         private String lookahead = null;
 1460  
 
 1461  
         /**
 1462  
          * A boolean that determines if we are running on Novell NetWare, which
 1463  
          * exhibits slightly different path name characteristics (multi-character
 1464  
          * volume / drive names)
 1465  
          */
 1466  9
         private boolean onNetWare = Os.isFamily( "netware" );
 1467  
 
 1468  
         /**
 1469  
          * Flag to indicate whether or not we are running on a platform with a
 1470  
          * DOS style filesystem
 1471  
          */
 1472  
         private boolean dosStyleFilesystem;
 1473  
 
 1474  
         /**
 1475  
          * Constructs a path tokenizer for the specified path.
 1476  
          *
 1477  
          * @param path The path to tokenize. Must not be <code>null</code>.
 1478  
          */
 1479  
         public PathTokenizer( String path )
 1480  9
         {
 1481  9
             if ( onNetWare )
 1482  
             {
 1483  
                 // For NetWare, use the boolean=true mode, so we can use delimiter
 1484  
                 // information to make a better decision later.
 1485  0
                 tokenizer = new StringTokenizer( path, ":;", true );
 1486  
             }
 1487  
             else
 1488  
             {
 1489  
                 // on Windows and Unix, we can ignore delimiters and still have
 1490  
                 // enough information to tokenize correctly.
 1491  9
                 tokenizer = new StringTokenizer( path, ":;", false );
 1492  
             }
 1493  9
             dosStyleFilesystem = File.pathSeparatorChar == ';';
 1494  9
         }
 1495  
 
 1496  
         /**
 1497  
          * Tests if there are more path elements available from this tokenizer's
 1498  
          * path. If this method returns <code>true</code>, then a subsequent call
 1499  
          * to nextToken will successfully return a token.
 1500  
          *
 1501  
          * @return <code>true</code> if and only if there is at least one token
 1502  
          * in the string after the current position; <code>false</code> otherwise.
 1503  
          */
 1504  
         public boolean hasMoreTokens()
 1505  
         {
 1506  30
             if ( lookahead != null )
 1507  
             {
 1508  0
                 return true;
 1509  
             }
 1510  
 
 1511  30
             return tokenizer.hasMoreTokens();
 1512  
         }
 1513  
 
 1514  
         /**
 1515  
          * Returns the next path element from this tokenizer.
 1516  
          *
 1517  
          * @return the next path element from this tokenizer.
 1518  
          *
 1519  
          * @exception NoSuchElementException if there are no more elements in this
 1520  
          *            tokenizer's path.
 1521  
          */
 1522  
         public String nextToken()
 1523  
             throws NoSuchElementException
 1524  
         {
 1525  21
             String token = null;
 1526  21
             if ( lookahead != null )
 1527  
             {
 1528  0
                 token = lookahead;
 1529  0
                 lookahead = null;
 1530  
             }
 1531  
             else
 1532  
             {
 1533  21
                 token = tokenizer.nextToken().trim();
 1534  
             }
 1535  
 
 1536  21
             if ( !onNetWare )
 1537  
             {
 1538  21
                 if ( token.length() == 1 && Character.isLetter( token.charAt( 0 ) ) && dosStyleFilesystem
 1539  
                     && tokenizer.hasMoreTokens() )
 1540  
                 {
 1541  
                     // we are on a dos style system so this path could be a drive
 1542  
                     // spec. We look at the next token
 1543  13
                     String nextToken = tokenizer.nextToken().trim();
 1544  13
                     if ( nextToken.startsWith( "\\" ) || nextToken.startsWith( "/" ) )
 1545  
                     {
 1546  
                         // we know we are on a DOS style platform and the next path
 1547  
                         // starts with a slash or backslash, so we know this is a
 1548  
                         // drive spec
 1549  13
                         token += ":" + nextToken;
 1550  
                     }
 1551  
                     else
 1552  
                     {
 1553  
                         // store the token just read for next time
 1554  0
                         lookahead = nextToken;
 1555  
                     }
 1556  13
                 }
 1557  
             }
 1558  
             else
 1559  
             {
 1560  
                 // we are on NetWare, tokenizing is handled a little differently,
 1561  
                 // due to the fact that NetWare has multiple-character volume names.
 1562  0
                 if ( token.equals( File.pathSeparator ) || token.equals( ":" ) )
 1563  
                 {
 1564  
                     // ignore ";" and get the next token
 1565  0
                     token = tokenizer.nextToken().trim();
 1566  
                 }
 1567  
 
 1568  0
                 if ( tokenizer.hasMoreTokens() )
 1569  
                 {
 1570  
                     // this path could be a drive spec, so look at the next token
 1571  0
                     String nextToken = tokenizer.nextToken().trim();
 1572  
 
 1573  
                     // make sure we aren't going to get the path separator next
 1574  0
                     if ( !nextToken.equals( File.pathSeparator ) )
 1575  
                     {
 1576  0
                         if ( nextToken.equals( ":" ) )
 1577  
                         {
 1578  0
                             if ( !token.startsWith( "/" ) && !token.startsWith( "\\" ) && !token.startsWith( "." )
 1579  
                                 && !token.startsWith( ".." ) )
 1580  
                             {
 1581  
                                 // it indeed is a drive spec, get the next bit
 1582  0
                                 String oneMore = tokenizer.nextToken().trim();
 1583  0
                                 if ( !oneMore.equals( File.pathSeparator ) )
 1584  
                                 {
 1585  0
                                     token += ":" + oneMore;
 1586  
                                 }
 1587  
                                 else
 1588  
                                 {
 1589  0
                                     token += ":";
 1590  0
                                     lookahead = oneMore;
 1591  
                                 }
 1592  0
                             }
 1593  
                             // implicit else: ignore the ':' since we have either a
 1594  
                             // UNIX or a relative path
 1595  
                         }
 1596  
                         else
 1597  
                         {
 1598  
                             // store the token just read for next time
 1599  0
                             lookahead = nextToken;
 1600  
                         }
 1601  
                     }
 1602  
                 }
 1603  
             }
 1604  21
             return token;
 1605  
         }
 1606  
     }
 1607  
 }