Coverage Report - org.apache.maven.plugin.javadoc.JavadocUtil
 
Classes in this File Line Coverage Branch Coverage Complexity
JavadocUtil
79 %
213/268
62 %
128/208
5,571
JavadocUtil$1
0 %
0/2
N/A
5,571
 
 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.IOException;
 26  
 import java.io.InputStream;
 27  
 import java.io.OutputStream;
 28  
 import java.io.OutputStreamWriter;
 29  
 import java.io.UnsupportedEncodingException;
 30  
 import java.lang.reflect.Modifier;
 31  
 import java.net.Authenticator;
 32  
 import java.net.PasswordAuthentication;
 33  
 import java.net.URL;
 34  
 import java.net.URLClassLoader;
 35  
 import java.util.ArrayList;
 36  
 import java.util.Arrays;
 37  
 import java.util.Iterator;
 38  
 import java.util.List;
 39  
 import java.util.Locale;
 40  
 import java.util.Properties;
 41  
 import java.util.Set;
 42  
 import java.util.StringTokenizer;
 43  
 import java.util.jar.JarEntry;
 44  
 import java.util.jar.JarInputStream;
 45  
 import java.util.regex.Matcher;
 46  
 import java.util.regex.Pattern;
 47  
 import java.util.regex.PatternSyntaxException;
 48  
 
 49  
 import org.apache.maven.artifact.Artifact;
 50  
 import org.apache.maven.project.MavenProject;
 51  
 import org.apache.maven.settings.Proxy;
 52  
 import org.apache.maven.settings.Settings;
 53  
 import org.codehaus.plexus.util.FileUtils;
 54  
 import org.codehaus.plexus.util.IOUtil;
 55  
 import org.codehaus.plexus.util.StringUtils;
 56  
 import org.codehaus.plexus.util.cli.CommandLineException;
 57  
 import org.codehaus.plexus.util.cli.CommandLineUtils;
 58  
 import org.codehaus.plexus.util.cli.Commandline;
 59  
 
 60  
 /**
 61  
  * Set of utilities methods for Javadoc.
 62  
  *
 63  
  * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
 64  
  * @version $Id$
 65  
  * @since 2.4
 66  
  */
 67  0
 public class JavadocUtil
 68  
 {
 69  
     /**
 70  
      * Method that removes the invalid directories in the specified directories.
 71  
      * <b>Note</b>: All elements in <code>dirs</code> could be an absolute or relative against the project's base
 72  
      * directory <code>String</code> path.
 73  
      *
 74  
      * @param project the current Maven project not null
 75  
      * @param dirs the list of <code>String</code> directories path that will be validated.
 76  
      * @return a List of valid <code>String</code> directories absolute paths.
 77  
      */
 78  
     protected static List pruneDirs( MavenProject project, List dirs )
 79  
     {
 80  61
         List pruned = new ArrayList( dirs.size() );
 81  61
         for ( Iterator i = dirs.iterator(); i.hasNext(); )
 82  
         {
 83  72
             String dir = (String) i.next();
 84  
 
 85  72
             if ( dir == null )
 86  
             {
 87  0
                 continue;
 88  
             }
 89  
 
 90  72
             File directory = new File( dir );
 91  72
             if ( !directory.isAbsolute() )
 92  
             {
 93  0
                 directory = new File( project.getBasedir(), directory.getPath() );
 94  
             }
 95  
 
 96  72
             if ( directory.isDirectory() && !pruned.contains( dir ) )
 97  
             {
 98  65
                 pruned.add( directory.getAbsolutePath() );
 99  
             }
 100  72
         }
 101  
 
 102  61
         return pruned;
 103  
     }
 104  
 
 105  
     /**
 106  
      * Method that removes the invalid files in the specified files.
 107  
      * <b>Note</b>: All elements in <code>files</code> should be an absolute <code>String</code> path.
 108  
      *
 109  
      * @param files the list of <code>String</code> files paths that will be validated.
 110  
      * @return a List of valid <code>File</code> objects.
 111  
      */
 112  
     protected static List pruneFiles( List files )
 113  
     {
 114  2
         List pruned = new ArrayList( files.size() );
 115  2
         for ( Iterator i = files.iterator(); i.hasNext(); )
 116  
         {
 117  94
             String f = (String) i.next();
 118  
 
 119  94
             if ( f == null )
 120  
             {
 121  0
                 continue;
 122  
             }
 123  
 
 124  94
             File file = new File( f );
 125  94
             if ( file.isFile() && !pruned.contains( f ) )
 126  
             {
 127  68
                 pruned.add( f );
 128  
             }
 129  94
         }
 130  
 
 131  2
         return pruned;
 132  
     }
 133  
 
 134  
     /**
 135  
      * Method that gets all the source files to be excluded from the javadoc on the given
 136  
      * source paths.
 137  
      *
 138  
      * @param sourcePaths      the path to the source files
 139  
      * @param subpackagesList  list of subpackages to be included in the javadoc
 140  
      * @param excludedPackages the package names to be excluded in the javadoc
 141  
      * @return a List of the source files to be excluded in the generated javadoc
 142  
      */
 143  
     protected static List getExcludedNames( List sourcePaths, String[] subpackagesList, String[] excludedPackages )
 144  
     {
 145  1
         List excludedNames = new ArrayList();
 146  1
         for ( Iterator i = sourcePaths.iterator(); i.hasNext(); )
 147  
         {
 148  1
             String path = (String) i.next();
 149  2
             for ( int j = 0; j < subpackagesList.length; j++ )
 150  
             {
 151  1
                 List excludes = getExcludedPackages( path, excludedPackages );
 152  1
                 excludedNames.addAll( excludes );
 153  
             }
 154  1
         }
 155  
 
 156  1
         return excludedNames;
 157  
     }
 158  
 
 159  
     /**
 160  
      * Copy from {@link org.apache.maven.project.MavenProject#getCompileArtifacts()}
 161  
      * @param artifacts not null
 162  
      * @return list of compile artifacts with compile scope
 163  
      * @deprecated since 2.5, using {@link #getCompileArtifacts(Set, boolean)} instead of.
 164  
      */
 165  
     protected static List getCompileArtifacts( Set artifacts )
 166  
     {
 167  0
         return getCompileArtifacts( artifacts, false );
 168  
     }
 169  
 
 170  
     /**
 171  
      * Copy from {@link org.apache.maven.project.MavenProject#getCompileArtifacts()}
 172  
      * @param artifacts not null
 173  
      * @param withTestScope flag to include or not the artifacts with test scope
 174  
      * @return list of compile artifacts with or without test scope.
 175  
      */
 176  
     protected static List getCompileArtifacts( Set artifacts, boolean withTestScope )
 177  
     {
 178  0
         List list = new ArrayList( artifacts.size() );
 179  
 
 180  0
         for ( Iterator i = artifacts.iterator(); i.hasNext(); )
 181  
         {
 182  0
             Artifact a = (Artifact) i.next();
 183  
 
 184  
             // TODO: classpath check doesn't belong here - that's the other method
 185  0
             if ( a.getArtifactHandler().isAddedToClasspath() )
 186  
             {
 187  
                 // TODO: let the scope handler deal with this
 188  0
                 if ( withTestScope )
 189  
                 {
 190  0
                     if ( Artifact.SCOPE_COMPILE.equals( a.getScope() )
 191  
                         || Artifact.SCOPE_PROVIDED.equals( a.getScope() )
 192  
                         || Artifact.SCOPE_SYSTEM.equals( a.getScope() )
 193  
                         || Artifact.SCOPE_TEST.equals( a.getScope() ) )
 194  
                     {
 195  0
                         list.add( a );
 196  
                     }
 197  
                 }
 198  
                 else
 199  
                 {
 200  0
                     if ( Artifact.SCOPE_COMPILE.equals( a.getScope() ) || Artifact.SCOPE_PROVIDED.equals( a.getScope() )
 201  
                         || Artifact.SCOPE_SYSTEM.equals( a.getScope() ) )
 202  
                     {
 203  0
                         list.add( a );
 204  
                     }
 205  
                 }
 206  
             }
 207  0
         }
 208  
 
 209  0
         return list;
 210  
     }
 211  
 
 212  
     /**
 213  
      * Convenience method to wrap an argument value in single quotes (i.e. <code>'</code>). Intended for values
 214  
      * which may contain whitespaces.
 215  
      * <br/>
 216  
      * To prevent javadoc error, the line separator (i.e. <code>\n</code>) are skipped.
 217  
      *
 218  
      * @param value the argument value.
 219  
      * @return argument with quote
 220  
      */
 221  
     protected static String quotedArgument( String value )
 222  
     {
 223  315
         String arg = value;
 224  
 
 225  315
         if ( StringUtils.isNotEmpty( arg ) )
 226  
         {
 227  124
             if ( arg.indexOf( "'" ) != -1 )
 228  
             {
 229  0
                 arg = StringUtils.replace( arg, "'", "\\'" );
 230  
             }
 231  124
             arg = "'" + arg + "'";
 232  
 
 233  
             // To prevent javadoc error
 234  124
             arg = StringUtils.replace( arg, "\n", " " );
 235  
         }
 236  
 
 237  315
         return arg;
 238  
     }
 239  
 
 240  
     /**
 241  
      * Convenience method to format a path argument so that it is properly interpreted by the javadoc tool. Intended
 242  
      * for path values which may contain whitespaces.
 243  
      *
 244  
      * @param value the argument value.
 245  
      * @return path argument with quote
 246  
      */
 247  
     protected static String quotedPathArgument( String value )
 248  
     {
 249  238
         String path = value;
 250  
 
 251  238
         if ( StringUtils.isNotEmpty( path ) )
 252  
         {
 253  111
             path = path.replace( '\\', '/' );
 254  111
             if ( path.indexOf( "\'" ) != -1 )
 255  
             {
 256  4
                 String split[] = path.split( "\'" );
 257  4
                 path = "";
 258  
 
 259  12
                 for ( int i = 0; i < split.length; i++ )
 260  
                 {
 261  8
                     if ( i != split.length - 1 )
 262  
                     {
 263  4
                         path = path + split[i] + "\\'";
 264  
                     }
 265  
                     else
 266  
                     {
 267  4
                         path = path + split[i];
 268  
                     }
 269  
                 }
 270  
             }
 271  111
             path = "'" + path + "'";
 272  
         }
 273  
 
 274  238
         return path;
 275  
     }
 276  
 
 277  
     /**
 278  
      * Convenience method that copy all <code>doc-files</code> directories from <code>javadocDir</code>
 279  
      * to the <code>outputDirectory</code>.
 280  
      *
 281  
      * @param outputDirectory the output directory
 282  
      * @param javadocDir the javadoc directory
 283  
      * @throws IOException if any
 284  
      * @deprecated since 2.5, using {@link #copyJavadocResources(File, File, String)} instead of.
 285  
      */
 286  
     protected static void copyJavadocResources( File outputDirectory, File javadocDir )
 287  
         throws IOException
 288  
     {
 289  0
         copyJavadocResources( outputDirectory, javadocDir, null );
 290  0
     }
 291  
 
 292  
     /**
 293  
      * Convenience method that copy all <code>doc-files</code> directories from <code>javadocDir</code>
 294  
      * to the <code>outputDirectory</code>.
 295  
      *
 296  
      * @param outputDirectory the output directory
 297  
      * @param javadocDir the javadoc directory
 298  
      * @param excludedocfilessubdir the excludedocfilessubdir parameter
 299  
      * @throws IOException if any
 300  
      * @since 2.5
 301  
      */
 302  
     protected static void copyJavadocResources( File outputDirectory, File javadocDir, String excludedocfilessubdir )
 303  
         throws IOException
 304  
     {
 305  5
         List excludes = new ArrayList();
 306  5
         excludes.addAll( Arrays.asList( FileUtils.getDefaultExcludes() ) );
 307  
 
 308  5
         if ( StringUtils.isNotEmpty( excludedocfilessubdir ) )
 309  
         {
 310  1
             StringTokenizer st = new StringTokenizer( excludedocfilessubdir, ":" );
 311  
             String current;
 312  2
             while ( st.hasMoreTokens() )
 313  
             {
 314  1
                 current = st.nextToken();
 315  1
                 excludes.add( "**/" + current + "/*" );
 316  
             }
 317  
         }
 318  
 
 319  5
         if ( javadocDir.exists() && javadocDir.isDirectory() )
 320  
         {
 321  4
             List docFiles = FileUtils.getDirectoryNames( javadocDir, "**/doc-files", StringUtils.join( excludes
 322  
                 .iterator(), "," ), false, true );
 323  4
             for ( Iterator it = docFiles.iterator(); it.hasNext(); )
 324  
             {
 325  4
                 String docFile = (String) it.next();
 326  
 
 327  4
                 File docFileOutput = new File( outputDirectory, docFile );
 328  4
                 FileUtils.mkdir( docFileOutput.getAbsolutePath() );
 329  4
                 FileUtils.copyDirectory( new File( javadocDir, docFile ), docFileOutput );
 330  4
             }
 331  
         }
 332  5
     }
 333  
 
 334  
     /**
 335  
      * Method that gets the files or classes that would be included in the javadocs using the subpackages
 336  
      * parameter.
 337  
      *
 338  
      * @param sourceDirectory the directory where the source files are located
 339  
      * @param fileList        the list of all files found in the sourceDirectory
 340  
      * @param excludePackages package names to be excluded in the javadoc
 341  
      * @return a StringBuffer that contains the appended file names of the files to be included in the javadoc
 342  
      */
 343  
     protected static List getIncludedFiles( File sourceDirectory, String[] fileList, String[] excludePackages )
 344  
     {
 345  25
         List files = new ArrayList();
 346  
 
 347  78
         for ( int j = 0; j < fileList.length; j++ )
 348  
         {
 349  53
             boolean include = true;
 350  62
             for ( int k = 0; k < excludePackages.length && include; k++ )
 351  
             {
 352  
                 // handle wildcards (*) in the excludePackageNames
 353  9
                 String[] excludeName = excludePackages[k].split( "[*]" );
 354  
 
 355  9
                 if ( excludeName.length > 1 )
 356  
                 {
 357  5
                     int u = 0;
 358  15
                     while ( include && u < excludeName.length )
 359  
                     {
 360  10
                         if ( !"".equals( excludeName[u].trim() ) && fileList[j].indexOf( excludeName[u] ) != -1 )
 361  
                         {
 362  1
                             include = false;
 363  
                         }
 364  10
                         u++;
 365  
                     }
 366  5
                 }
 367  
                 else
 368  
                 {
 369  4
                     if ( fileList[j].startsWith( sourceDirectory.toString() + File.separatorChar + excludeName[0] ) )
 370  
                     {
 371  2
                         if ( excludeName[0].endsWith( String.valueOf( File.separatorChar ) ) )
 372  
                         {
 373  2
                             int i = fileList[j].lastIndexOf( File.separatorChar );
 374  2
                             String packageName = fileList[j].substring( 0, i + 1 );
 375  2
                             File currentPackage = new File( packageName );
 376  2
                             File excludedPackage = new File( sourceDirectory, excludeName[0] );
 377  2
                             if ( currentPackage.equals( excludedPackage )
 378  
                                 && fileList[j].substring( i ).indexOf( ".java" ) != -1 )
 379  
                             {
 380  1
                                 include = true;
 381  
                             }
 382  
                             else
 383  
                             {
 384  1
                                 include = false;
 385  
                             }
 386  2
                         }
 387  
                         else
 388  
                         {
 389  0
                             include = false;
 390  
                         }
 391  
                     }
 392  
                 }
 393  
             }
 394  
 
 395  53
             if ( include )
 396  
             {
 397  51
                 files.add( quotedPathArgument( fileList[j] ) );
 398  
             }
 399  
         }
 400  
 
 401  25
         return files;
 402  
     }
 403  
 
 404  
     /**
 405  
      * Method that gets the complete package names (including subpackages) of the packages that were defined
 406  
      * in the excludePackageNames parameter.
 407  
      *
 408  
      * @param sourceDirectory     the directory where the source files are located
 409  
      * @param excludePackagenames package names to be excluded in the javadoc
 410  
      * @return a List of the packagenames to be excluded
 411  
      */
 412  
     protected static List getExcludedPackages( String sourceDirectory, String[] excludePackagenames )
 413  
     {
 414  1
         List files = new ArrayList();
 415  3
         for ( int i = 0; i < excludePackagenames.length; i++ )
 416  
         {
 417  2
             String[] fileList = FileUtils.getFilesFromExtension( sourceDirectory, new String[] { "java" } );
 418  14
             for ( int j = 0; j < fileList.length; j++ )
 419  
             {
 420  12
                 String[] excludeName = excludePackagenames[i].split( "[*]" );
 421  12
                 int u = 0;
 422  24
                 while ( u < excludeName.length )
 423  
                 {
 424  12
                     if ( !"".equals( excludeName[u].trim() ) && fileList[j].indexOf( excludeName[u] ) != -1
 425  
                         && sourceDirectory.indexOf( excludeName[u] ) == -1 )
 426  
                     {
 427  2
                         files.add( fileList[j] );
 428  
                     }
 429  12
                     u++;
 430  
                 }
 431  
             }
 432  
         }
 433  
 
 434  1
         List excluded = new ArrayList();
 435  1
         for ( Iterator it = files.iterator(); it.hasNext(); )
 436  
         {
 437  2
             String file = (String) it.next();
 438  2
             int idx = file.lastIndexOf( File.separatorChar );
 439  2
             String tmpStr = file.substring( 0, idx );
 440  2
             tmpStr = tmpStr.replace( '\\', '/' );
 441  2
             String[] srcSplit = tmpStr.split( sourceDirectory.replace( '\\', '/' ) + '/' );
 442  2
             String excludedPackage = srcSplit[1].replace( '/', '.' );
 443  
 
 444  2
             if ( !excluded.contains( excludedPackage ) )
 445  
             {
 446  2
                 excluded.add( excludedPackage );
 447  
             }
 448  2
         }
 449  
 
 450  1
         return excluded;
 451  
     }
 452  
 
 453  
     /**
 454  
      * Convenience method that gets the files to be included in the javadoc.
 455  
      *
 456  
      * @param sourceDirectory the directory where the source files are located
 457  
      * @param files the variable that contains the appended filenames of the files to be included in the javadoc
 458  
      * @param excludePackages the packages to be excluded in the javadocs
 459  
      */
 460  
     protected static void addFilesFromSource( List files, File sourceDirectory, String[] excludePackages )
 461  
     {
 462  29
         String[] fileList = FileUtils.getFilesFromExtension( sourceDirectory.getPath(), new String[] { "java" } );
 463  29
         if ( fileList != null && fileList.length != 0 )
 464  
         {
 465  25
             List tmpFiles = getIncludedFiles( sourceDirectory, fileList, excludePackages );
 466  25
             files.addAll( tmpFiles );
 467  
         }
 468  29
     }
 469  
 
 470  
     /**
 471  
      * Call the Javadoc tool and parse its output to find its version, i.e.:
 472  
      * <pre>
 473  
      * javadoc.exe(or .sh) -J-version
 474  
      * </pre>
 475  
      *
 476  
      * @param javadocExe not null file
 477  
      * @return the javadoc version as float
 478  
      * @throws IOException if javadocExe is null, doesn't exist or is not a file
 479  
      * @throws CommandLineException if any
 480  
      * @throws IllegalArgumentException if no output was found in the command line
 481  
      * @throws PatternSyntaxException if the output contains a syntax error in the regular-expression pattern.
 482  
      * @see #parseJavadocVersion(String)
 483  
      */
 484  
     protected static float getJavadocVersion( File javadocExe )
 485  
         throws IOException, CommandLineException, IllegalArgumentException, PatternSyntaxException
 486  
     {
 487  24
         if ( ( javadocExe == null ) || ( !javadocExe.exists() ) || ( !javadocExe.isFile() ) )
 488  
         {
 489  0
             throw new IOException( "The javadoc executable '" + javadocExe + "' doesn't exist or is not a file. " );
 490  
         }
 491  
 
 492  24
         Commandline cmd = new Commandline();
 493  24
         cmd.setExecutable( javadocExe.getAbsolutePath() );
 494  24
         cmd.setWorkingDirectory( javadocExe.getParentFile() );
 495  24
         cmd.createArg().setValue( "-J-version" );
 496  
 
 497  24
         CommandLineUtils.StringStreamConsumer out = new CommandLineUtils.StringStreamConsumer();
 498  24
         CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
 499  
 
 500  24
         int exitCode = CommandLineUtils.executeCommandLine( cmd, out, err );
 501  
 
 502  24
         if ( exitCode != 0 )
 503  
         {
 504  0
             StringBuffer msg = new StringBuffer( "Exit code: " + exitCode + " - " + err.getOutput() );
 505  0
             msg.append( '\n' );
 506  0
             msg.append( "Command line was:" + CommandLineUtils.toString( cmd.getCommandline() ) );
 507  0
             throw new CommandLineException( msg.toString() );
 508  
         }
 509  
 
 510  24
         if ( StringUtils.isNotEmpty( err.getOutput() ) )
 511  
         {
 512  24
             return parseJavadocVersion( err.getOutput() );
 513  
         }
 514  0
         else if ( StringUtils.isNotEmpty( out.getOutput() ) )
 515  
         {
 516  0
             return parseJavadocVersion( out.getOutput() );
 517  
         }
 518  
 
 519  0
         throw new IllegalArgumentException( "No output found from the command line 'javadoc -J-version'" );
 520  
     }
 521  
 
 522  
     /**
 523  
      * Parse the output for 'javadoc -J-version' and return the javadoc version recognized.
 524  
      * <br/>
 525  
      * Here are some output for 'javadoc -J-version' depending the JDK used:
 526  
      * <table>
 527  
      * <tr>
 528  
      *   <th>JDK</th>
 529  
      *   <th>Output for 'javadoc -J-version'</th>
 530  
      * </tr>
 531  
      * <tr>
 532  
      *   <td>Sun 1.4</td>
 533  
      *   <td>java full version "1.4.2_12-b03"</td>
 534  
      * </tr>
 535  
      * <tr>
 536  
      *   <td>Sun 1.5</td>
 537  
      *   <td>java full version "1.5.0_07-164"</td>
 538  
      * </tr>
 539  
      * <tr>
 540  
      *   <td>IBM 1.4</td>
 541  
      *   <td>javadoc full version "J2RE 1.4.2 IBM Windows 32 build cn1420-20040626"</td>
 542  
      * </tr>
 543  
      * <tr>
 544  
      *   <td>IBM 1.5 (French JVM)</td>
 545  
      *   <td>javadoc version complète de "J2RE 1.5.0 IBM Windows 32 build pwi32pdev-20070426a"</td>
 546  
      * </tr>
 547  
      * <tr>
 548  
      *   <td>FreeBSD 1.5</td>
 549  
      *   <td>java full version "diablo-1.5.0-b01"</td>
 550  
      * </tr>
 551  
      * <tr>
 552  
      *   <td>BEA jrockit 1.5</td>
 553  
      *   <td>java full version "1.5.0_11-b03"</td>
 554  
      * </tr>
 555  
      * </table>
 556  
      *
 557  
      * @param output for 'javadoc -J-version'
 558  
      * @return the version of the javadoc for the output.
 559  
      * @throws PatternSyntaxException if the output doesn't match with the output pattern
 560  
      * <tt>(?s).*?([0-9]+\\.[0-9]+)(\\.([0-9]+))?.*</tt>.
 561  
      * @throws IllegalArgumentException if the output is null
 562  
      */
 563  
     protected static float parseJavadocVersion( String output )
 564  
         throws IllegalArgumentException, PatternSyntaxException
 565  
     {
 566  41
         if ( StringUtils.isEmpty( output ) )
 567  
         {
 568  1
             throw new IllegalArgumentException( "The output could not be null." );
 569  
         }
 570  
 
 571  40
         Pattern pattern = Pattern.compile( "(?s).*?([0-9]+\\.[0-9]+)(\\.([0-9]+))?.*" );
 572  
 
 573  40
         Matcher matcher = pattern.matcher( output );
 574  40
         if ( !matcher.matches() )
 575  
         {
 576  1
             throw new PatternSyntaxException( "Unrecognized version of Javadoc: '" + output + "'", pattern.pattern(),
 577  
                                               pattern.toString().length() - 1 );
 578  
         }
 579  
 
 580  39
         String version = matcher.group( 3 );
 581  39
         if ( version == null )
 582  
         {
 583  1
             version = matcher.group( 1 );
 584  
         }
 585  
         else
 586  
         {
 587  38
             version = matcher.group( 1 ) + version;
 588  
         }
 589  
 
 590  39
         return Float.parseFloat( version );
 591  
     }
 592  
 
 593  
     /**
 594  
      * Parse a memory string which be used in the JVM arguments <code>-Xms</code> or <code>-Xmx</code>.
 595  
      * <br/>
 596  
      * Here are some supported memory string depending the JDK used:
 597  
      * <table>
 598  
      * <tr>
 599  
      *   <th>JDK</th>
 600  
      *   <th>Memory argument support for <code>-Xms</code> or <code>-Xmx</code></th>
 601  
      * </tr>
 602  
      * <tr>
 603  
      *   <td>SUN</td>
 604  
      *   <td>1024k | 128m | 1g | 1t</td>
 605  
      * </tr>
 606  
      * <tr>
 607  
      *   <td>IBM</td>
 608  
      *   <td>1024k | 1024b | 128m | 128mb | 1g | 1gb</td>
 609  
      * </tr>
 610  
      * <tr>
 611  
      *   <td>BEA</td>
 612  
      *   <td>1024k | 1024kb | 128m | 128mb | 1g | 1gb</td>
 613  
      * </tr>
 614  
      * </table>
 615  
      *
 616  
      * @param memory the memory to be parsed, not null.
 617  
      * @return the memory parsed with a supported unit. If no unit specified in the <code>memory</code> parameter,
 618  
      * the default unit is <code>m</code>. The units <code>g | gb</code> or <code>t | tb</code> will be converted
 619  
      * in <code>m</code>.
 620  
      * @throws IllegalArgumentException if the <code>memory</code> parameter is null or doesn't match any pattern.
 621  
      */
 622  
     protected static String parseJavadocMemory( String memory )
 623  
         throws IllegalArgumentException
 624  
     {
 625  18
         if ( StringUtils.isEmpty( memory ) )
 626  
         {
 627  1
             throw new IllegalArgumentException( "The memory could not be null." );
 628  
         }
 629  
 
 630  17
         Pattern p = Pattern.compile( "^\\s*(\\d+)\\s*?\\s*$" );
 631  17
         Matcher m = p.matcher( memory );
 632  17
         if ( m.matches() )
 633  
         {
 634  2
             return m.group( 1 ) + "m";
 635  
         }
 636  
 
 637  15
         p = Pattern.compile( "^\\s*(\\d+)\\s*k(b)?\\s*$", Pattern.CASE_INSENSITIVE );
 638  15
         m = p.matcher( memory );
 639  15
         if ( m.matches() )
 640  
         {
 641  2
             return m.group( 1 ) + "k";
 642  
         }
 643  
 
 644  13
         p = Pattern.compile( "^\\s*(\\d+)\\s*m(b)?\\s*$", Pattern.CASE_INSENSITIVE );
 645  13
         m = p.matcher( memory );
 646  13
         if ( m.matches() )
 647  
         {
 648  7
             return m.group( 1 ) + "m";
 649  
         }
 650  
 
 651  6
         p = Pattern.compile( "^\\s*(\\d+)\\s*g(b)?\\s*$", Pattern.CASE_INSENSITIVE );
 652  6
         m = p.matcher( memory );
 653  6
         if ( m.matches() )
 654  
         {
 655  2
             return ( Integer.parseInt( m.group( 1 ) ) * 1024 ) + "m";
 656  
         }
 657  
 
 658  4
         p = Pattern.compile( "^\\s*(\\d+)\\s*t(b)?\\s*$", Pattern.CASE_INSENSITIVE );
 659  4
         m = p.matcher( memory );
 660  4
         if ( m.matches() )
 661  
         {
 662  2
             return ( Integer.parseInt( m.group( 1 ) ) * 1024 * 1024 ) + "m";
 663  
         }
 664  
 
 665  2
         throw new IllegalArgumentException( "Could convert not to a memory size: " + memory );
 666  
     }
 667  
 
 668  
     /**
 669  
      * Fetch an URL
 670  
      *
 671  
      * @param settings the user settings used to fetch the url with an active proxy, if defined.
 672  
      * @param url the url to fetch
 673  
      * @throws IOException if any
 674  
      */
 675  
     protected static void fetchURL( Settings settings, URL url )
 676  
         throws IOException
 677  
     {
 678  7
         if ( url == null )
 679  
         {
 680  0
             throw new IOException( "The url is null" );
 681  
         }
 682  
 
 683  7
         Properties oldSystemProperties = new Properties();
 684  7
         oldSystemProperties.putAll( System.getProperties() );
 685  
 
 686  7
         if ( settings != null )
 687  
         {
 688  0
             String scheme = url.getProtocol();
 689  
 
 690  0
             if ( !"file".equals( scheme ) )
 691  
             {
 692  0
                 Proxy activeProxy = settings.getActiveProxy();
 693  0
                 if ( activeProxy != null )
 694  
                 {
 695  0
                     if ( "http".equals( scheme ) || "https".equals( scheme ) || "ftp".equals( scheme ) )
 696  
                     {
 697  0
                         scheme += ".";
 698  
                     }
 699  
                     else
 700  
                     {
 701  0
                         scheme = "";
 702  
                     }
 703  
 
 704  0
                     if ( StringUtils.isNotEmpty( activeProxy.getHost() ) )
 705  
                     {
 706  0
                         Properties systemProperties = System.getProperties();
 707  0
                         systemProperties.setProperty( scheme + "proxySet", "true" );
 708  0
                         systemProperties.setProperty( scheme + "proxyHost", activeProxy.getHost() );
 709  
 
 710  0
                         if ( activeProxy.getPort() > 0 )
 711  
                         {
 712  0
                             systemProperties
 713  
                                 .setProperty( scheme + "proxyPort", String.valueOf( activeProxy.getPort() ) );
 714  
                         }
 715  
 
 716  0
                         if ( StringUtils.isNotEmpty( activeProxy.getNonProxyHosts() ) )
 717  
                         {
 718  0
                             systemProperties.setProperty( scheme + "nonProxyHosts", activeProxy.getNonProxyHosts() );
 719  
                         }
 720  
 
 721  0
                         final String userName = activeProxy.getUsername();
 722  0
                         if ( StringUtils.isNotEmpty( userName ) )
 723  
                         {
 724  0
                             final String pwd = StringUtils.isEmpty( activeProxy.getPassword() ) ? "" : activeProxy
 725  
                                 .getPassword();
 726  0
                             Authenticator.setDefault( new Authenticator()
 727  
                             {
 728  0
                                 protected PasswordAuthentication getPasswordAuthentication()
 729  
                                 {
 730  0
                                     return new PasswordAuthentication( userName, pwd.toCharArray() );
 731  
                                 }
 732  
                             } );
 733  
                         }
 734  
                     }
 735  
                 }
 736  
             }
 737  
         }
 738  
 
 739  7
         InputStream in = null;
 740  
         try
 741  
         {
 742  7
             in = url.openStream();
 743  
         }
 744  
         finally
 745  
         {
 746  7
             IOUtil.close( in );
 747  
 
 748  
             // Reset system properties
 749  7
             if ( ( settings != null ) && ( !"file".equals( url.getProtocol() ) )
 750  
                 && ( settings.getActiveProxy() != null )
 751  
                 && ( StringUtils.isNotEmpty( settings.getActiveProxy().getHost() ) ) )
 752  
             {
 753  0
                 System.setProperties( oldSystemProperties );
 754  0
                 Authenticator.setDefault( null );
 755  
             }
 756  
         }
 757  7
     }
 758  
 
 759  
     /**
 760  
      * Validate if a charset is supported on this platform.
 761  
      *
 762  
      * @param charsetName the charsetName to be check.
 763  
      * @return <code>true</code> if the charset is supported by the JVM, <code>false</code> otherwise.
 764  
      */
 765  
     protected static boolean validateEncoding( String charsetName )
 766  
     {
 767  50
         if ( StringUtils.isEmpty( charsetName ) )
 768  
         {
 769  1
             return false;
 770  
         }
 771  
 
 772  49
         OutputStream ost = new ByteArrayOutputStream();
 773  49
         OutputStreamWriter osw = null;
 774  
         try
 775  
         {
 776  49
             osw = new OutputStreamWriter( ost, charsetName );
 777  
         }
 778  3
         catch ( UnsupportedEncodingException exc )
 779  
         {
 780  3
             return false;
 781  
         }
 782  
         finally
 783  
         {
 784  49
             IOUtil.close( osw );
 785  46
         }
 786  46
         return true;
 787  
     }
 788  
 
 789  
     /**
 790  
      * For security reasons, if an active proxy is defined and needs an authentication by
 791  
      * username/password, hide the proxy password in the command line.
 792  
      *
 793  
      * @param cmdLine a command line, not null
 794  
      * @param settings the user settings
 795  
      * @return the cmdline with '*' for the http.proxyPassword JVM property
 796  
      */
 797  
     protected static String hideProxyPassword( String cmdLine, Settings settings )
 798  
     {
 799  3
         if ( cmdLine == null )
 800  
         {
 801  0
             throw new IllegalArgumentException( "cmdLine could not be null" );
 802  
         }
 803  
 
 804  3
         if ( settings == null )
 805  
         {
 806  2
             return cmdLine;
 807  
         }
 808  
 
 809  1
         Proxy activeProxy = settings.getActiveProxy();
 810  1
         if ( activeProxy != null && StringUtils.isNotEmpty( activeProxy.getHost() )
 811  
             && StringUtils.isNotEmpty( activeProxy.getUsername() )
 812  
             && StringUtils.isNotEmpty( activeProxy.getPassword() ) )
 813  
         {
 814  1
             String pass = "-J-Dhttp.proxyPassword=\"" + activeProxy.getPassword() + "\"";
 815  1
             String hidepass =
 816  
                 "-J-Dhttp.proxyPassword=\"" + StringUtils.repeat( "*", activeProxy.getPassword().length() ) + "\"";
 817  
 
 818  1
             return StringUtils.replace( cmdLine, pass, hidepass );
 819  
         }
 820  
 
 821  0
         return cmdLine;
 822  
     }
 823  
 
 824  
     /**
 825  
      * Auto-detect the class names of the implementation of <code>com.sun.tools.doclets.Taglet</code> class from a
 826  
      * given jar file.
 827  
      * <br/>
 828  
      * <b>Note</b>: <code>JAVA_HOME/lib/tools.jar</code> is a requirement to find
 829  
      * <code>com.sun.tools.doclets.Taglet</code> class.
 830  
      *
 831  
      * @param jarFile not null
 832  
      * @return the list of <code>com.sun.tools.doclets.Taglet</code> class names from a given jarFile.
 833  
      * @throws IOException if jarFile is invalid or not found, or if the <code>JAVA_HOME/lib/tools.jar</code>
 834  
      * is not found.
 835  
      * @throws ClassNotFoundException if any
 836  
      * @throws NoClassDefFoundError if any
 837  
      */
 838  
     protected static List getTagletClassNames( File jarFile )
 839  
         throws IOException, ClassNotFoundException, NoClassDefFoundError
 840  
     {
 841  2
         List classes = getClassNamesFromJar( jarFile );
 842  
         ClassLoader cl;
 843  
 
 844  
         // Needed to find com.sun.tools.doclets.Taglet class
 845  2
         File tools = new File( System.getProperty( "java.home" ), "../lib/tools.jar" );
 846  2
         if ( tools.exists() && tools.isFile() )
 847  
         {
 848  2
             cl = new URLClassLoader( new URL[] { jarFile.toURI().toURL(), tools.toURI().toURL() }, null );
 849  
         }
 850  
         else
 851  
         {
 852  0
             cl = new URLClassLoader( new URL[] { jarFile.toURI().toURL() }, null );
 853  
         }
 854  
 
 855  2
         List tagletClasses = new ArrayList();
 856  
 
 857  2
         Class tagletClass = cl.loadClass( "com.sun.tools.doclets.Taglet" );
 858  2
         for ( Iterator it = classes.iterator(); it.hasNext(); )
 859  
         {
 860  24
             String s = (String) it.next();
 861  
 
 862  24
             Class c = cl.loadClass( s );
 863  
 
 864  24
             if ( tagletClass.isAssignableFrom( c ) && !Modifier.isAbstract( c.getModifiers() ) )
 865  
             {
 866  20
                 tagletClasses.add( c.getName() );
 867  
             }
 868  24
         }
 869  
 
 870  2
         return tagletClasses;
 871  
     }
 872  
 
 873  
     // ----------------------------------------------------------------------
 874  
     // private methods
 875  
     // ----------------------------------------------------------------------
 876  
 
 877  
     /**
 878  
      * @param jarFile not null
 879  
      * @return all class names from the given jar file.
 880  
      * @throws IOException if any or if the jarFile is null or doesn't exist.
 881  
      */
 882  
     private static List getClassNamesFromJar( File jarFile )
 883  
         throws IOException
 884  
     {
 885  2
         if ( jarFile == null || !jarFile.exists() || !jarFile.isFile() )
 886  
         {
 887  0
             throw new IOException( "The jar '" + jarFile + "' doesn't exist or is not a file." );
 888  
         }
 889  
 
 890  2
         List classes = new ArrayList();
 891  2
         JarInputStream jarStream = null;
 892  
 
 893  
         try
 894  
         {
 895  2
             jarStream = new JarInputStream( new FileInputStream( jarFile ) );
 896  2
             JarEntry jarEntry = jarStream.getNextJarEntry();
 897  48
             while ( jarEntry != null )
 898  
             {
 899  46
                 if ( jarEntry == null )
 900  
                 {
 901  0
                     break;
 902  
                 }
 903  
 
 904  46
                 if ( jarEntry.getName().toLowerCase( Locale.ENGLISH ).endsWith( ".class" ) )
 905  
                 {
 906  24
                     String name = jarEntry.getName().substring( 0, jarEntry.getName().indexOf( "." ) );
 907  
 
 908  24
                     classes.add( name.replaceAll( "/", "\\." ) );
 909  
                 }
 910  
 
 911  46
                 jarStream.closeEntry();
 912  46
                 jarEntry = jarStream.getNextJarEntry();
 913  
             }
 914  
         }
 915  
         finally
 916  
         {
 917  2
             IOUtil.close( jarStream );
 918  2
         }
 919  
 
 920  2
         return classes;
 921  
     }
 922  
 }