Coverage Report - org.apache.maven.plugin.javadoc.AbstractFixJavadocMojo
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractFixJavadocMojo
77%
809/1054
59%
423/722
5.552
AbstractFixJavadocMojo$JavaEntityTags
55%
24/44
43%
6/14
5.552
 
 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.BufferedReader;
 23  
 import java.io.File;
 24  
 import java.io.IOException;
 25  
 import java.io.InputStream;
 26  
 import java.io.StringReader;
 27  
 import java.io.StringWriter;
 28  
 import java.io.Writer;
 29  
 import java.lang.reflect.Method;
 30  
 import java.net.MalformedURLException;
 31  
 import java.net.URL;
 32  
 import java.net.URLClassLoader;
 33  
 import java.util.ArrayList;
 34  
 import java.util.Arrays;
 35  
 import java.util.Collections;
 36  
 import java.util.Iterator;
 37  
 import java.util.LinkedHashMap;
 38  
 import java.util.LinkedList;
 39  
 import java.util.List;
 40  
 import java.util.Locale;
 41  
 import java.util.Map;
 42  
 import java.util.Properties;
 43  
 import java.util.StringTokenizer;
 44  
 import java.util.regex.Pattern;
 45  
 
 46  
 import org.apache.commons.lang.ClassUtils;
 47  
 import org.apache.maven.artifact.Artifact;
 48  
 import org.apache.maven.artifact.DependencyResolutionRequiredException;
 49  
 import org.apache.maven.artifact.repository.ArtifactRepository;
 50  
 import org.apache.maven.plugin.AbstractMojo;
 51  
 import org.apache.maven.plugin.MojoExecutionException;
 52  
 import org.apache.maven.plugin.MojoFailureException;
 53  
 import org.apache.maven.project.MavenProject;
 54  
 import org.apache.maven.settings.Settings;
 55  
 import org.apache.maven.shared.invoker.MavenInvocationException;
 56  
 import org.codehaus.plexus.components.interactivity.InputHandler;
 57  
 import org.codehaus.plexus.util.FileUtils;
 58  
 import org.codehaus.plexus.util.IOUtil;
 59  
 import org.codehaus.plexus.util.ReaderFactory;
 60  
 import org.codehaus.plexus.util.StringUtils;
 61  
 import org.codehaus.plexus.util.WriterFactory;
 62  
 
 63  
 import com.thoughtworks.qdox.JavaDocBuilder;
 64  
 import com.thoughtworks.qdox.model.AbstractInheritableJavaEntity;
 65  
 import com.thoughtworks.qdox.model.AbstractJavaEntity;
 66  
 import com.thoughtworks.qdox.model.Annotation;
 67  
 import com.thoughtworks.qdox.model.DocletTag;
 68  
 import com.thoughtworks.qdox.model.JavaClass;
 69  
 import com.thoughtworks.qdox.model.JavaField;
 70  
 import com.thoughtworks.qdox.model.JavaMethod;
 71  
 import com.thoughtworks.qdox.model.JavaParameter;
 72  
 import com.thoughtworks.qdox.model.Type;
 73  
 import com.thoughtworks.qdox.model.TypeVariable;
 74  
 import com.thoughtworks.qdox.parser.ParseException;
 75  
 
 76  
 /**
 77  
  * Abstract class to fix Javadoc documentation and tags in source files.
 78  
  * <br/>
 79  
  * See <a href="http://java.sun.com/j2se/1.5.0/docs/tooldocs/windows/javadoc.html#wheretags">Where Tags Can Be Used</a>.
 80  
  *
 81  
  * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
 82  
  * @version $Id$
 83  
  * @since 2.6
 84  
  */
 85  2
 public abstract class AbstractFixJavadocMojo
 86  
     extends AbstractMojo
 87  
 {
 88  
     /** The vm line separator */
 89  1
     private static final String EOL = System.getProperty( "line.separator" );
 90  
 
 91  
     /** Tag name for &#64;author **/
 92  
     private static final String AUTHOR_TAG = "author";
 93  
 
 94  
     /** Tag name for &#64;version **/
 95  
     private static final String VERSION_TAG = "version";
 96  
 
 97  
     /** Tag name for &#64;since **/
 98  
     private static final String SINCE_TAG = "since";
 99  
 
 100  
     /** Tag name for &#64;param **/
 101  
     private static final String PARAM_TAG = "param";
 102  
 
 103  
     /** Tag name for &#64;return **/
 104  
     private static final String RETURN_TAG = "return";
 105  
 
 106  
     /** Tag name for &#64;throws **/
 107  
     private static final String THROWS_TAG = "throws";
 108  
 
 109  
     /** Tag name for {&#64;inheritDoc} **/
 110  
     private static final String INHERITED_TAG = "{@inheritDoc}";
 111  
 
 112  
     /** Start Javadoc String i.e. <code>&#47;&#42;&#42;</code> **/
 113  
     private static final String START_JAVADOC = "/**";
 114  
 
 115  
     /** End Javadoc String i.e. <code>&#42;&#47;</code> **/
 116  
     private static final String END_JAVADOC = "*/";
 117  
 
 118  
     /** Javadoc Separator i.e. <code> &#42; </code> **/
 119  
     private static final String SEPARATOR_JAVADOC = " * ";
 120  
 
 121  
     /** Inherited Javadoc i.e. <code>&#47;&#42;&#42; {&#64;inheritDoc} &#42;&#47;</code> **/
 122  
     private static final String INHERITED_JAVADOC = START_JAVADOC + " " + INHERITED_TAG + " " + END_JAVADOC;
 123  
 
 124  
     /** <code>all</code> parameter used by {@link #fixTags} **/
 125  
     private static final String FIX_TAGS_ALL = "all";
 126  
 
 127  
     /** <code>public</code> parameter used by {@link #level} **/
 128  
     private static final String LEVEL_PUBLIC = "public";
 129  
 
 130  
     /** <code>protected</code> parameter used by {@link #level} **/
 131  
     private static final String LEVEL_PROTECTED = "protected";
 132  
 
 133  
     /** <code>package</code> parameter used by {@link #level} **/
 134  
     private static final String LEVEL_PACKAGE = "package";
 135  
 
 136  
     /** <code>private</code> parameter used by {@link #level} **/
 137  
     private static final String LEVEL_PRIVATE = "private";
 138  
 
 139  
     /** The Clirr Maven plugin groupId <code>org.codehaus.mojo</code> **/
 140  
     private static final String CLIRR_MAVEN_PLUGIN_GROUPID = "org.codehaus.mojo";
 141  
 
 142  
     /** The Clirr Maven plugin artifactId <code>clirr-maven-plugin</code> **/
 143  
     private static final String CLIRR_MAVEN_PLUGIN_ARTIFACTID = "clirr-maven-plugin";
 144  
 
 145  
     /** The latest Clirr Maven plugin version <code>2.2.2</code> **/
 146  
     private static final String CLIRR_MAVEN_PLUGIN_VERSION = "2.2.2";
 147  
 
 148  
     /** The Clirr Maven plugin goal <code>check</code> **/
 149  
     private static final String CLIRR_MAVEN_PLUGIN_GOAL = "check";
 150  
 
 151  
     // ----------------------------------------------------------------------
 152  
     // Mojo components
 153  
     // ----------------------------------------------------------------------
 154  
 
 155  
     /**
 156  
      * Input handler, needed for command line handling.
 157  
      *
 158  
      * @component
 159  
      */
 160  
     private InputHandler inputHandler;
 161  
 
 162  
     // ----------------------------------------------------------------------
 163  
     // Mojo parameters
 164  
     // ----------------------------------------------------------------------
 165  
 
 166  
     /**
 167  
      * Version to compare the current code against using the
 168  
      * <a href="http://mojo.codehaus.org/clirr-maven-plugin/">Clirr Maven Plugin</a>.
 169  
      * <br/>
 170  
      * See <a href="#defaultSince">defaultSince</a>.
 171  
      *
 172  
      * @parameter expression="${comparisonVersion}" default-value="(,${project.version})"
 173  
      */
 174  
     private String comparisonVersion;
 175  
 
 176  
     /**
 177  
      * Default value for the Javadoc tag <code>&#64;author</code>.
 178  
      * <br/>
 179  
      * If not specified, the <code>user.name</code> defined in the System properties will be used.
 180  
      *
 181  
      * @parameter expression="${defaultAuthor}"
 182  
      */
 183  
     private String defaultAuthor;
 184  
 
 185  
     /**
 186  
      * Default value for the Javadoc tag <code>&#64;since</code>.
 187  
      * <br/>
 188  
      *
 189  
      * @parameter expression="${defaultSince}" default-value="${project.version}"
 190  
      */
 191  
     private String defaultSince;
 192  
 
 193  
     /**
 194  
      * Default value for the Javadoc tag <code>&#64;version</code>.
 195  
      * <br/>
 196  
      * By default, it is <code>&#36;Id:&#36;</code>, corresponding to a
 197  
      * <a href="http://svnbook.red-bean.com/en/1.1/ch07s02.html#svn-ch-7-sect-2.3.4">SVN keyword</a>.
 198  
      * Refer to your SCM to use an other SCM keyword.
 199  
      *
 200  
      * @parameter expression="${defaultVersion}"
 201  
      */
 202  2
     private String defaultVersion = "\u0024Id: \u0024"; // can't use default-value="\u0024Id: \u0024"
 203  
 
 204  
     /**
 205  
      * The file encoding to use when reading the source files. If the property
 206  
      * <code>project.build.sourceEncoding</code> is not set, the platform default encoding is used.
 207  
      *
 208  
      * @parameter expression="${encoding}" default-value="${project.build.sourceEncoding}"
 209  
      */
 210  
     private String encoding;
 211  
 
 212  
     /**
 213  
      * Comma separated excludes Java files, i.e. <code>&#42;&#42;/&#42;Test.java</code>.
 214  
      *
 215  
      * @parameter expression="${excludes}"
 216  
      */
 217  
     private String excludes;
 218  
 
 219  
     /**
 220  
      * Comma separated tags to fix in classes, interfaces or methods Javadoc comments.
 221  
      * Possible values are:
 222  
      * <ul>
 223  
      * <li>all (fix all Javadoc tags)</li>
 224  
      * <li>author (fix only &#64;author tag)</li>
 225  
      * <li>version (fix only &#64;version tag)</li>
 226  
      * <li>since (fix only &#64;since tag)</li>
 227  
      * <li>param (fix only &#64;param tag)</li>
 228  
      * <li>return (fix only &#64;return tag)</li>
 229  
      * <li>throws (fix only &#64;throws tag)</li>
 230  
      * </ul>
 231  
      *
 232  
      * @parameter expression="${fixTags}" default-value="all"
 233  
      */
 234  
     private String fixTags;
 235  
 
 236  
     /**
 237  
      * Flag to fix the classes or interfaces Javadoc comments according the <code>level</code>.
 238  
      *
 239  
      * @parameter expression="${fixClassComment}" default-value="true"
 240  
      */
 241  
     private boolean fixClassComment;
 242  
 
 243  
     /**
 244  
      * Flag to fix the fields Javadoc comments according the <code>level</code>.
 245  
      *
 246  
      * @parameter expression="${fixFieldComment}" default-value="true"
 247  
      */
 248  
     private boolean fixFieldComment;
 249  
 
 250  
     /**
 251  
      * Flag to fix the methods Javadoc comments according the <code>level</code>.
 252  
      *
 253  
      * @parameter expression="${fixMethodComment}" default-value="true"
 254  
      */
 255  
     private boolean fixMethodComment;
 256  
 
 257  
     /**
 258  
      * Forcing the goal execution i.e. skip warranty messages (not recommended).
 259  
      *
 260  
      * @parameter expression="${force}"
 261  
      */
 262  
     private boolean force;
 263  
 
 264  
     /**
 265  
      * Flag to ignore or not Clirr.
 266  
      *
 267  
      * @parameter expression="${ignoreClirr}" default-value="false"
 268  
      */
 269  
     protected boolean ignoreClirr;
 270  
 
 271  
     /**
 272  
      * Comma separated includes Java files, i.e. <code>&#42;&#42;/&#42;Test.java</code>.
 273  
      *
 274  
      * @parameter expression="${includes}" default-value="**\/*.java"
 275  
      */
 276  
     private String includes;
 277  
 
 278  
     /**
 279  
      * Specifies the access level for classes and members to show in the Javadocs.
 280  
      * Possible values are:
 281  
      * <ul>
 282  
      * <li><a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/javadoc.html#public">public</a>
 283  
      * (shows only public classes and members)</li>
 284  
      * <li><a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/javadoc.html#protected">protected</a>
 285  
      * (shows only public and protected classes and members)</li>
 286  
      * <li><a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/javadoc.html#package">package</a>
 287  
      * (shows all classes and members not marked private)</li>
 288  
      * <li><a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/javadoc.html#private">private</a>
 289  
      * (shows all classes and members)</li>
 290  
      * </ul>
 291  
      * <br/>
 292  
      *
 293  
      * @parameter expression="${level}" default-value="protected"
 294  
      */
 295  
     private String level;
 296  
 
 297  
     /**
 298  
      * The local repository where the artifacts are located, used by the tests.
 299  
      *
 300  
      * @parameter expression="${localRepository}"
 301  
      */
 302  
     private ArtifactRepository localRepository;
 303  
 
 304  
     /**
 305  
      * Output directory where Java classes will be rewrited.
 306  
      *
 307  
      * @parameter expression="${outputDirectory}" default-value="${project.build.sourceDirectory}"
 308  
      */
 309  
     private File outputDirectory;
 310  
 
 311  
     /**
 312  
      * The Maven Project Object.
 313  
      *
 314  
      * @parameter expression="${project}"
 315  
      * @required
 316  
      * @readonly
 317  
      */
 318  
     private MavenProject project;
 319  
 
 320  
     /**
 321  
      * The current user system settings for use in Maven.
 322  
      *
 323  
      * @parameter expression="${settings}"
 324  
      * @required
 325  
      * @readonly
 326  
      */
 327  
     private Settings settings;
 328  
 
 329  
     // ----------------------------------------------------------------------
 330  
     // Internal fields
 331  
     // ----------------------------------------------------------------------
 332  
 
 333  
     /** The current project class loader. */
 334  
     private ClassLoader projectClassLoader;
 335  
 
 336  
     /**
 337  
      * Split {@link #fixTags} by comma.
 338  
      * @see {@link #init()}
 339  
      */
 340  
     private String[] fixTagsSplitted;
 341  
 
 342  
     /** New classes found by Clirr. */
 343  
     private List clirrNewClasses;
 344  
 
 345  
     /** New Methods in a Class (the key) found by Clirr. */
 346  
     private Map clirrNewMethods;
 347  
 
 348  
     /** List of classes where <code>&#42;since</code> is added. Will be used to add or not this tag in the methods. */
 349  
     private List sinceClasses;
 350  
 
 351  
     /** {@inheritDoc} */
 352  
     public void execute()
 353  
         throws MojoExecutionException, MojoFailureException
 354  
     {
 355  2
         if ( !fixClassComment && !fixFieldComment && !fixMethodComment )
 356  
         {
 357  0
             getLog().info( "Specified to NOT fix classes, fields and methods. Nothing to do." );
 358  0
             return;
 359  
         }
 360  
 
 361  
         // verify goal params
 362  2
         init();
 363  
 
 364  2
         if ( fixTagsSplitted.length == 0 )
 365  
         {
 366  0
             getLog().info( "No fix tag specified. Nothing to do." );
 367  0
             return;
 368  
         }
 369  
 
 370  
         // add warranty msg
 371  2
         if ( !preCheck() )
 372  
         {
 373  0
             return;
 374  
         }
 375  
 
 376  
         // run clirr
 377  
         try
 378  
         {
 379  2
             executeClirr();
 380  
         }
 381  0
         catch ( MavenInvocationException e )
 382  
         {
 383  0
             if ( getLog().isDebugEnabled() )
 384  
             {
 385  0
                 getLog().error( "MavenInvocationException: " + e.getMessage(), e );
 386  
             }
 387  
             else
 388  
             {
 389  0
                 getLog().error( "MavenInvocationException: " + e.getMessage() );
 390  
             }
 391  0
             getLog().info( "Clirr is ignored." );
 392  2
         }
 393  
 
 394  
         // run qdox and process
 395  
         try
 396  
         {
 397  2
             JavaClass[] javaClasses = getQdoxClasses();
 398  
 
 399  2
             if ( javaClasses != null )
 400  
             {
 401  14
                 for ( int i = 0; i < javaClasses.length; i++ )
 402  
                 {
 403  12
                     JavaClass javaClass = javaClasses[i];
 404  
 
 405  12
                     processFix( javaClass );
 406  
                 }
 407  
             }
 408  
         }
 409  0
         catch ( IOException e )
 410  
         {
 411  0
             throw new MojoExecutionException( "IOException: " + e.getMessage(), e );
 412  2
         }
 413  2
     }
 414  
 
 415  
     // ----------------------------------------------------------------------
 416  
     // protected methods
 417  
     // ----------------------------------------------------------------------
 418  
 
 419  
     /**
 420  
      * @param p not null maven project.
 421  
      * @return the artifact type.
 422  
      */
 423  
     protected String getArtifactType( MavenProject p )
 424  
     {
 425  0
         return p.getArtifact().getType();
 426  
     }
 427  
 
 428  
     /**
 429  
      * @param p not null maven project.
 430  
      * @return the list of source paths for the given project.
 431  
      */
 432  
     protected List getProjectSourceRoots( MavenProject p )
 433  
     {
 434  2
         return ( p.getCompileSourceRoots() == null ? Collections.EMPTY_LIST
 435  
                         : new LinkedList( p.getCompileSourceRoots() ) );
 436  
     }
 437  
 
 438  
     /**
 439  
      * @param p not null
 440  
      * @return the compile classpath elements
 441  
      * @throws DependencyResolutionRequiredException if any
 442  
      */
 443  
     protected List getCompileClasspathElements( MavenProject p )
 444  
         throws DependencyResolutionRequiredException
 445  
     {
 446  2
         return ( p.getCompileClasspathElements() == null ? Collections.EMPTY_LIST
 447  
                         : new LinkedList( p.getCompileClasspathElements() ) );
 448  
     }
 449  
 
 450  
     /**
 451  
      * @param javaMethod not null
 452  
      * @return the fully qualify name of javaMethod with signature
 453  
      */
 454  
     protected static String getJavaMethodAsString( JavaMethod javaMethod )
 455  
     {
 456  5
         StringBuffer sb = new StringBuffer();
 457  
 
 458  5
         sb.append( javaMethod.getParentClass().getFullyQualifiedName() );
 459  5
         sb.append( "#" ).append( javaMethod.getCallSignature() );
 460  
 
 461  5
         return sb.toString();
 462  
     }
 463  
 
 464  
     // ----------------------------------------------------------------------
 465  
     // private methods
 466  
     // ----------------------------------------------------------------------
 467  
 
 468  
     /**
 469  
      * Init goal parameters.
 470  
      */
 471  
     private void init()
 472  
     {
 473  
         // defaultAuthor
 474  2
         if ( StringUtils.isEmpty( defaultAuthor ) )
 475  
         {
 476  0
             defaultAuthor = System.getProperty( "user.name" );
 477  
         }
 478  
 
 479  
         // defaultSince
 480  2
         int i = defaultSince.indexOf( "-" + Artifact.SNAPSHOT_VERSION );
 481  2
         if ( i != -1 )
 482  
         {
 483  2
             defaultSince = defaultSince.substring( 0, i );
 484  
         }
 485  
 
 486  
         // fixTags
 487  2
         if ( !FIX_TAGS_ALL.equalsIgnoreCase( fixTags.trim() ) )
 488  
         {
 489  0
             String[] split = StringUtils.split( fixTags, "," );
 490  0
             List filtered = new LinkedList();
 491  0
             for ( int j = 0; j < split.length; j++ )
 492  
             {
 493  0
                 String s = split[j].trim();
 494  0
                 if ( FIX_TAGS_ALL.equalsIgnoreCase( s.trim() ) || AUTHOR_TAG.equalsIgnoreCase( s.trim() )
 495  
                     || VERSION_TAG.equalsIgnoreCase( s.trim() ) || SINCE_TAG.equalsIgnoreCase( s.trim() )
 496  
                     || PARAM_TAG.equalsIgnoreCase( s.trim() ) || RETURN_TAG.equalsIgnoreCase( s.trim() )
 497  
                     || THROWS_TAG.equalsIgnoreCase( s.trim() ) )
 498  
                 {
 499  0
                     filtered.add( s );
 500  
                 }
 501  
                 else
 502  
                 {
 503  0
                     if ( getLog().isWarnEnabled() )
 504  
                     {
 505  0
                         getLog().warn( "Unrecognized '" + s + "' for fixTags parameter. Ignored it!" );
 506  
                     }
 507  
                 }
 508  
             }
 509  0
             fixTags = StringUtils.join( filtered.iterator(), "," );
 510  
         }
 511  2
         fixTagsSplitted = StringUtils.split( fixTags, "," );
 512  
 
 513  
         // encoding
 514  2
         if ( StringUtils.isEmpty( encoding ) )
 515  
         {
 516  0
             if ( getLog().isWarnEnabled() )
 517  
             {
 518  0
                 getLog().warn(
 519  
                                "File encoding has not been set, using platform encoding "
 520  
                                    + ReaderFactory.FILE_ENCODING + ", i.e. build is platform dependent!" );
 521  
             }
 522  0
             encoding = ReaderFactory.FILE_ENCODING;
 523  
         }
 524  
 
 525  
         // level
 526  2
         if ( !( LEVEL_PUBLIC.equalsIgnoreCase( level.trim() ) || LEVEL_PROTECTED.equalsIgnoreCase( level.trim() )
 527  
             || LEVEL_PACKAGE.equalsIgnoreCase( level.trim() ) || LEVEL_PRIVATE.equalsIgnoreCase( level.trim() ) ) )
 528  
         {
 529  0
             if ( getLog().isWarnEnabled() )
 530  
             {
 531  0
                 getLog().warn( "Unrecognized '" + level + "' for level parameter, using 'protected' level." );
 532  
             }
 533  0
             level = "protected";
 534  
         }
 535  2
     }
 536  
 
 537  
     /**
 538  
      * @return <code>true</code> if the user wants to proceed, <code>false</code> otherwise.
 539  
      * @throws MojoExecutionException if any
 540  
      */
 541  
     private boolean preCheck()
 542  
         throws MojoExecutionException
 543  
     {
 544  2
         if ( force )
 545  
         {
 546  2
             return true;
 547  
         }
 548  
 
 549  0
         if ( outputDirectory != null
 550  
             && !outputDirectory.getAbsolutePath().equals( getProjectSourceDirectory().getAbsolutePath() ) )
 551  
         {
 552  0
             return true;
 553  
         }
 554  
 
 555  0
         if ( !settings.isInteractiveMode() )
 556  
         {
 557  0
             getLog().error(
 558  
                             "Maven is not attempt to interact with the user for input. "
 559  
                                 + "Verify the <interactiveMode/> configuration in your settings." );
 560  0
             return false;
 561  
         }
 562  
 
 563  0
         getLog().warn( "" );
 564  0
         getLog().warn( "    WARRANTY DISCLAIMER" );
 565  0
         getLog().warn( "" );
 566  0
         getLog().warn( "All warranties with regard to this Maven goal are disclaimed!" );
 567  0
         getLog().warn( "The changes will be done directly in the source code." );
 568  0
         getLog().warn(
 569  
                        "The Maven Team strongly recommends the use of a SCM software BEFORE executing this "
 570  
                            + "goal." );
 571  0
         getLog().warn( "" );
 572  
 
 573  
         while ( true )
 574  
         {
 575  0
             getLog().info( "Are you sure to proceed? [Y]es [N]o" );
 576  
 
 577  
             try
 578  
             {
 579  0
                 String userExpression = inputHandler.readLine();
 580  0
                 if ( userExpression == null || userExpression.toLowerCase( Locale.ENGLISH ).equalsIgnoreCase( "Y" )
 581  
                     || userExpression.toLowerCase( Locale.ENGLISH ).equalsIgnoreCase( "Yes" ) )
 582  
                 {
 583  0
                     getLog().info( "OK, let's proceed..." );
 584  0
                     break;
 585  
                 }
 586  0
                 if ( userExpression == null || userExpression.toLowerCase( Locale.ENGLISH ).equalsIgnoreCase( "N" )
 587  
                     || userExpression.toLowerCase( Locale.ENGLISH ).equalsIgnoreCase( "No" ) )
 588  
                 {
 589  0
                     getLog().info( "No changes in your sources occur." );
 590  0
                     return false;
 591  
                 }
 592  
             }
 593  0
             catch ( IOException e )
 594  
             {
 595  0
                 throw new MojoExecutionException( "Unable to read from standard input.", e );
 596  0
             }
 597  
         }
 598  
 
 599  0
         return true;
 600  
     }
 601  
 
 602  
     /**
 603  
      * @return the source dir as File for the given project
 604  
      */
 605  
     private File getProjectSourceDirectory()
 606  
     {
 607  9
         return new File( project.getBuild().getSourceDirectory() );
 608  
     }
 609  
 
 610  
     /**
 611  
      * Invoke Maven to run clirr-maven-plugin to find API differences.
 612  
      *
 613  
      * @throws MavenInvocationException if any
 614  
      */
 615  
     private void executeClirr()
 616  
         throws MavenInvocationException
 617  
     {
 618  2
         if ( ignoreClirr )
 619  
         {
 620  0
             getLog().info( "Clirr is ignored." );
 621  0
             return;
 622  
         }
 623  
 
 624  2
         String clirrGoal = getFullClirrGoal();
 625  
 
 626  
         // http://mojo.codehaus.org/clirr-maven-plugin/check-mojo.html
 627  2
         File clirrTextOutputFile =
 628  
             FileUtils.createTempFile( "clirr", ".txt", new File( project.getBuild().getDirectory() ) );
 629  2
         Properties properties = new Properties();
 630  2
         properties.put( "textOutputFile", clirrTextOutputFile.getAbsolutePath() );
 631  2
         properties.put( "comparisonVersion", comparisonVersion );
 632  2
         properties.put( "failOnError", "false" );
 633  
 
 634  2
         File invokerDir = new File( project.getBuild().getDirectory(), "invoker" );
 635  2
         invokerDir.mkdirs();
 636  2
         File invokerLogFile = FileUtils.createTempFile( "clirr-maven-plugin", ".txt", invokerDir );
 637  2
         new File( project.getBuild().getDirectory(), "invoker-clirr-maven-plugin.txt" );
 638  2
         JavadocUtil.invokeMaven( getLog(), new File( localRepository.getBasedir() ), project.getFile(),
 639  
                                  Collections.singletonList( clirrGoal ), properties, invokerLogFile );
 640  
 
 641  
         try
 642  
         {
 643  2
             if ( invokerLogFile.exists() )
 644  
             {
 645  2
                 String invokerLogContent = StringUtils.unifyLineSeparators( FileUtils.fileRead( invokerLogFile, "UTF-8" ) );
 646  
                 // see org.codehaus.mojo.clirr.AbstractClirrMojo#getComparisonArtifact()
 647  2
                 final String artifactNotFoundMsg =
 648  
                     "Unable to find a previous version of the project in the repository";
 649  2
                 if ( invokerLogContent.indexOf( artifactNotFoundMsg ) != -1 )
 650  
                 {
 651  0
                     getLog().warn( "No previous artifact has been deployed, Clirr is ignored." );
 652  0
                     return;
 653  
                 }
 654  
             }
 655  
         }
 656  0
         catch ( IOException e )
 657  
         {
 658  0
             getLog().debug( "IOException: " + e.getMessage() );
 659  2
         }
 660  
 
 661  
         try
 662  
         {
 663  2
             parseClirrTextOutputFile( clirrTextOutputFile );
 664  
         }
 665  0
         catch ( IOException e )
 666  
         {
 667  0
             if ( getLog().isDebugEnabled() )
 668  
             {
 669  0
                 getLog().debug( "IOException: " + e.getMessage(), e );
 670  
             }
 671  0
             getLog().info(
 672  
                            "IOException when parsing Clirr output '" + clirrTextOutputFile.getAbsolutePath()
 673  
                                + "', Clirr is ignored." );
 674  2
         }
 675  2
     }
 676  
 
 677  
     /**
 678  
      * @param clirrTextOutputFile not null
 679  
      * @throws IOException if any
 680  
      */
 681  
     private void parseClirrTextOutputFile( File clirrTextOutputFile )
 682  
         throws IOException
 683  
     {
 684  2
         if ( !clirrTextOutputFile.exists() )
 685  
         {
 686  0
             if ( getLog().isInfoEnabled() )
 687  
             {
 688  0
                 getLog().info(
 689  
                                "No Clirr output file '" + clirrTextOutputFile.getAbsolutePath()
 690  
                                    + "' exists, Clirr is ignored." );
 691  
             }
 692  0
             return;
 693  
         }
 694  
 
 695  2
         if ( getLog().isInfoEnabled() )
 696  
         {
 697  2
             getLog().info( "Clirr output file was created: " + clirrTextOutputFile.getAbsolutePath() );
 698  
         }
 699  
 
 700  2
         clirrNewClasses = new LinkedList();
 701  2
         clirrNewMethods = new LinkedHashMap();
 702  
 
 703  2
         BufferedReader input = new BufferedReader( ReaderFactory.newReader( clirrTextOutputFile, "UTF-8" ) );
 704  2
         String line = null;
 705  18
         while ( ( line = input.readLine() ) != null )
 706  
         {
 707  16
             String[] split = StringUtils.split( line, ":" );
 708  16
             if ( split.length != 4 )
 709  
             {
 710  0
                 if ( getLog().isDebugEnabled() )
 711  
                 {
 712  0
                     getLog().debug( "Unable to parse the clirr line: " + line );
 713  
                 }
 714  
                 continue;
 715  
             }
 716  
 
 717  
             int code;
 718  
             try
 719  
             {
 720  16
                 code = Integer.parseInt( split[1].trim() );
 721  
             }
 722  0
             catch ( NumberFormatException e )
 723  
             {
 724  0
                 if ( getLog().isDebugEnabled() )
 725  
                 {
 726  0
                     getLog().debug( "Unable to parse the clirr line: " + line );
 727  
                 }
 728  0
                 continue;
 729  16
             }
 730  
 
 731  
             // http://clirr.sourceforge.net/clirr-core/exegesis.html
 732  
             // 7011 - Method Added
 733  
             // 7012 - Method Added to Interface
 734  
             // 8000 - Class Added
 735  
             List list;
 736  
             String[] splits2;
 737  16
             switch ( code )
 738  
             {
 739  
                 case 7011:
 740  11
                     list = (List) clirrNewMethods.get( split[2].trim() );
 741  11
                     if ( list == null )
 742  
                     {
 743  4
                         list = new ArrayList();
 744  
                     }
 745  11
                     splits2 = StringUtils.split( split[3].trim(), "'" );
 746  11
                     if ( splits2.length != 3 )
 747  
                     {
 748  0
                         continue;
 749  
                     }
 750  11
                     list.add( splits2[1].trim() );
 751  11
                     clirrNewMethods.put( split[2].trim(), list );
 752  11
                     break;
 753  
 
 754  
                 case 7012:
 755  4
                     list = (List) clirrNewMethods.get( split[2].trim() );
 756  4
                     if ( list == null )
 757  
                     {
 758  4
                         list = new ArrayList();
 759  
                     }
 760  4
                     splits2 = StringUtils.split( split[3].trim(), "'" );
 761  4
                     if ( splits2.length != 3 )
 762  
                     {
 763  0
                         continue;
 764  
                     }
 765  4
                     list.add( splits2[1].trim() );
 766  4
                     clirrNewMethods.put( split[2].trim(), list );
 767  4
                     break;
 768  
 
 769  
                 case 8000:
 770  0
                     clirrNewClasses.add( split[2].trim() );
 771  0
                     break;
 772  
                 default:
 773  
                     break;
 774  
             }
 775  16
         }
 776  
 
 777  2
         if ( clirrNewClasses.isEmpty() && clirrNewMethods.isEmpty() )
 778  
         {
 779  0
             getLog().info( "Clirr NOT found API differences." );
 780  
         }
 781  
         else
 782  
         {
 783  2
             getLog().info( "Clirr found API differences, i.e. new classes/interfaces or methods." );
 784  
         }
 785  2
     }
 786  
 
 787  
     /**
 788  
      * @param tag not null
 789  
      * @return <code>true</code> if <code>tag</code> is defined in {@link #fixTags}.
 790  
      */
 791  
     private boolean fixTag( String tag )
 792  
     {
 793  406
         if ( fixTagsSplitted.length == 1 && fixTagsSplitted[0].equals( FIX_TAGS_ALL ) )
 794  
         {
 795  406
             return true;
 796  
         }
 797  
 
 798  0
         for ( int i = 0; i < fixTagsSplitted.length; i++ )
 799  
         {
 800  0
             if ( fixTagsSplitted[i].trim().equals( tag ) )
 801  
             {
 802  0
                 return true;
 803  
             }
 804  
         }
 805  
 
 806  0
         return false;
 807  
     }
 808  
 
 809  
     /**
 810  
      * Calling Qdox to find {@link JavaClass} objects from the Maven project sources.
 811  
      * Ignore java class if Qdox has parsing errors.
 812  
      *
 813  
      * @return an array of {@link JavaClass} found by QDox
 814  
      * @throws IOException if any
 815  
      * @throws MojoExecutionException if any
 816  
      */
 817  
     private JavaClass[] getQdoxClasses()
 818  
         throws IOException, MojoExecutionException
 819  
     {
 820  2
         if ( "pom".equals( project.getPackaging().toLowerCase() ) )
 821  
         {
 822  0
             getLog().warn( "This project has 'pom' packaging, no Java sources is available." );
 823  0
             return null;
 824  
         }
 825  
 
 826  2
         List javaFiles = new LinkedList();
 827  2
         for ( Iterator i = getProjectSourceRoots( project ).iterator(); i.hasNext(); )
 828  
         {
 829  4
             File f = new File( (String) i.next() );
 830  4
             if ( f.isDirectory() )
 831  
             {
 832  4
                 javaFiles.addAll( FileUtils.getFiles( f, includes, excludes, true ) );
 833  
             }
 834  
             else
 835  
             {
 836  0
                 if ( getLog().isWarnEnabled() )
 837  
                 {
 838  0
                     getLog().warn( f + " doesn't exist. Ignored it." );
 839  
                 }
 840  
             }
 841  4
         }
 842  
 
 843  2
         JavaDocBuilder builder = new JavaDocBuilder();
 844  2
         builder.getClassLibrary().addClassLoader( getProjectClassLoader() );
 845  2
         builder.setEncoding( encoding );
 846  2
         for ( Iterator i = javaFiles.iterator(); i.hasNext(); )
 847  
         {
 848  9
             File f = (File) i.next();
 849  9
             if ( !f.getAbsolutePath().toLowerCase( Locale.ENGLISH ).endsWith( ".java" )
 850  
                 && getLog().isWarnEnabled() )
 851  
             {
 852  0
                 getLog().warn( "'" + f + "' is not a Java file. Ignored it." );
 853  0
                 continue;
 854  
             }
 855  
 
 856  
             try
 857  
             {
 858  9
                 builder.addSource( f );
 859  
             }
 860  0
             catch ( ParseException e )
 861  
             {
 862  0
                 if ( getLog().isWarnEnabled() )
 863  
                 {
 864  0
                     getLog().warn( "QDOX ParseException: " + e.getMessage() + ". Can't fix it." );
 865  
                 }
 866  9
             }
 867  9
         }
 868  
 
 869  2
         return builder.getClasses();
 870  
     }
 871  
 
 872  
     /**
 873  
      * @return the classLoader for the given project using lazy instantiation.
 874  
      * @throws MojoExecutionException if any
 875  
      */
 876  
     private ClassLoader getProjectClassLoader()
 877  
         throws MojoExecutionException
 878  
     {
 879  133
         if ( projectClassLoader == null )
 880  
         {
 881  
             List classPath;
 882  
             try
 883  
             {
 884  2
                 classPath = getCompileClasspathElements( project );
 885  
             }
 886  0
             catch ( DependencyResolutionRequiredException e )
 887  
             {
 888  0
                 throw new MojoExecutionException( "DependencyResolutionRequiredException: " + e.getMessage(), e );
 889  2
             }
 890  
 
 891  2
             List urls = new ArrayList( classPath.size() );
 892  2
             Iterator iter = classPath.iterator();
 893  6
             while ( iter.hasNext() )
 894  
             {
 895  
                 try
 896  
                 {
 897  4
                     urls.add( new File( ( (String) iter.next() ) ).toURL() );
 898  
                 }
 899  0
                 catch ( MalformedURLException e )
 900  
                 {
 901  0
                     throw new MojoExecutionException( "MalformedURLException: " + e.getMessage(), e );
 902  4
                 }
 903  
             }
 904  
 
 905  2
             projectClassLoader = new URLClassLoader( (URL[]) urls.toArray( new URL[urls.size()] ), null );
 906  
         }
 907  
 
 908  133
         return projectClassLoader;
 909  
     }
 910  
 
 911  
     /**
 912  
      * Process the given {@link JavaClass}, ie add missing javadoc tags depending user parameters.
 913  
      *
 914  
      * @param javaClass not null
 915  
      * @throws IOException if any
 916  
      * @throws MojoExecutionException if any
 917  
      */
 918  
     private void processFix( JavaClass javaClass )
 919  
         throws IOException, MojoExecutionException
 920  
     {
 921  
         // Skipping inner classes
 922  12
         if ( javaClass.isInner() )
 923  
         {
 924  3
             return;
 925  
         }
 926  
 
 927  9
         File javaFile = new File( javaClass.getSource().getURL().getFile() );
 928  
         // the original java content in memory
 929  9
         final String originalContent = StringUtils.unifyLineSeparators( FileUtils.fileRead( javaFile, encoding ) );
 930  
 
 931  9
         if ( getLog().isDebugEnabled() )
 932  
         {
 933  0
             getLog().debug( "Fixing " + javaClass.getFullyQualifiedName() );
 934  
         }
 935  
 
 936  9
         final StringWriter stringWriter = new StringWriter();
 937  9
         BufferedReader reader = null;
 938  
         try
 939  
         {
 940  9
             reader = new BufferedReader( new StringReader( originalContent ) );
 941  
 
 942  
             String line;
 943  9
             int lineNumber = 0;
 944  807
             while ( ( line = reader.readLine() ) != null )
 945  
             {
 946  798
                 lineNumber++;
 947  798
                 final String indent = autodetectIndentation( line );
 948  
 
 949  
                 // fixing classes
 950  798
                 if ( javaClass.getComment() == null && javaClass.getAnnotations() != null
 951  
                     && javaClass.getAnnotations().length != 0 )
 952  
                 {
 953  0
                     if ( lineNumber == javaClass.getAnnotations()[0].getLineNumber() )
 954  
                     {
 955  0
                         fixClassComment( stringWriter, originalContent, javaClass, indent );
 956  
 
 957  0
                         takeCareSingleComment( stringWriter, originalContent, javaClass );
 958  
                     }
 959  
                 }
 960  
                 else
 961  
                 {
 962  798
                     if ( lineNumber == javaClass.getLineNumber() )
 963  
                     {
 964  9
                         fixClassComment( stringWriter, originalContent, javaClass, indent );
 965  
 
 966  9
                         takeCareSingleComment( stringWriter, originalContent, javaClass );
 967  
                     }
 968  
                 }
 969  
 
 970  
                 // fixing fields
 971  798
                 if ( javaClass.getFields() != null )
 972  
                 {
 973  1790
                     for ( int i = 0; i < javaClass.getFields().length; i++ )
 974  
                     {
 975  992
                         JavaField field = javaClass.getFields()[i];
 976  
 
 977  992
                         if ( lineNumber == field.getLineNumber() )
 978  
                         {
 979  12
                             fixFieldComment( stringWriter, javaClass, field, indent );
 980  
                         }
 981  
                     }
 982  
                 }
 983  
 
 984  
                 // fixing methods
 985  798
                 if ( javaClass.getMethods() != null )
 986  
                 {
 987  10392
                     for ( int i = 0; i < javaClass.getMethods().length; i++ )
 988  
                     {
 989  9594
                         JavaMethod method = javaClass.getMethods()[i];
 990  
 
 991  9594
                         if ( lineNumber == method.getLineNumber() )
 992  
                         {
 993  61
                             fixMethodComment( stringWriter, originalContent, method, indent );
 994  
 
 995  61
                             takeCareSingleComment( stringWriter, originalContent, method );
 996  
                         }
 997  
                     }
 998  
                 }
 999  
 
 1000  798
                 stringWriter.write( line );
 1001  798
                 stringWriter.write( EOL );
 1002  798
             }
 1003  
         }
 1004  
         finally
 1005  
         {
 1006  9
             IOUtil.close( reader );
 1007  9
         }
 1008  
 
 1009  9
         if ( getLog().isDebugEnabled() )
 1010  
         {
 1011  0
             getLog().debug( "Saving " + javaClass.getFullyQualifiedName() );
 1012  
         }
 1013  
 
 1014  9
         if ( outputDirectory != null
 1015  
             && !outputDirectory.getAbsolutePath().equals( getProjectSourceDirectory().getAbsolutePath() ) )
 1016  
         {
 1017  9
             String path =
 1018  
                 StringUtils.replace( javaFile.getAbsolutePath().replaceAll( "\\\\", "/" ),
 1019  
                                      project.getBuild().getSourceDirectory().replaceAll( "\\\\", "/" ), "" );
 1020  9
             javaFile = new File( outputDirectory, path );
 1021  9
             javaFile.getParentFile().mkdirs();
 1022  
         }
 1023  9
         writeFile( javaFile, encoding, stringWriter.toString() );
 1024  9
     }
 1025  
 
 1026  
     /**
 1027  
      * Take care of block or single comments between Javadoc comment and entity declaration ie:
 1028  
      * <br/>
 1029  
      * <code>
 1030  
      * <font color="#808080">1</font>&nbsp;<font color="#ffffff">&nbsp;</font>
 1031  
      * <font color="#3f5fbf">&#47;&#42;&#42;</font><br />
 1032  
      * <font color="#808080">2</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;</font>
 1033  
      * <font color="#3f5fbf">&#42;&nbsp;{Javadoc&nbsp;Comment}</font><br />
 1034  
      * <font color="#808080">3</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;</font>
 1035  
      * <font color="#3f5fbf">&#42;&#47;</font><br />
 1036  
      * <font color="#808080">4</font>&nbsp;<font color="#ffffff">&nbsp;</font>
 1037  
      * <font color="#3f7f5f">&#47;&#42;</font><br />
 1038  
      * <font color="#808080">5</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;</font>
 1039  
      * <font color="#3f7f5f">&#42;&nbsp;{Block&nbsp;Comment}</font><br />
 1040  
      * <font color="#808080">6</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;</font>
 1041  
      * <font color="#3f7f5f">&#42;&#47;</font><br />
 1042  
      * <font color="#808080">7</font>&nbsp;<font color="#ffffff">&nbsp;</font>
 1043  
      * <font color="#3f7f5f">&#47;&#47;&nbsp;{Single&nbsp;comment}</font><br />
 1044  
      * <font color="#808080">8</font>&nbsp;<font color="#ffffff">&nbsp;</font>
 1045  
      * <font color="#7f0055"><b>public&nbsp;</b></font><font color="#7f0055"><b>void&nbsp;</b></font>
 1046  
      * <font color="#000000">dummyMethod</font><font color="#000000">(&nbsp;</font>
 1047  
      * <font color="#000000">String&nbsp;s&nbsp;</font><font color="#000000">){}</font>
 1048  
      * </code>
 1049  
      *
 1050  
      * @param stringWriter not null
 1051  
      * @param originalContent not null
 1052  
      * @param entity not null
 1053  
      * @throws IOException if any
 1054  
      * @see #extractOriginalJavadoc(String, AbstractJavaEntity)
 1055  
      */
 1056  
     private void takeCareSingleComment( final StringWriter stringWriter, final String originalContent,
 1057  
                                         final AbstractInheritableJavaEntity entity )
 1058  
         throws IOException
 1059  
     {
 1060  70
         if ( entity.getComment() == null )
 1061  
         {
 1062  26
             return;
 1063  
         }
 1064  
 
 1065  44
         String javadocComment = trimRight( extractOriginalJavadoc( originalContent, entity ) );
 1066  44
         String extraComment =
 1067  
             javadocComment.substring( javadocComment.indexOf( END_JAVADOC ) + END_JAVADOC.length() );
 1068  44
         if ( StringUtils.isNotEmpty( extraComment ) )
 1069  
         {
 1070  2
             if ( extraComment.indexOf( EOL ) != -1 )
 1071  
             {
 1072  2
                 stringWriter.write( extraComment.substring( extraComment.indexOf( EOL ) + EOL.length() ) );
 1073  
             }
 1074  
             else
 1075  
             {
 1076  0
                 stringWriter.write( extraComment );
 1077  
             }
 1078  2
             stringWriter.write( EOL );
 1079  
         }
 1080  44
     }
 1081  
 
 1082  
     /**
 1083  
      * Add/update Javadoc class comment.
 1084  
      *
 1085  
      * @param stringWriter
 1086  
      * @param originalContent
 1087  
      * @param javaClass
 1088  
      * @param indent
 1089  
      * @throws MojoExecutionException
 1090  
      * @throws IOException
 1091  
      */
 1092  
     private void fixClassComment( final StringWriter stringWriter, final String originalContent,
 1093  
                                   final JavaClass javaClass, final String indent )
 1094  
         throws MojoExecutionException, IOException
 1095  
     {
 1096  9
         if ( !fixClassComment )
 1097  
         {
 1098  0
             return;
 1099  
         }
 1100  
 
 1101  9
         if ( !isInLevel( javaClass.getModifiers() ) )
 1102  
         {
 1103  0
             return;
 1104  
         }
 1105  
 
 1106  
         // add
 1107  9
         if ( javaClass.getComment() == null )
 1108  
         {
 1109  4
             addDefaultClassComment( stringWriter, javaClass, indent );
 1110  4
             return;
 1111  
         }
 1112  
 
 1113  
         // update
 1114  5
         updateEntityComment( stringWriter, originalContent, javaClass, indent );
 1115  5
     }
 1116  
 
 1117  
     /**
 1118  
      * @param modifiers list of modifiers (public, private, protected, package)
 1119  
      * @return <code>true</code> if modifier is align with <code>level</code>.
 1120  
      */
 1121  
     private boolean isInLevel( String[] modifiers )
 1122  
     {
 1123  64
         List modifiersAsList = Arrays.asList( modifiers );
 1124  
 
 1125  64
         if ( LEVEL_PUBLIC.equalsIgnoreCase( level.trim() ) )
 1126  
         {
 1127  0
             if ( modifiersAsList.contains( LEVEL_PUBLIC ) )
 1128  
             {
 1129  0
                 return true;
 1130  
             }
 1131  
 
 1132  0
             return false;
 1133  
         }
 1134  
 
 1135  64
         if ( LEVEL_PROTECTED.equalsIgnoreCase( level.trim() ) )
 1136  
         {
 1137  64
             if ( modifiersAsList.contains( LEVEL_PUBLIC ) || modifiersAsList.contains( LEVEL_PROTECTED ) )
 1138  
             {
 1139  62
                 return true;
 1140  
             }
 1141  
 
 1142  2
             return false;
 1143  
         }
 1144  
 
 1145  0
         if ( LEVEL_PACKAGE.equalsIgnoreCase( level.trim() ) )
 1146  
         {
 1147  0
             if ( !modifiersAsList.contains( LEVEL_PRIVATE ) )
 1148  
             {
 1149  0
                 return true;
 1150  
             }
 1151  
 
 1152  0
             return false;
 1153  
         }
 1154  
 
 1155  
         // should be private (shows all classes and members)
 1156  0
         return true;
 1157  
     }
 1158  
 
 1159  
     /**
 1160  
      * Add a default Javadoc for the given class, i.e.:
 1161  
      * <br/>
 1162  
      * <code>
 1163  
      * <font color="#808080">1</font>&nbsp;<font color="#ffffff">&nbsp;</font>
 1164  
      * <font color="#3f5fbf">&#47;&#42;&#42;</font><br />
 1165  
      * <font color="#808080">2</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;</font>
 1166  
      * <font color="#3f5fbf">&#42;&nbsp;{Comment&nbsp;based&nbsp;on&nbsp;the&nbsp;class&nbsp;name}</font><br />
 1167  
      * <font color="#808080">3</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;</font>
 1168  
      * <font color="#3f5fbf">&#42;</font><br />
 1169  
      * <font color="#808080">4</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;</font>
 1170  
      * <font color="#3f5fbf">&#42;&nbsp;</font><font color="#7f9fbf">@author&nbsp;</font>
 1171  
      * <font color="#3f5fbf">X&nbsp;{added&nbsp;if&nbsp;addMissingAuthor}</font><br />
 1172  
      * <font color="#808080">5</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;</font>
 1173  
      * <font color="#3f5fbf">&#42;&nbsp;</font><font color="#7f9fbf">@version&nbsp;</font>
 1174  
      * <font color="#3f5fbf">X&nbsp;{added&nbsp;if&nbsp;addMissingVersion}</font><br />
 1175  
      * <font color="#808080">6</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;</font>
 1176  
      * <font color="#3f5fbf">&#42;&nbsp;</font><font color="#7f9fbf">@since&nbsp;</font>
 1177  
      * <font color="#3f5fbf">X&nbsp;{added&nbsp;if&nbsp;addMissingSince&nbsp;and&nbsp;new&nbsp;classes
 1178  
      * from&nbsp;previous&nbsp;version}</font><br />
 1179  
      * <font color="#808080">7</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;</font>
 1180  
      * <font color="#3f5fbf">&#42;&#47;</font><br />
 1181  
      * <font color="#808080">8</font>&nbsp;<font color="#7f0055"><b>public&nbsp;class&nbsp;</b></font>
 1182  
      * <font color="#000000">DummyClass&nbsp;</font><font color="#000000">{}</font></code>
 1183  
      * </code>
 1184  
      *
 1185  
      * @param buffer not null
 1186  
      * @param javaClass not null
 1187  
      * @param indent not null
 1188  
      * @see #getDefaultClassJavadocComment(JavaClass)
 1189  
      * @see #appendDefaultAuthorTag(StringBuffer, String)
 1190  
      * @see #appendDefaultSinceTag(StringBuffer, String)
 1191  
      * @see #appendDefaultVersionTag(StringBuffer, String)
 1192  
      */
 1193  
     private void addDefaultClassComment( final StringWriter stringWriter, final JavaClass javaClass,
 1194  
                                          final String indent )
 1195  
     {
 1196  4
         StringBuffer sb = new StringBuffer();
 1197  
 
 1198  4
         sb.append( indent ).append( START_JAVADOC );
 1199  4
         sb.append( EOL );
 1200  4
         sb.append( indent ).append( SEPARATOR_JAVADOC );
 1201  4
         sb.append( getDefaultClassJavadocComment( javaClass ) );
 1202  4
         sb.append( EOL );
 1203  
 
 1204  4
         appendSeparator( sb, indent );
 1205  
 
 1206  4
         appendDefaultAuthorTag( sb, indent );
 1207  
 
 1208  4
         appendDefaultVersionTag( sb, indent );
 1209  
 
 1210  4
         if ( fixTag( SINCE_TAG ) )
 1211  
         {
 1212  4
             if ( !ignoreClirr )
 1213  
             {
 1214  4
                 if ( isNewClassFromLastVersion( javaClass ) )
 1215  
                 {
 1216  0
                     appendDefaultSinceTag( sb, indent );
 1217  
                 }
 1218  
             }
 1219  
             else
 1220  
             {
 1221  0
                 appendDefaultSinceTag( sb, indent );
 1222  0
                 addSinceClasses( javaClass );
 1223  
             }
 1224  
         }
 1225  
 
 1226  4
         sb.append( indent ).append( " " ).append( END_JAVADOC );
 1227  4
         sb.append( EOL );
 1228  
 
 1229  4
         stringWriter.write( sb.toString() );
 1230  4
     }
 1231  
 
 1232  
     /**
 1233  
      * Add Javadoc field comment, only for static fields or interface fields.
 1234  
      *
 1235  
      * @param stringWriter not null
 1236  
      * @param javaClass not null
 1237  
      * @param field not null
 1238  
      * @param indent not null
 1239  
      * @throws IOException if any
 1240  
      */
 1241  
     private void fixFieldComment( final StringWriter stringWriter, final JavaClass javaClass,
 1242  
                                   final JavaField field, final String indent )
 1243  
         throws IOException
 1244  
     {
 1245  12
         if ( !fixFieldComment )
 1246  
         {
 1247  0
             return;
 1248  
         }
 1249  
 
 1250  12
         if ( !javaClass.isInterface() )
 1251  
         {
 1252  7
             if ( !isInLevel( field.getModifiers() ) )
 1253  
             {
 1254  1
                 return;
 1255  
             }
 1256  
 
 1257  6
             if ( !field.isStatic() )
 1258  
             {
 1259  0
                 return;
 1260  
             }
 1261  
         }
 1262  
 
 1263  
         // add
 1264  11
         if ( field.getComment() == null )
 1265  
         {
 1266  9
             addDefaultFieldComment( stringWriter, field, indent );
 1267  9
             return;
 1268  
         }
 1269  
 
 1270  
         // no update
 1271  2
     }
 1272  
 
 1273  
     /**
 1274  
      * Add a default Javadoc for the given field, i.e.:
 1275  
      * <br/>
 1276  
      * <code>
 1277  
      * <font color="#808080">1</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;</font>
 1278  
      * <font color="#3f5fbf">&#47;&#42;&#42;&nbsp;Constant&nbsp;</font><font color="#7f7f9f">&lt;code&gt;</font>
 1279  
      * <font color="#3f5fbf">MY_STRING_CONSTANT=&#34;value&#34;</font>
 1280  
      * <font color="#7f7f9f">&lt;/code&gt;&nbsp;</font><font color="#3f5fbf">&#42;&#47;</font><br />
 1281  
      * <font color="#808080">2</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;</font>
 1282  
      * <font color="#7f0055"><b>public&nbsp;static&nbsp;final&nbsp;</b></font>
 1283  
      * <font color="#000000">String&nbsp;MY_STRING_CONSTANT&nbsp;=&nbsp;</font>
 1284  
      * <font color="#2a00ff">&#34;value&#34;</font><font color="#000000">;</font>
 1285  
      * </code>
 1286  
      *
 1287  
      * @param stringWriter not null
 1288  
      * @param field not null
 1289  
      * @param indent not null
 1290  
      * @throws IOException if any
 1291  
      */
 1292  
     private void addDefaultFieldComment( final StringWriter stringWriter, final JavaField field,
 1293  
                                          final String indent )
 1294  
         throws IOException
 1295  
     {
 1296  9
         StringBuffer sb = new StringBuffer();
 1297  
 
 1298  9
         sb.append( indent ).append( START_JAVADOC ).append( " " );
 1299  9
         sb.append( "Constant <code>" ).append( field.getName() );
 1300  
 
 1301  9
         if ( StringUtils.isNotEmpty( field.getInitializationExpression() ) )
 1302  
         {
 1303  9
             String qualifiedName = field.getType().getJavaClass().getFullyQualifiedName();
 1304  
 
 1305  9
             if ( qualifiedName.equals( Byte.TYPE.toString() ) || qualifiedName.equals( Short.TYPE.toString() )
 1306  
                 || qualifiedName.equals( Integer.TYPE.toString() ) || qualifiedName.equals( Long.TYPE.toString() )
 1307  
                 || qualifiedName.equals( Float.TYPE.toString() ) || qualifiedName.equals( Double.TYPE.toString() )
 1308  
                 || qualifiedName.equals( Boolean.TYPE.toString() )
 1309  
                 || qualifiedName.equals( Character.TYPE.toString() ) )
 1310  
             {
 1311  2
                 sb.append( "=" );
 1312  2
                 sb.append( field.getInitializationExpression().trim() );
 1313  
             }
 1314  
 
 1315  9
             if ( qualifiedName.equals( String.class.getName() ) )
 1316  
             {
 1317  5
                 StringBuffer value = new StringBuffer();
 1318  5
                 String[] lines = getLines( field.getInitializationExpression() );
 1319  10
                 for ( int i = 0; i < lines.length; i++ )
 1320  
                 {
 1321  5
                     String line = lines[i];
 1322  
 
 1323  5
                     StringTokenizer token = new StringTokenizer( line.trim(), "\"\n\r" );
 1324  14
                     while ( token.hasMoreTokens() )
 1325  
                     {
 1326  9
                         String s = token.nextToken();
 1327  
 
 1328  9
                         if ( s.trim().equals( "+" ) )
 1329  
                         {
 1330  1
                             continue;
 1331  
                         }
 1332  8
                         if ( s.trim().endsWith( "\\" ) )
 1333  
                         {
 1334  0
                             s += "\"";
 1335  
                         }
 1336  8
                         value.append( s );
 1337  8
                     }
 1338  
                 }
 1339  
 
 1340  5
                 sb.append( "=\"" );
 1341  
                 // reduce the size
 1342  5
                 if ( value.length() < 40 )
 1343  
                 {
 1344  5
                     sb.append( value.toString() ).append( "\"" );
 1345  
                 }
 1346  
                 else
 1347  
                 {
 1348  0
                     sb.append( value.toString().substring( 0, 39 ) ).append( "\"{trunked}" );
 1349  
                 }
 1350  
             }
 1351  
         }
 1352  
 
 1353  9
         sb.append( "</code> " ).append( END_JAVADOC );
 1354  9
         sb.append( EOL );
 1355  
 
 1356  9
         stringWriter.write( sb.toString() );
 1357  9
     }
 1358  
 
 1359  
     /**
 1360  
      * Add/update Javadoc method comment.
 1361  
      *
 1362  
      * @param stringWriter not null
 1363  
      * @param originalContent not null
 1364  
      * @param javaMethod not null
 1365  
      * @param indent not null
 1366  
      * @throws MojoExecutionException if any
 1367  
      * @throws IOException if any
 1368  
      */
 1369  
     private void fixMethodComment( final StringWriter stringWriter, final String originalContent,
 1370  
                                    final JavaMethod javaMethod, final String indent )
 1371  
         throws MojoExecutionException, IOException
 1372  
     {
 1373  61
         if ( !fixMethodComment )
 1374  
         {
 1375  0
             return;
 1376  
         }
 1377  
 
 1378  61
         if ( !javaMethod.getParentClass().isInterface() && !isInLevel( javaMethod.getModifiers() ) )
 1379  
         {
 1380  1
             return;
 1381  
         }
 1382  
 
 1383  
         // add
 1384  60
         if ( javaMethod.getComment() == null )
 1385  
         {
 1386  21
             addDefaultMethodComment( stringWriter, javaMethod, indent );
 1387  21
             return;
 1388  
         }
 1389  
 
 1390  
         // update
 1391  39
         updateEntityComment( stringWriter, originalContent, javaMethod, indent );
 1392  39
     }
 1393  
 
 1394  
     /**
 1395  
      * Add in the buffer a default Javadoc for the given class:
 1396  
      * <br/>
 1397  
      * <code>
 1398  
      * <font color="#808080">1</font>&nbsp;<font color="#ffffff">&nbsp;</font>
 1399  
      * <font color="#3f5fbf">&#47;&#42;&#42;</font><br />
 1400  
      * <font color="#808080">2</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;</font>
 1401  
      * <font color="#3f5fbf">&#42;&nbsp;{Comment&nbsp;based&nbsp;on&nbsp;the&nbsp;method&nbsp;name}</font><br />
 1402  
      * <font color="#808080">3</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;</font>
 1403  
      * <font color="#3f5fbf">&#42;</font><br />
 1404  
      * <font color="#808080">4</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;</font>
 1405  
      * <font color="#3f5fbf">&#42;&nbsp;</font><font color="#7f9fbf">@param&nbsp;</font>
 1406  
      * <font color="#3f5fbf">X&nbsp;{added&nbsp;if&nbsp;addMissingParam}</font><br />
 1407  
      * <font color="#808080">5</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;</font>
 1408  
      * <font color="#3f5fbf">&#42;&nbsp;</font><font color="#7f9fbf">@return&nbsp;</font>
 1409  
      * <font color="#3f5fbf">X&nbsp;{added&nbsp;if&nbsp;addMissingReturn}</font><br />
 1410  
      * <font color="#808080">6</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;</font>
 1411  
      * <font color="#3f5fbf">&#42;&nbsp;</font><font color="#7f9fbf">@throws&nbsp;</font>
 1412  
      * <font color="#3f5fbf">X&nbsp;{added&nbsp;if&nbsp;addMissingThrows}</font><br />
 1413  
      * <font color="#808080">7</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;</font>
 1414  
      * <font color="#3f5fbf">&#42;&nbsp;</font><font color="#7f9fbf">@since&nbsp;</font>
 1415  
      * <font color="#3f5fbf">X&nbsp;{added&nbsp;if&nbsp;addMissingSince&nbsp;and&nbsp;new&nbsp;classes
 1416  
      * from&nbsp;previous&nbsp;version}</font><br />
 1417  
      * <font color="#808080">8</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;</font>
 1418  
      * <font color="#3f5fbf">&#42;&#47;</font><br />
 1419  
      * <font color="#808080">9</font>&nbsp;<font color="#7f0055"><b>public&nbsp;</b></font>
 1420  
      * <font color="#7f0055"><b>void&nbsp;</b></font><font color="#000000">dummyMethod</font>
 1421  
      * <font color="#000000">(&nbsp;</font><font color="#000000">String&nbsp;s&nbsp;</font>
 1422  
      * <font color="#000000">){}</font>
 1423  
      * </code>
 1424  
      *
 1425  
      * @param buffer not null
 1426  
      * @param javaMethod not null
 1427  
      * @param indent not null
 1428  
      * @throws MojoExecutionException if any
 1429  
      * @see #getDefaultMethodJavadocComment(JavaMethod)
 1430  
      * @see #appendDefaultSinceTag(StringBuffer, String)
 1431  
      */
 1432  
     private void addDefaultMethodComment( final StringWriter stringWriter, final JavaMethod javaMethod,
 1433  
                                           final String indent )
 1434  
         throws MojoExecutionException
 1435  
     {
 1436  21
         StringBuffer sb = new StringBuffer();
 1437  
 
 1438  
         // special case
 1439  21
         if ( isInherited( javaMethod ) )
 1440  
         {
 1441  6
             sb.append( indent ).append( INHERITED_JAVADOC );
 1442  6
             sb.append( EOL );
 1443  
 
 1444  6
             stringWriter.write( sb.toString() );
 1445  6
             return;
 1446  
         }
 1447  
 
 1448  15
         sb.append( indent ).append( START_JAVADOC );
 1449  15
         sb.append( EOL );
 1450  15
         sb.append( indent ).append( SEPARATOR_JAVADOC );
 1451  15
         sb.append( getDefaultMethodJavadocComment( javaMethod ) );
 1452  15
         sb.append( EOL );
 1453  
 
 1454  15
         boolean separatorAdded = false;
 1455  15
         if ( fixTag( PARAM_TAG ) )
 1456  
         {
 1457  15
             if ( javaMethod.getParameters() != null )
 1458  
             {
 1459  39
                 for ( int i = 0; i < javaMethod.getParameters().length; i++ )
 1460  
                 {
 1461  24
                     JavaParameter javaParameter = javaMethod.getParameters()[i];
 1462  
 
 1463  24
                     separatorAdded = appendDefaultParamTag( sb, indent, separatorAdded, javaParameter );
 1464  
                 }
 1465  
             }
 1466  
             // is generic?
 1467  15
             if ( javaMethod.getTypeParameters() != null )
 1468  
             {
 1469  22
                 for ( int i = 0; i < javaMethod.getTypeParameters().length; i++ )
 1470  
                 {
 1471  7
                     TypeVariable typeParam = javaMethod.getTypeParameters()[i];
 1472  
 
 1473  7
                     separatorAdded = appendDefaultParamTag( sb, indent, separatorAdded, typeParam );
 1474  
                 }
 1475  
             }
 1476  
         }
 1477  15
         if ( fixTag( RETURN_TAG ) && javaMethod.getReturns() != null && !javaMethod.getReturns().isVoid() )
 1478  
         {
 1479  6
             separatorAdded = appendDefaultReturnTag( sb, indent, separatorAdded, javaMethod );
 1480  
         }
 1481  15
         if ( fixTag( THROWS_TAG ) && javaMethod.getExceptions() != null && javaMethod.getExceptions().length > 0 )
 1482  
         {
 1483  2
             for ( int i = 0; i < javaMethod.getExceptions().length; i++ )
 1484  
             {
 1485  1
                 Type exception = javaMethod.getExceptions()[i];
 1486  
 
 1487  1
                 separatorAdded = appendDefaultThrowsTag( sb, indent, separatorAdded, exception );
 1488  
             }
 1489  
         }
 1490  15
         if ( fixTag( SINCE_TAG ) && isNewMethodFromLastRevision( javaMethod ) )
 1491  
         {
 1492  5
             separatorAdded = appendDefaultSinceTag( sb, indent, separatorAdded );
 1493  
         }
 1494  
 
 1495  15
         sb.append( indent ).append( " " ).append( END_JAVADOC );
 1496  15
         sb.append( EOL );
 1497  
 
 1498  15
         stringWriter.write( sb.toString() );
 1499  15
     }
 1500  
 
 1501  
     /**
 1502  
      * @param stringWriter not null
 1503  
      * @param originalContent not null
 1504  
      * @param entity not null
 1505  
      * @param indent not null
 1506  
      * @throws MojoExecutionException if any
 1507  
      * @throws IOException if any
 1508  
      */
 1509  
     private void updateEntityComment( final StringWriter stringWriter, final String originalContent,
 1510  
                                       final AbstractInheritableJavaEntity entity, final String indent )
 1511  
         throws MojoExecutionException, IOException
 1512  
     {
 1513  44
         String s = stringWriter.toString();
 1514  44
         int i = s.lastIndexOf( START_JAVADOC );
 1515  44
         if ( i != -1 )
 1516  
         {
 1517  44
             String tmp = s.substring( 0, i );
 1518  44
             if ( tmp.lastIndexOf( EOL ) != -1 )
 1519  
             {
 1520  44
                 tmp = tmp.substring( 0, tmp.lastIndexOf( EOL ) );
 1521  
             }
 1522  44
             stringWriter.getBuffer().delete( 0, stringWriter.getBuffer().length() );
 1523  44
             stringWriter.write( tmp );
 1524  44
             stringWriter.write( EOL );
 1525  
         }
 1526  
 
 1527  44
         updateJavadocComment( stringWriter, originalContent, entity, indent );
 1528  44
     }
 1529  
 
 1530  
     /**
 1531  
      * @param stringWriter not null
 1532  
      * @param originalContent not null
 1533  
      * @param entity not null
 1534  
      * @param indent not null
 1535  
      * @throws MojoExecutionException if any
 1536  
      * @throws IOException if any
 1537  
      */
 1538  
     private void updateJavadocComment( final StringWriter stringWriter, final String originalContent,
 1539  
                                        final AbstractInheritableJavaEntity entity, final String indent )
 1540  
         throws MojoExecutionException, IOException
 1541  
     {
 1542  44
         if ( entity.getComment() == null && ( entity.getTags() == null || entity.getTags().length == 0 ) )
 1543  
         {
 1544  0
             return;
 1545  
         }
 1546  
 
 1547  44
         boolean isJavaMethod = false;
 1548  44
         if ( entity instanceof JavaMethod )
 1549  
         {
 1550  39
             isJavaMethod = true;
 1551  
         }
 1552  
 
 1553  44
         StringBuffer sb = new StringBuffer();
 1554  
 
 1555  
         // special case for inherited method
 1556  44
         if ( isJavaMethod )
 1557  
         {
 1558  39
             JavaMethod javaMethod = (JavaMethod) entity;
 1559  
 
 1560  39
             if ( isInherited( javaMethod ) )
 1561  
             {
 1562  
                 // QDOX-154 could be empty
 1563  8
                 if ( StringUtils.isEmpty( javaMethod.getComment() ) )
 1564  
                 {
 1565  1
                     sb.append( indent ).append( INHERITED_JAVADOC );
 1566  1
                     sb.append( EOL );
 1567  1
                     stringWriter.write( sb.toString() );
 1568  1
                     return;
 1569  
                 }
 1570  
 
 1571  7
                 String javadoc = getJavadocComment( originalContent, javaMethod );
 1572  
 
 1573  
                 // case: /** {@inheritDoc} */ or no tags
 1574  7
                 if ( hasInheritedTag( javadoc )
 1575  
                     && ( javaMethod.getTags() == null || javaMethod.getTags().length == 0 ) )
 1576  
                 {
 1577  3
                     sb.append( indent ).append( INHERITED_JAVADOC );
 1578  3
                     sb.append( EOL );
 1579  3
                     stringWriter.write( sb.toString() );
 1580  3
                     return;
 1581  
                 }
 1582  
 
 1583  4
                 if ( javadoc.indexOf( START_JAVADOC ) != -1 )
 1584  
                 {
 1585  0
                     javadoc = javadoc.substring( javadoc.indexOf( START_JAVADOC ) + START_JAVADOC.length() );
 1586  
                 }
 1587  4
                 if ( javadoc.indexOf( END_JAVADOC ) != -1 )
 1588  
                 {
 1589  0
                     javadoc = javadoc.substring( 0, javadoc.indexOf( END_JAVADOC ) );
 1590  
                 }
 1591  
 
 1592  4
                 sb.append( indent ).append( START_JAVADOC );
 1593  4
                 sb.append( EOL );
 1594  4
                 if ( javadoc.indexOf( INHERITED_TAG ) == -1 )
 1595  
                 {
 1596  2
                     sb.append( indent ).append( SEPARATOR_JAVADOC ).append( INHERITED_TAG );
 1597  2
                     sb.append( EOL );
 1598  2
                     appendSeparator( sb, indent );
 1599  
                 }
 1600  4
                 javadoc = removeLastEmptyJavadocLines( javadoc );
 1601  4
                 javadoc = alignIndentationJavadocLines( javadoc, indent );
 1602  4
                 sb.append( javadoc );
 1603  4
                 sb.append( EOL );
 1604  4
                 if ( javaMethod.getTags() != null )
 1605  
                 {
 1606  6
                     for ( int i = 0; i < javaMethod.getTags().length; i++ )
 1607  
                     {
 1608  2
                         DocletTag docletTag = javaMethod.getTags()[i];
 1609  
 
 1610  
                         // Voluntary ignore these tags
 1611  2
                         if ( docletTag.getName().equals( PARAM_TAG )
 1612  
                             || docletTag.getName().equals( RETURN_TAG )
 1613  
                             || docletTag.getName().equals( THROWS_TAG ) )
 1614  
                         {
 1615  0
                             continue;
 1616  
                         }
 1617  
 
 1618  0
                         String s = getJavadocComment( originalContent, entity, docletTag );
 1619  0
                         s = removeLastEmptyJavadocLines( s );
 1620  0
                         s = alignIndentationJavadocLines( s, indent );
 1621  0
                         sb.append( s );
 1622  0
                         sb.append( EOL );
 1623  
                     }
 1624  
                 }
 1625  4
                 sb.append( indent ).append( " " ).append( END_JAVADOC );
 1626  4
                 sb.append( EOL );
 1627  
 
 1628  4
                 if ( hasInheritedTag( sb.toString().trim() ) )
 1629  
                 {
 1630  1
                     sb = new StringBuffer();
 1631  1
                     sb.append( indent ).append( INHERITED_JAVADOC );
 1632  1
                     sb.append( EOL );
 1633  1
                     stringWriter.write( sb.toString() );
 1634  1
                     return;
 1635  
                 }
 1636  
 
 1637  3
                 stringWriter.write( sb.toString() );
 1638  3
                 return;
 1639  
             }
 1640  
         }
 1641  
 
 1642  36
         sb.append( indent ).append( START_JAVADOC );
 1643  36
         sb.append( EOL );
 1644  
 
 1645  
         // comment
 1646  36
         if ( StringUtils.isNotEmpty( entity.getComment() ) )
 1647  
         {
 1648  31
             updateJavadocComment( sb, originalContent, entity, indent );
 1649  
         }
 1650  
         else
 1651  
         {
 1652  5
             addDefaultJavadocComment( sb, entity, indent, isJavaMethod );
 1653  
         }
 1654  
 
 1655  
         // tags
 1656  36
         if ( entity.getTags() != null && entity.getTags().length > 0 )
 1657  
         {
 1658  17
             updateJavadocTags( sb, originalContent, entity, indent, isJavaMethod );
 1659  
         }
 1660  
         else
 1661  
         {
 1662  19
             addDefaultJavadocTags( sb, entity, indent, isJavaMethod );
 1663  
         }
 1664  
 
 1665  36
         sb = new StringBuffer( removeLastEmptyJavadocLines( sb.toString() ) ).append( EOL );
 1666  
 
 1667  36
         sb.append( indent ).append( " " ).append( END_JAVADOC );
 1668  36
         sb.append( EOL );
 1669  
 
 1670  36
         stringWriter.write( sb.toString() );
 1671  36
     }
 1672  
 
 1673  
     /**
 1674  
      * @param sb not null
 1675  
      * @param originalContent not null
 1676  
      * @param entity not null
 1677  
      * @param indent not null
 1678  
      * @throws IOException if any
 1679  
      */
 1680  
     private void updateJavadocComment( final StringBuffer sb, final String originalContent,
 1681  
                                        final AbstractInheritableJavaEntity entity, final String indent )
 1682  
         throws IOException
 1683  
     {
 1684  31
         String comment = getJavadocComment( originalContent, entity );
 1685  31
         comment = removeLastEmptyJavadocLines( comment );
 1686  31
         comment = alignIndentationJavadocLines( comment, indent );
 1687  
 
 1688  31
         if ( comment.indexOf( START_JAVADOC ) != -1 )
 1689  
         {
 1690  0
             comment = comment.substring( comment.indexOf( START_JAVADOC ) + START_JAVADOC.length() );
 1691  0
             comment = indent + SEPARATOR_JAVADOC + comment.trim();
 1692  
         }
 1693  31
         if ( comment.indexOf( END_JAVADOC ) != -1 )
 1694  
         {
 1695  0
             comment = comment.substring( 0, comment.indexOf( END_JAVADOC ) );
 1696  
         }
 1697  
 
 1698  31
         String[] lines = getLines( comment );
 1699  62
         for ( int i = 0; i < lines.length; i++ )
 1700  
         {
 1701  31
             sb.append( indent ).append( " " ).append( lines[i].trim() );
 1702  31
             sb.append( EOL );
 1703  
         }
 1704  31
     }
 1705  
 
 1706  
     /**
 1707  
      * @param sb not null
 1708  
      * @param entity not null
 1709  
      * @param indent not null
 1710  
      * @param isJavaMethod
 1711  
      */
 1712  
     private void addDefaultJavadocComment( final StringBuffer sb, final AbstractInheritableJavaEntity entity,
 1713  
                                            final String indent, final boolean isJavaMethod )
 1714  
     {
 1715  5
         sb.append( indent ).append( SEPARATOR_JAVADOC );
 1716  5
         if ( isJavaMethod )
 1717  
         {
 1718  5
             sb.append( getDefaultMethodJavadocComment( (JavaMethod) entity ) );
 1719  
         }
 1720  
         else
 1721  
         {
 1722  0
             sb.append( getDefaultClassJavadocComment( (JavaClass) entity ) );
 1723  
         }
 1724  5
         sb.append( EOL );
 1725  5
     }
 1726  
 
 1727  
     /**
 1728  
      * @param sb not null
 1729  
      * @param originalContent not null
 1730  
      * @param entity not null
 1731  
      * @param indent not null
 1732  
      * @param isJavaMethod
 1733  
      * @throws IOException if any
 1734  
      * @throws MojoExecutionException if any
 1735  
      */
 1736  
     private void updateJavadocTags( final StringBuffer sb, final String originalContent,
 1737  
                                     final AbstractInheritableJavaEntity entity, final String indent,
 1738  
                                     final boolean isJavaMethod )
 1739  
         throws IOException, MojoExecutionException
 1740  
     {
 1741  17
         appendSeparator( sb, indent );
 1742  
 
 1743  
         // parse tags
 1744  17
         JavaEntityTags javaEntityTags = parseJavadocTags( originalContent, entity, indent, isJavaMethod );
 1745  
 
 1746  
         // update and write tags
 1747  17
         updateJavadocTags( sb, entity, isJavaMethod, javaEntityTags );
 1748  
 
 1749  
         // add missing tags...
 1750  17
         addMissingJavadocTags( sb, entity, indent, isJavaMethod, javaEntityTags );
 1751  17
     }
 1752  
 
 1753  
     /**
 1754  
      * Parse entity tags
 1755  
      *
 1756  
      * @param originalContent not null
 1757  
      * @param entity not null
 1758  
      * @param indent not null
 1759  
      * @param isJavaMethod
 1760  
      * @return an instance of {@link JavaEntityTags}
 1761  
      * @throws IOException if any
 1762  
      */
 1763  
     private JavaEntityTags parseJavadocTags( final String originalContent,
 1764  
                                              final AbstractInheritableJavaEntity entity, final String indent,
 1765  
                                              final boolean isJavaMethod )
 1766  
         throws IOException
 1767  
     {
 1768  17
         JavaEntityTags javaEntityTags = new JavaEntityTags( entity, isJavaMethod );
 1769  49
         for ( int i = 0; i < entity.getTags().length; i++ )
 1770  
         {
 1771  32
             DocletTag docletTag = entity.getTags()[i];
 1772  
 
 1773  32
             String originalJavadocTag = getJavadocComment( originalContent, entity, docletTag );
 1774  32
             originalJavadocTag = removeLastEmptyJavadocLines( originalJavadocTag );
 1775  32
             originalJavadocTag = alignIndentationJavadocLines( originalJavadocTag, indent );
 1776  
 
 1777  32
             javaEntityTags.getNamesTags().add( docletTag.getName() );
 1778  
 
 1779  32
             if ( isJavaMethod )
 1780  
             {
 1781  32
                 String[] params = docletTag.getParameters();
 1782  32
                 if ( params.length < 1 )
 1783  
                 {
 1784  1
                     continue;
 1785  
                 }
 1786  
 
 1787  31
                 params = fixQdox173( params );
 1788  31
                 String paramName = params[0];
 1789  31
                 if ( docletTag.getName().equals( PARAM_TAG ) )
 1790  
                 {
 1791  18
                     javaEntityTags.putJavadocParamTag( paramName, originalJavadocTag );
 1792  
                 }
 1793  13
                 else if ( docletTag.getName().equals( RETURN_TAG ) )
 1794  
                 {
 1795  8
                     javaEntityTags.setJavadocReturnTag( originalJavadocTag );
 1796  
                 }
 1797  5
                 else if ( docletTag.getName().equals( THROWS_TAG ) )
 1798  
                 {
 1799  5
                     javaEntityTags.putJavadocThrowsTag( paramName, originalJavadocTag );
 1800  
                 }
 1801  
                 else
 1802  
                 {
 1803  0
                     javaEntityTags.getUnknownTags().add( originalJavadocTag );
 1804  
                 }
 1805  31
             }
 1806  
             else
 1807  
             {
 1808  0
                 javaEntityTags.getUnknownTags().add( originalJavadocTag );
 1809  
             }
 1810  
         }
 1811  
 
 1812  17
         return javaEntityTags;
 1813  
     }
 1814  
 
 1815  
     /**
 1816  
      * Write tags according javaEntityTags.
 1817  
      *
 1818  
      * @param sb not null
 1819  
      * @param entity not null
 1820  
      * @param isJavaMethod
 1821  
      * @param javaEntityTags not null
 1822  
      */
 1823  
     private void updateJavadocTags( final StringBuffer sb, final AbstractInheritableJavaEntity entity,
 1824  
                                     final boolean isJavaMethod, final JavaEntityTags javaEntityTags )
 1825  
     {
 1826  49
         for ( int i = 0; i < entity.getTags().length; i++ )
 1827  
         {
 1828  32
             DocletTag docletTag = entity.getTags()[i];
 1829  
 
 1830  32
             if ( isJavaMethod )
 1831  
             {
 1832  32
                 JavaMethod javaMethod = (JavaMethod) entity;
 1833  
 
 1834  32
                 String[] params = docletTag.getParameters();
 1835  32
                 if ( params.length < 1 )
 1836  
                 {
 1837  1
                     continue;
 1838  
                 }
 1839  
 
 1840  31
                 if ( docletTag.getName().equals( PARAM_TAG ) )
 1841  
                 {
 1842  18
                     writeParamTag( sb, javaMethod, javaEntityTags, params );
 1843  
                 }
 1844  13
                 else if ( docletTag.getName().equals( RETURN_TAG ) )
 1845  
                 {
 1846  8
                     writeReturnTag( sb, javaMethod, javaEntityTags );
 1847  
                 }
 1848  5
                 else if ( docletTag.getName().equals( THROWS_TAG ) )
 1849  
                 {
 1850  5
                     writeThrowsTag( sb, javaMethod, javaEntityTags, params );
 1851  
                 }
 1852  
                 else
 1853  
                 {
 1854  
                     // write unknown tags
 1855  0
                     for ( Iterator it = javaEntityTags.getUnknownTags().iterator(); it.hasNext(); )
 1856  
                     {
 1857  0
                         String originalJavadocTag = it.next().toString();
 1858  
 
 1859  0
                         if ( StringUtils.removeDuplicateWhitespace( originalJavadocTag ).trim()
 1860  
                                         .indexOf( "@" + docletTag.getName() ) != -1 )
 1861  
                         {
 1862  0
                             it.remove();
 1863  0
                             sb.append( originalJavadocTag );
 1864  0
                             sb.append( EOL );
 1865  
                         }
 1866  0
                     }
 1867  
                 }
 1868  31
             }
 1869  
             else
 1870  
             {
 1871  0
                 for ( Iterator it = javaEntityTags.getUnknownTags().iterator(); it.hasNext(); )
 1872  
                 {
 1873  0
                     String originalJavadocTag = it.next().toString();
 1874  
 
 1875  0
                     if ( StringUtils.removeDuplicateWhitespace( originalJavadocTag ).trim()
 1876  
                                     .indexOf( "@" + docletTag.getName() ) != -1 )
 1877  
                     {
 1878  0
                         it.remove();
 1879  0
                         sb.append( originalJavadocTag );
 1880  0
                         sb.append( EOL );
 1881  
                     }
 1882  0
                 }
 1883  
             }
 1884  
 
 1885  31
             if ( sb.toString().endsWith( EOL ) )
 1886  
             {
 1887  0
                 sb.delete( sb.toString().lastIndexOf( EOL ), sb.toString().length() );
 1888  
             }
 1889  
 
 1890  31
             sb.append( EOL );
 1891  
         }
 1892  17
     }
 1893  
 
 1894  
     private void writeParamTag( final StringBuffer sb, final JavaMethod javaMethod,
 1895  
                                 final JavaEntityTags javaEntityTags, String[] params )
 1896  
     {
 1897  18
         params = fixQdox173( params );
 1898  
 
 1899  18
         String paramName = params[0];
 1900  
 
 1901  18
         if ( !fixTag( PARAM_TAG ) )
 1902  
         {
 1903  
             // write original param tag if found
 1904  0
             String originalJavadocTag = javaEntityTags.getJavadocParamTag( paramName );
 1905  0
             if ( originalJavadocTag != null )
 1906  
             {
 1907  0
                 sb.append( originalJavadocTag );
 1908  
             }
 1909  0
             return;
 1910  
         }
 1911  
 
 1912  18
         boolean found = false;
 1913  18
         JavaParameter javaParam = javaMethod.getParameterByName( paramName );
 1914  18
         if ( javaParam == null )
 1915  
         {
 1916  
             // is generic?
 1917  6
             TypeVariable[] typeParams = javaMethod.getTypeParameters();
 1918  14
             for ( int i = 0; i < typeParams.length; i++ )
 1919  
             {
 1920  8
                 if ( typeParams[i].getGenericValue().equals( paramName ) )
 1921  
                 {
 1922  2
                     found = true;
 1923  
                 }
 1924  
             }
 1925  6
         }
 1926  
         else
 1927  
         {
 1928  12
             found = true;
 1929  
         }
 1930  
 
 1931  18
         if ( !found )
 1932  
         {
 1933  4
             if ( getLog().isWarnEnabled() )
 1934  
             {
 1935  4
                 StringBuffer warn = new StringBuffer();
 1936  
 
 1937  4
                 warn.append( "Fixed unknown param '" ).append( paramName ).append( "' defined in " );
 1938  4
                 warn.append( getJavaMethodAsString( javaMethod ) );
 1939  
 
 1940  4
                 getLog().warn( warn.toString() );
 1941  
             }
 1942  
 
 1943  4
             if ( sb.toString().endsWith( EOL ) )
 1944  
             {
 1945  4
                 sb.delete( sb.toString().lastIndexOf( EOL ), sb.toString().length() );
 1946  
             }
 1947  
         }
 1948  
         else
 1949  
         {
 1950  14
             String originalJavadocTag = javaEntityTags.getJavadocParamTag( paramName );
 1951  14
             if ( originalJavadocTag != null )
 1952  
             {
 1953  14
                 sb.append( originalJavadocTag );
 1954  14
                 String s = "@" + PARAM_TAG + " " + paramName;
 1955  14
                 if ( StringUtils.removeDuplicateWhitespace( originalJavadocTag ).trim().endsWith( s ) )
 1956  
                 {
 1957  3
                     sb.append( " " );
 1958  3
                     sb.append( getDefaultJavadocForType( javaParam.getType() ) );
 1959  
                 }
 1960  
             }
 1961  
         }
 1962  18
     }
 1963  
 
 1964  
     private void writeReturnTag( final StringBuffer sb, final JavaMethod javaMethod,
 1965  
                                  final JavaEntityTags javaEntityTags )
 1966  
     {
 1967  8
         String originalJavadocTag = javaEntityTags.getJavadocReturnTag();
 1968  8
         if ( originalJavadocTag == null )
 1969  
         {
 1970  0
             return;
 1971  
         }
 1972  
 
 1973  8
         if ( !fixTag( RETURN_TAG ) )
 1974  
         {
 1975  
             // write original param tag if found
 1976  0
             sb.append( originalJavadocTag );
 1977  0
             return;
 1978  
         }
 1979  
 
 1980  8
         if ( StringUtils.isNotEmpty( originalJavadocTag ) && javaMethod.getReturns() != null
 1981  
             && !javaMethod.getReturns().isVoid() )
 1982  
         {
 1983  8
             sb.append( originalJavadocTag );
 1984  8
             if ( originalJavadocTag.trim().endsWith( "@" + RETURN_TAG ) )
 1985  
             {
 1986  0
                 sb.append( " " );
 1987  0
                 sb.append( getDefaultJavadocForType( javaMethod.getReturns() ) );
 1988  
             }
 1989  
         }
 1990  8
     }
 1991  
 
 1992  
     private void writeThrowsTag( final StringBuffer sb, final JavaMethod javaMethod,
 1993  
                                  final JavaEntityTags javaEntityTags, final String[] params )
 1994  
     {
 1995  5
         String exceptionClassName = params[0];
 1996  
 
 1997  5
         String originalJavadocTag = javaEntityTags.getJavadocThrowsTag( exceptionClassName );
 1998  5
         if ( originalJavadocTag == null )
 1999  
         {
 2000  0
             return;
 2001  
         }
 2002  
 
 2003  5
         if ( !fixTag( THROWS_TAG ) )
 2004  
         {
 2005  
             // write original param tag if found
 2006  0
             sb.append( originalJavadocTag );
 2007  0
             return;
 2008  
         }
 2009  
 
 2010  5
         if ( javaMethod.getExceptions() != null )
 2011  
         {
 2012  5
             for ( int j = 0; j < javaMethod.getExceptions().length; j++ )
 2013  
             {
 2014  2
                 Type exception = javaMethod.getExceptions()[j];
 2015  
 
 2016  2
                 if ( exception.getValue().endsWith( exceptionClassName ) )
 2017  
                 {
 2018  2
                     originalJavadocTag =
 2019  
                         StringUtils.replace( originalJavadocTag, exceptionClassName, exception.getValue() );
 2020  2
                     if ( StringUtils.removeDuplicateWhitespace( originalJavadocTag ).trim()
 2021  
                                     .endsWith( "@" + THROWS_TAG + " " + exception.getValue() ) )
 2022  
                     {
 2023  0
                         originalJavadocTag += " if any.";
 2024  
                     }
 2025  
 
 2026  2
                     sb.append( originalJavadocTag );
 2027  
 
 2028  
                     // added qualified name
 2029  2
                     javaEntityTags.putJavadocThrowsTag( exception.getValue(), originalJavadocTag );
 2030  
 
 2031  2
                     return;
 2032  
                 }
 2033  
             }
 2034  
         }
 2035  
 
 2036  
         // Maybe a RuntimeException
 2037  3
         Class clazz = getRuntimeExceptionClass( javaMethod.getParentClass(), exceptionClassName );
 2038  3
         if ( clazz != null )
 2039  
         {
 2040  2
             sb.append( StringUtils.replace( originalJavadocTag, exceptionClassName, clazz.getName() ) );
 2041  
 
 2042  
             // added qualified name
 2043  2
             javaEntityTags.putJavadocThrowsTag( clazz.getName(), originalJavadocTag );
 2044  
 
 2045  2
             return;
 2046  
         }
 2047  
 
 2048  1
         if ( getLog().isWarnEnabled() )
 2049  
         {
 2050  1
             StringBuffer warn = new StringBuffer();
 2051  
 
 2052  1
             warn.append( "Unknown throws exception '" ).append( exceptionClassName ).append( "' defined in " );
 2053  1
             warn.append( getJavaMethodAsString( javaMethod ) );
 2054  
 
 2055  1
             getLog().warn( warn.toString() );
 2056  
         }
 2057  
 
 2058  1
         sb.append( originalJavadocTag );
 2059  1
         if ( params.length == 1 )
 2060  
         {
 2061  0
             sb.append( " if any." );
 2062  
         }
 2063  1
     }
 2064  
 
 2065  
     /**
 2066  
      * Add missing tags not already written.
 2067  
      *
 2068  
      * @param sb not null
 2069  
      * @param entity not null
 2070  
      * @param indent not null
 2071  
      * @param isJavaMethod
 2072  
      * @param javaEntityTags not null
 2073  
      * @throws MojoExecutionException if any
 2074  
      */
 2075  
     private void addMissingJavadocTags( final StringBuffer sb, final AbstractInheritableJavaEntity entity,
 2076  
                                         final String indent, final boolean isJavaMethod,
 2077  
                                         final JavaEntityTags javaEntityTags )
 2078  
         throws MojoExecutionException
 2079  
     {
 2080  17
         if ( isJavaMethod )
 2081  
         {
 2082  17
             JavaMethod javaMethod = (JavaMethod) entity;
 2083  
 
 2084  17
             if ( fixTag( PARAM_TAG ) )
 2085  
             {
 2086  17
                 if ( javaMethod.getParameters() != null )
 2087  
                 {
 2088  35
                     for ( int i = 0; i < javaMethod.getParameters().length; i++ )
 2089  
                     {
 2090  18
                         JavaParameter javaParameter = javaMethod.getParameters()[i];
 2091  
 
 2092  18
                         if ( javaEntityTags.getJavadocParamTag( javaParameter.getName(), true ) == null )
 2093  
                         {
 2094  6
                             appendDefaultParamTag( sb, indent, javaParameter );
 2095  
                         }
 2096  
                     }
 2097  
                 }
 2098  
                 // is generic?
 2099  17
                 if ( javaMethod.getTypeParameters() != null )
 2100  
                 {
 2101  21
                     for ( int i = 0; i < javaMethod.getTypeParameters().length; i++ )
 2102  
                     {
 2103  4
                         TypeVariable typeParam = javaMethod.getTypeParameters()[i];
 2104  
 
 2105  4
                         if ( javaEntityTags.getJavadocParamTag( "<" + typeParam.getName() + ">", true ) == null )
 2106  
                         {
 2107  2
                             appendDefaultParamTag( sb, indent, typeParam );
 2108  
                         }
 2109  
                     }
 2110  
                 }
 2111  
             }
 2112  
 
 2113  17
             if ( fixTag( RETURN_TAG ) && StringUtils.isEmpty( javaEntityTags.getJavadocReturnTag() )
 2114  
                 && javaMethod.getReturns() != null && !javaMethod.getReturns().isVoid() )
 2115  
             {
 2116  2
                 appendDefaultReturnTag( sb, indent, javaMethod );
 2117  
             }
 2118  
 
 2119  17
             if ( fixTag( THROWS_TAG ) && javaMethod.getExceptions() != null )
 2120  
             {
 2121  19
                 for ( int i = 0; i < javaMethod.getExceptions().length; i++ )
 2122  
                 {
 2123  2
                     Type exception = javaMethod.getExceptions()[i];
 2124  
 
 2125  2
                     if ( javaEntityTags.getJavadocThrowsTag( exception.getValue(), true ) == null )
 2126  
                     {
 2127  0
                         appendDefaultThrowsTag( sb, indent, exception );
 2128  
                     }
 2129  
                 }
 2130  
             }
 2131  17
         }
 2132  
         else
 2133  
         {
 2134  0
             if ( !javaEntityTags.getNamesTags().contains( AUTHOR_TAG ) )
 2135  
             {
 2136  0
                 appendDefaultAuthorTag( sb, indent );
 2137  
             }
 2138  0
             if ( !javaEntityTags.getNamesTags().contains( VERSION_TAG ) )
 2139  
             {
 2140  0
                 appendDefaultVersionTag( sb, indent );
 2141  
             }
 2142  
         }
 2143  17
         if ( fixTag( SINCE_TAG ) && !javaEntityTags.getNamesTags().contains( SINCE_TAG ) )
 2144  
         {
 2145  17
             if ( !isJavaMethod )
 2146  
             {
 2147  0
                 if ( !ignoreClirr )
 2148  
                 {
 2149  0
                     if ( isNewClassFromLastVersion( (JavaClass) entity ) )
 2150  
                     {
 2151  0
                         appendDefaultSinceTag( sb, indent );
 2152  
                     }
 2153  
                 }
 2154  
                 else
 2155  
                 {
 2156  0
                     appendDefaultSinceTag( sb, indent );
 2157  0
                     addSinceClasses( (JavaClass) entity );
 2158  
                 }
 2159  
             }
 2160  
             else
 2161  
             {
 2162  17
                 if ( !ignoreClirr )
 2163  
                 {
 2164  17
                     if ( isNewMethodFromLastRevision( (JavaMethod) entity ) )
 2165  
                     {
 2166  2
                         appendDefaultSinceTag( sb, indent );
 2167  
                     }
 2168  
                 }
 2169  
                 else
 2170  
                 {
 2171  0
                     if ( sinceClasses != null && !sinceClassesContains( ( (JavaMethod) entity ).getParentClass() ) )
 2172  
                     {
 2173  0
                         appendDefaultSinceTag( sb, indent );
 2174  
                     }
 2175  
                 }
 2176  
             }
 2177  
         }
 2178  17
     }
 2179  
 
 2180  
     /**
 2181  
      * @param sb not null
 2182  
      * @param entity not null
 2183  
      * @param indent not null
 2184  
      * @param isJavaMethod
 2185  
      * @throws MojoExecutionException if any
 2186  
      */
 2187  
     private void addDefaultJavadocTags( final StringBuffer sb, final AbstractInheritableJavaEntity entity,
 2188  
                                         final String indent, final boolean isJavaMethod )
 2189  
         throws MojoExecutionException
 2190  
     {
 2191  19
         boolean separatorAdded = false;
 2192  19
         if ( isJavaMethod )
 2193  
         {
 2194  14
             JavaMethod javaMethod = (JavaMethod) entity;
 2195  
 
 2196  14
             if ( fixTag( PARAM_TAG ) && javaMethod.getParameters() != null )
 2197  
             {
 2198  29
                 for ( int i = 0; i < javaMethod.getParameters().length; i++ )
 2199  
                 {
 2200  15
                     JavaParameter javaParameter = javaMethod.getParameters()[i];
 2201  
 
 2202  15
                     separatorAdded = appendDefaultParamTag( sb, indent, separatorAdded, javaParameter );
 2203  
                 }
 2204  
             }
 2205  
 
 2206  14
             if ( fixTag( RETURN_TAG ) )
 2207  
             {
 2208  14
                 if ( javaMethod.getReturns() != null && !javaMethod.getReturns().isVoid() )
 2209  
                 {
 2210  8
                     separatorAdded = appendDefaultReturnTag( sb, indent, separatorAdded, javaMethod );
 2211  
                 }
 2212  
             }
 2213  
 
 2214  14
             if ( fixTag( THROWS_TAG ) && javaMethod.getExceptions() != null )
 2215  
             {
 2216  15
                 for ( int i = 0; i < javaMethod.getExceptions().length; i++ )
 2217  
                 {
 2218  1
                     Type exception = javaMethod.getExceptions()[i];
 2219  
 
 2220  1
                     separatorAdded = appendDefaultThrowsTag( sb, indent, separatorAdded, exception );
 2221  
                 }
 2222  
             }
 2223  14
         }
 2224  
         else
 2225  
         {
 2226  5
             separatorAdded = appendDefaultAuthorTag( sb, indent, separatorAdded );
 2227  
 
 2228  5
             separatorAdded = appendDefaultVersionTag( sb, indent, separatorAdded );
 2229  
         }
 2230  
 
 2231  19
         if ( fixTag( SINCE_TAG ) )
 2232  
         {
 2233  19
             if ( !isJavaMethod )
 2234  
             {
 2235  5
                 JavaClass javaClass = (JavaClass) entity;
 2236  
 
 2237  5
                 if ( !ignoreClirr )
 2238  
                 {
 2239  5
                     if ( isNewClassFromLastVersion( javaClass ) )
 2240  
                     {
 2241  0
                         separatorAdded = appendDefaultSinceTag( sb, indent, separatorAdded );
 2242  
                     }
 2243  
                 }
 2244  
                 else
 2245  
                 {
 2246  0
                     separatorAdded = appendDefaultSinceTag( sb, indent, separatorAdded );
 2247  
 
 2248  0
                     addSinceClasses( javaClass );
 2249  
                 }
 2250  5
             }
 2251  
             else
 2252  
             {
 2253  14
                 JavaMethod javaMethod = (JavaMethod) entity;
 2254  
 
 2255  14
                 if ( !ignoreClirr )
 2256  
                 {
 2257  14
                     if ( isNewMethodFromLastRevision( javaMethod ) )
 2258  
                     {
 2259  4
                         separatorAdded = appendDefaultSinceTag( sb, indent, separatorAdded );
 2260  
                     }
 2261  
                 }
 2262  
                 else
 2263  
                 {
 2264  0
                     if ( sinceClasses != null && !sinceClassesContains( javaMethod.getParentClass() ) )
 2265  
                     {
 2266  0
                         separatorAdded = appendDefaultSinceTag( sb, indent, separatorAdded );
 2267  
                     }
 2268  
                 }
 2269  
             }
 2270  
         }
 2271  19
     }
 2272  
 
 2273  
     /**
 2274  
      * @param sb not null
 2275  
      * @param indent not null
 2276  
      * @param separatorAdded
 2277  
      * @return true if separator has been added.
 2278  
      */
 2279  
     private boolean appendDefaultAuthorTag( final StringBuffer sb, final String indent, boolean separatorAdded )
 2280  
     {
 2281  5
         if ( !fixTag( AUTHOR_TAG ) )
 2282  
         {
 2283  0
             return separatorAdded;
 2284  
         }
 2285  
 
 2286  5
         if ( !separatorAdded )
 2287  
         {
 2288  5
             appendSeparator( sb, indent );
 2289  5
             separatorAdded = true;
 2290  
         }
 2291  
 
 2292  5
         appendDefaultAuthorTag( sb, indent );
 2293  5
         return separatorAdded;
 2294  
     }
 2295  
 
 2296  
     /**
 2297  
      * @param sb not null
 2298  
      * @param indent not null
 2299  
      */
 2300  
     private void appendDefaultAuthorTag( final StringBuffer sb, final String indent )
 2301  
     {
 2302  9
         if ( !fixTag( AUTHOR_TAG ) )
 2303  
         {
 2304  0
             return;
 2305  
         }
 2306  
 
 2307  9
         sb.append( indent ).append( " * @" ).append( AUTHOR_TAG ).append( " " );
 2308  9
         sb.append( defaultAuthor );
 2309  9
         sb.append( EOL );
 2310  9
     }
 2311  
 
 2312  
     /**
 2313  
      * @param sb not null
 2314  
      * @param indent not null
 2315  
      * @param separatorAdded
 2316  
      * @return true if separator has been added.
 2317  
      */
 2318  
     private boolean appendDefaultSinceTag( final StringBuffer sb, final String indent, boolean separatorAdded )
 2319  
     {
 2320  9
         if ( !fixTag( SINCE_TAG ) )
 2321  
         {
 2322  0
             return separatorAdded;
 2323  
         }
 2324  
 
 2325  9
         if ( !separatorAdded )
 2326  
         {
 2327  0
             appendSeparator( sb, indent );
 2328  0
             separatorAdded = true;
 2329  
         }
 2330  
 
 2331  9
         appendDefaultSinceTag( sb, indent );
 2332  9
         return separatorAdded;
 2333  
     }
 2334  
 
 2335  
     /**
 2336  
      * @param sb not null
 2337  
      * @param indent not null
 2338  
      */
 2339  
     private void appendDefaultSinceTag( final StringBuffer sb, final String indent )
 2340  
     {
 2341  11
         if ( !fixTag( SINCE_TAG ) )
 2342  
         {
 2343  0
             return;
 2344  
         }
 2345  
 
 2346  11
         sb.append( indent ).append( " * @" ).append( SINCE_TAG ).append( " " );
 2347  11
         sb.append( defaultSince );
 2348  11
         sb.append( EOL );
 2349  11
     }
 2350  
 
 2351  
     /**
 2352  
      * @param sb not null
 2353  
      * @param indent not null
 2354  
      * @param separatorAdded
 2355  
      * @return true if separator has been added.
 2356  
      */
 2357  
     private boolean appendDefaultVersionTag( final StringBuffer sb, final String indent, boolean separatorAdded )
 2358  
     {
 2359  5
         if ( !fixTag( VERSION_TAG ) )
 2360  
         {
 2361  0
             return separatorAdded;
 2362  
         }
 2363  
 
 2364  5
         if ( !separatorAdded )
 2365  
         {
 2366  0
             appendSeparator( sb, indent );
 2367  0
             separatorAdded = true;
 2368  
         }
 2369  
 
 2370  5
         appendDefaultVersionTag( sb, indent );
 2371  5
         return separatorAdded;
 2372  
     }
 2373  
 
 2374  
     /**
 2375  
      * @param sb not null
 2376  
      * @param indent not null
 2377  
      */
 2378  
     private void appendDefaultVersionTag( final StringBuffer sb, final String indent )
 2379  
     {
 2380  9
         if ( !fixTag( VERSION_TAG ) )
 2381  
         {
 2382  0
             return;
 2383  
         }
 2384  
 
 2385  9
         sb.append( indent ).append( " * @" ).append( VERSION_TAG ).append( " " );
 2386  9
         sb.append( defaultVersion );
 2387  9
         sb.append( EOL );
 2388  9
     }
 2389  
 
 2390  
     /**
 2391  
      * @param sb not null
 2392  
      * @param indent not null
 2393  
      * @param separatorAdded
 2394  
      * @param javaParameter not null
 2395  
      * @return true if separator has been added.
 2396  
      */
 2397  
     private boolean appendDefaultParamTag( final StringBuffer sb, final String indent, boolean separatorAdded,
 2398  
                                            final JavaParameter javaParameter )
 2399  
     {
 2400  39
         if ( !fixTag( PARAM_TAG ) )
 2401  
         {
 2402  0
             return separatorAdded;
 2403  
         }
 2404  
 
 2405  39
         if ( !separatorAdded )
 2406  
         {
 2407  25
             appendSeparator( sb, indent );
 2408  25
             separatorAdded = true;
 2409  
         }
 2410  
 
 2411  39
         appendDefaultParamTag( sb, indent, javaParameter );
 2412  39
         return separatorAdded;
 2413  
     }
 2414  
 
 2415  
     /**
 2416  
      * @param sb not null
 2417  
      * @param indent not null
 2418  
      * @param separatorAdded
 2419  
      * @param typeParameter not null
 2420  
      * @return true if separator has been added.
 2421  
      */
 2422  
     private boolean appendDefaultParamTag( final StringBuffer sb, final String indent, boolean separatorAdded,
 2423  
                                            final TypeVariable typeParameter )
 2424  
     {
 2425  7
         if ( !fixTag( PARAM_TAG ) )
 2426  
         {
 2427  0
             return separatorAdded;
 2428  
         }
 2429  
 
 2430  7
         if ( !separatorAdded )
 2431  
         {
 2432  0
             appendSeparator( sb, indent );
 2433  0
             separatorAdded = true;
 2434  
         }
 2435  
 
 2436  7
         appendDefaultParamTag( sb, indent, typeParameter );
 2437  7
         return separatorAdded;
 2438  
     }
 2439  
 
 2440  
     /**
 2441  
      * @param sb not null
 2442  
      * @param indent not null
 2443  
      * @param javaParameter not null
 2444  
      */
 2445  
     private void appendDefaultParamTag( final StringBuffer sb, final String indent,
 2446  
                                         final JavaParameter javaParameter )
 2447  
     {
 2448  45
         if ( !fixTag( PARAM_TAG ) )
 2449  
         {
 2450  0
             return;
 2451  
         }
 2452  
 
 2453  45
         sb.append( indent ).append( " * @" ).append( PARAM_TAG ).append( " " );
 2454  45
         sb.append( javaParameter.getName() );
 2455  45
         sb.append( " " );
 2456  45
         sb.append( getDefaultJavadocForType( javaParameter.getType() ) );
 2457  45
         sb.append( EOL );
 2458  45
     }
 2459  
 
 2460  
     /**
 2461  
      * @param sb not null
 2462  
      * @param indent not null
 2463  
      * @param typeParameter not null
 2464  
      */
 2465  
     private void appendDefaultParamTag( final StringBuffer sb, final String indent,
 2466  
                                         final TypeVariable typeParameter )
 2467  
     {
 2468  9
         if ( !fixTag( PARAM_TAG ) )
 2469  
         {
 2470  0
             return;
 2471  
         }
 2472  
 
 2473  9
         sb.append( indent ).append( " * @" ).append( PARAM_TAG ).append( " " );
 2474  9
         sb.append( "<" + typeParameter.getName() + ">" );
 2475  9
         sb.append( " " );
 2476  9
         sb.append( getDefaultJavadocForType( typeParameter ) );
 2477  9
         sb.append( EOL );
 2478  9
     }
 2479  
 
 2480  
     /**
 2481  
      * @param sb not null
 2482  
      * @param indent not null
 2483  
      * @param separatorAdded
 2484  
      * @param javaMethod not null
 2485  
      * @return true if separator has been added.
 2486  
      */
 2487  
     private boolean appendDefaultReturnTag( final StringBuffer sb, final String indent, boolean separatorAdded,
 2488  
                                             final JavaMethod javaMethod )
 2489  
     {
 2490  14
         if ( !fixTag( RETURN_TAG ) )
 2491  
         {
 2492  0
             return separatorAdded;
 2493  
         }
 2494  
 
 2495  14
         if ( !separatorAdded )
 2496  
         {
 2497  0
             appendSeparator( sb, indent );
 2498  0
             separatorAdded = true;
 2499  
         }
 2500  
 
 2501  14
         appendDefaultReturnTag( sb, indent, javaMethod );
 2502  14
         return separatorAdded;
 2503  
     }
 2504  
 
 2505  
     /**
 2506  
      * @param sb not null
 2507  
      * @param indent not null
 2508  
      * @param javaMethod not null
 2509  
      */
 2510  
     private void appendDefaultReturnTag( final StringBuffer sb, final String indent, final JavaMethod javaMethod )
 2511  
     {
 2512  16
         if ( !fixTag( RETURN_TAG ) )
 2513  
         {
 2514  0
             return;
 2515  
         }
 2516  
 
 2517  16
         sb.append( indent ).append( " * @" ).append( RETURN_TAG ).append( " " );
 2518  16
         sb.append( getDefaultJavadocForType( javaMethod.getReturns() ) );
 2519  16
         sb.append( EOL );
 2520  16
     }
 2521  
 
 2522  
     /**
 2523  
      * @param sb not null
 2524  
      * @param indent not null
 2525  
      * @param separatorAdded
 2526  
      * @param exception not null
 2527  
      * @return true if separator has been added.
 2528  
      */
 2529  
     private boolean appendDefaultThrowsTag( final StringBuffer sb, final String indent, boolean separatorAdded,
 2530  
                                             final Type exception )
 2531  
     {
 2532  2
         if ( !fixTag( THROWS_TAG ) )
 2533  
         {
 2534  0
             return separatorAdded;
 2535  
         }
 2536  
 
 2537  2
         if ( !separatorAdded )
 2538  
         {
 2539  0
             appendSeparator( sb, indent );
 2540  0
             separatorAdded = true;
 2541  
         }
 2542  
 
 2543  2
         appendDefaultThrowsTag( sb, indent, exception );
 2544  2
         return separatorAdded;
 2545  
     }
 2546  
 
 2547  
     /**
 2548  
      * @param sb not null
 2549  
      * @param indent not null
 2550  
      * @param exception not null
 2551  
      */
 2552  
     private void appendDefaultThrowsTag( final StringBuffer sb, final String indent, final Type exception )
 2553  
     {
 2554  2
         if ( !fixTag( THROWS_TAG ) )
 2555  
         {
 2556  0
             return;
 2557  
         }
 2558  
 
 2559  2
         sb.append( indent ).append( " * @" ).append( THROWS_TAG ).append( " " );
 2560  2
         sb.append( exception.getJavaClass().getFullyQualifiedName() );
 2561  2
         sb.append( " if any." );
 2562  2
         sb.append( EOL );
 2563  2
     }
 2564  
 
 2565  
     /**
 2566  
      * @param sb not null
 2567  
      * @param indent not null
 2568  
      */
 2569  
     private void appendSeparator( final StringBuffer sb, final String indent )
 2570  
     {
 2571  53
         sb.append( indent ).append( " *" );
 2572  53
         sb.append( EOL );
 2573  53
     }
 2574  
 
 2575  
     /**
 2576  
      * Verify if a method has <code>&#64;java.lang.Override()</code> annotation or if it is an inherited method
 2577  
      * from an interface or a super class. The goal is to handle <code>&#123;&#64;inheritDoc&#125;</code> tag.
 2578  
      *
 2579  
      * @param javaMethod not null
 2580  
      * @return <code>true</code> if the method is inherited, <code>false</code> otherwise.
 2581  
      * @throws MojoExecutionException if any
 2582  
      */
 2583  
     private boolean isInherited( JavaMethod javaMethod )
 2584  
         throws MojoExecutionException, SecurityException
 2585  
     {
 2586  60
         if ( javaMethod.getAnnotations() != null )
 2587  
         {
 2588  60
             for ( int i = 0; i < javaMethod.getAnnotations().length; i++ )
 2589  
             {
 2590  1
                 Annotation annotation = javaMethod.getAnnotations()[i];
 2591  
 
 2592  1
                 if ( annotation.toString().equals( "@java.lang.Override()" ) )
 2593  
                 {
 2594  1
                     return true;
 2595  
                 }
 2596  
             }
 2597  
         }
 2598  
 
 2599  59
         Class clazz = getClass( javaMethod.getParentClass().getFullyQualifiedName() );
 2600  
 
 2601  59
         List interfaces = ClassUtils.getAllInterfaces( clazz );
 2602  59
         for ( Iterator it = interfaces.iterator(); it.hasNext(); )
 2603  
         {
 2604  46
             Class intface = (Class) it.next();
 2605  
 
 2606  46
             if ( isInherited( intface, javaMethod ) )
 2607  
             {
 2608  13
                 return true;
 2609  
             }
 2610  33
         }
 2611  
 
 2612  46
         List classes = ClassUtils.getAllSuperclasses( clazz );
 2613  46
         for ( Iterator it = classes.iterator(); it.hasNext(); )
 2614  
         {
 2615  33
             Class superClass = (Class) it.next();
 2616  
 
 2617  33
             if ( isInherited( superClass, javaMethod ) )
 2618  
             {
 2619  0
                 return true;
 2620  
             }
 2621  33
         }
 2622  
 
 2623  46
         return false;
 2624  
     }
 2625  
 
 2626  
     /**
 2627  
      * @param clazz the Java class object, not null
 2628  
      * @param javaMethod the QDox JavaMethod object not null
 2629  
      * @return <code>true</code> if <code>javaMethod</code> exists in the given <code>clazz</code>,
 2630  
      * <code>false</code> otherwise.
 2631  
      * @see #isInherited(JavaMethod)
 2632  
      */
 2633  
     private boolean isInherited( Class clazz, JavaMethod javaMethod )
 2634  
     {
 2635  79
         Method[] methods = clazz.getDeclaredMethods();
 2636  634
         for ( int i = 0; i < methods.length; i++ )
 2637  
         {
 2638  568
             if ( !methods[i].getName().equals( javaMethod.getName() ) )
 2639  
             {
 2640  555
                 continue;
 2641  
             }
 2642  
 
 2643  13
             if ( methods[i].getParameterTypes().length != javaMethod.getParameters().length )
 2644  
             {
 2645  0
                 continue;
 2646  
             }
 2647  
 
 2648  13
             boolean found = false;
 2649  30
             for ( int j = 0; j < methods[i].getParameterTypes().length; j++ )
 2650  
             {
 2651  17
                 String name1 = methods[i].getParameterTypes()[j].getName();
 2652  17
                 String name2 = javaMethod.getParameters()[j].getType().getFullQualifiedName();
 2653  17
                 if ( name1.equals( name2 ) )
 2654  
                 {
 2655  17
                     found = true;
 2656  
                 }
 2657  
                 else
 2658  
                 {
 2659  0
                     found = found && false;
 2660  
                 }
 2661  
             }
 2662  
 
 2663  13
             return found;
 2664  
         }
 2665  
 
 2666  66
         return false;
 2667  
     }
 2668  
 
 2669  
     /**
 2670  
      * @param type
 2671  
      * @return
 2672  
      */
 2673  
     private String getDefaultJavadocForType( Type type )
 2674  
     {
 2675  73
         StringBuffer sb = new StringBuffer();
 2676  
 
 2677  73
         if ( !TypeVariable.class.isAssignableFrom( type.getClass() ) && type.isPrimitive() )
 2678  
         {
 2679  12
             if ( type.isArray() )
 2680  
             {
 2681  0
                 sb.append( "an array of " );
 2682  
             }
 2683  
             else
 2684  
             {
 2685  12
                 sb.append( "a " );
 2686  
             }
 2687  12
             sb.append( type.getJavaClass().getFullyQualifiedName() );
 2688  12
             sb.append( "." );
 2689  12
             return sb.toString();
 2690  
         }
 2691  
 
 2692  61
         StringBuffer javadocLink = new StringBuffer();
 2693  
         try
 2694  
         {
 2695  61
             getClass( type.getJavaClass().getFullyQualifiedName() );
 2696  
 
 2697  50
             javadocLink.append( "{@link " );
 2698  50
             String s = type.getJavaClass().getFullyQualifiedName();
 2699  50
             s = StringUtils.replace( s, "$", "." );
 2700  50
             javadocLink.append( s );
 2701  50
             javadocLink.append( "}" );
 2702  
         }
 2703  11
         catch ( Exception e )
 2704  
         {
 2705  11
             javadocLink.append( type.getJavaClass().getFullyQualifiedName() );
 2706  50
         }
 2707  
 
 2708  61
         if ( type.isArray() )
 2709  
         {
 2710  0
             sb.append( "an array of " );
 2711  0
             sb.append( javadocLink.toString() );
 2712  0
             sb.append( " objects." );
 2713  
         }
 2714  
         else
 2715  
         {
 2716  61
             sb.append( "a " ).append( javadocLink.toString() ).append( " object." );
 2717  
         }
 2718  
 
 2719  61
         return sb.toString();
 2720  
     }
 2721  
 
 2722  
     /**
 2723  
      * Check under Clirr if this given class is newer from the last version.
 2724  
      *
 2725  
      * @param javaClass a given class not null
 2726  
      * @return <code>true</code> if Clirr said that this class is added from the last version,
 2727  
      * <code>false</code> otherwise or if {@link #clirrNewClasses} is null.
 2728  
      */
 2729  
     private boolean isNewClassFromLastVersion( JavaClass javaClass )
 2730  
     {
 2731  9
         if ( clirrNewClasses == null )
 2732  
         {
 2733  0
             return false;
 2734  
         }
 2735  
 
 2736  9
         return clirrNewClasses.contains( javaClass.getFullyQualifiedName() );
 2737  
     }
 2738  
 
 2739  
     /**
 2740  
      * Check under Clirr if this given method is newer from the last version.
 2741  
      *
 2742  
      * @param javaMethod a given method not null
 2743  
      * @return <code>true</code> if Clirr said that this method is added from the last version,
 2744  
      * <code>false</code> otherwise or if {@link #clirrNewMethods} is null.
 2745  
      * @throws MojoExecutionException  if any
 2746  
      */
 2747  
     private boolean isNewMethodFromLastRevision( JavaMethod javaMethod )
 2748  
         throws MojoExecutionException
 2749  
     {
 2750  46
         if ( clirrNewMethods == null )
 2751  
         {
 2752  0
             return false;
 2753  
         }
 2754  
 
 2755  46
         List clirrMethods = (List) clirrNewMethods.get( javaMethod.getParentClass().getFullyQualifiedName() );
 2756  46
         if ( clirrMethods == null )
 2757  
         {
 2758  0
             return false;
 2759  
         }
 2760  
 
 2761  46
         for ( Iterator it = clirrMethods.iterator(); it.hasNext(); )
 2762  
         {
 2763  
             // see net.sf.clirr.core.internal.checks.MethodSetCheck#getMethodId(JavaType clazz, Method method)
 2764  88
             String clirrMethod = (String) it.next();
 2765  
 
 2766  88
             String retrn = "";
 2767  88
             if ( javaMethod.getReturns() != null )
 2768  
             {
 2769  66
                 retrn = javaMethod.getReturns().getFullQualifiedName();
 2770  
             }
 2771  88
             StringBuffer params = new StringBuffer();
 2772  88
             JavaParameter[] parameters = javaMethod.getParameters();
 2773  189
             for ( int i = 0; i < parameters.length; i++ )
 2774  
             {
 2775  101
                 params.append( parameters[i].getResolvedValue() );
 2776  101
                 if ( i < parameters.length - 1 )
 2777  
                 {
 2778  32
                     params.append( ", " );
 2779  
                 }
 2780  
             }
 2781  88
             if ( ( clirrMethod.indexOf( retrn + " " ) != -1 )
 2782  
                 && ( clirrMethod.indexOf( javaMethod.getName() + "(" ) != -1 )
 2783  
                 && ( clirrMethod.indexOf( "(" + params.toString() + ")" ) != -1 ) )
 2784  
             {
 2785  11
                 return true;
 2786  
             }
 2787  77
         }
 2788  
 
 2789  35
         return false;
 2790  
     }
 2791  
 
 2792  
     /**
 2793  
      * @param className not null
 2794  
      * @return the Class corresponding to the given class name using the project classloader.
 2795  
      * @throws MojoExecutionException if class not found
 2796  
      * @see {@link ClassUtils#getClass(ClassLoader, String, boolean)}
 2797  
      * @see {@link #getProjectClassLoader()}
 2798  
      */
 2799  
     private Class getClass( String className )
 2800  
         throws MojoExecutionException
 2801  
     {
 2802  
         try
 2803  
         {
 2804  131
             return ClassUtils.getClass( getProjectClassLoader(), className, false );
 2805  
         }
 2806  20
         catch ( ClassNotFoundException e )
 2807  
         {
 2808  20
             throw new MojoExecutionException( "ClassNotFoundException: " + e.getMessage(), e );
 2809  
         }
 2810  
     }
 2811  
 
 2812  
     /**
 2813  
      * Returns the Class object assignable for {@link RuntimeException} class and associated with the given
 2814  
      * exception class name.
 2815  
      *
 2816  
      * @param currentClass not null
 2817  
      * @param exceptionClassName not null, an exception class name defined as:
 2818  
      * <ul>
 2819  
      * <li>exception class fully qualified</li>
 2820  
      * <li>exception class in the same package</li>
 2821  
      * <li>exception inner class</li>
 2822  
      * <li>exception class in java.lang package</li>
 2823  
      * </ul>
 2824  
      * @return a RuntimeException assignable class.
 2825  
      * @see #getClass(String)
 2826  
      */
 2827  
     private Class getRuntimeExceptionClass( JavaClass currentClass, String exceptionClassName )
 2828  
     {
 2829  3
         String[] potentialClassNames =
 2830  
             new String[] { exceptionClassName, currentClass.getPackage().getName() + "." + exceptionClassName,
 2831  
                 currentClass.getPackage().getName() + "." + currentClass.getName() + "$" + exceptionClassName,
 2832  
                 "java.lang." + exceptionClassName };
 2833  
 
 2834  3
         Class clazz = null;
 2835  12
         for ( int i = 0; i < potentialClassNames.length; i++ )
 2836  
         {
 2837  
             try
 2838  
             {
 2839  11
                 clazz = getClass( potentialClassNames[i] );
 2840  
             }
 2841  9
             catch ( MojoExecutionException e )
 2842  
             {
 2843  
                 // nop
 2844  2
             }
 2845  11
             if ( clazz != null && ClassUtils.isAssignable( clazz, RuntimeException.class ) )
 2846  
             {
 2847  2
                 return clazz;
 2848  
             }
 2849  
         }
 2850  
 
 2851  1
         return null;
 2852  
     }
 2853  
 
 2854  
     /**
 2855  
      * @param javaClass not null
 2856  
      */
 2857  
     private void addSinceClasses( JavaClass javaClass )
 2858  
     {
 2859  0
         if ( sinceClasses == null )
 2860  
         {
 2861  0
             sinceClasses = new ArrayList();
 2862  
         }
 2863  0
         sinceClasses.add( javaClass.getFullyQualifiedName() );
 2864  0
     }
 2865  
 
 2866  
     private boolean sinceClassesContains( JavaClass javaClass )
 2867  
     {
 2868  0
         return sinceClasses.contains( javaClass.getFullyQualifiedName() );
 2869  
     }
 2870  
 
 2871  
     // ----------------------------------------------------------------------
 2872  
     // Static methods
 2873  
     // ----------------------------------------------------------------------
 2874  
 
 2875  
     /**
 2876  
      * Write content into the given javaFile and using the given encoding.
 2877  
      * All line separators will be unified.
 2878  
      *
 2879  
      * @param javaFile not null
 2880  
      * @param encoding not null
 2881  
      * @param content not null
 2882  
      * @throws IOException if any
 2883  
      */
 2884  
     private static void writeFile( final File javaFile, final String encoding, final String content )
 2885  
         throws IOException
 2886  
     {
 2887  9
         Writer writer = null;
 2888  
         try
 2889  
         {
 2890  9
             writer = WriterFactory.newWriter( javaFile, encoding );
 2891  9
             writer.write( StringUtils.unifyLineSeparators( content ) );
 2892  
         }
 2893  
         finally
 2894  
         {
 2895  9
             IOUtil.close( writer );
 2896  9
         }
 2897  9
     }
 2898  
 
 2899  
     /**
 2900  
      * @return the full clirr goal, i.e. <code>groupId:artifactId:version:goal</code>. The clirr-plugin version
 2901  
      * could be load from the pom.properties in the clirr-maven-plugin dependency.
 2902  
      */
 2903  
     private static String getFullClirrGoal()
 2904  
     {
 2905  2
         StringBuffer sb = new StringBuffer();
 2906  
 
 2907  2
         sb.append( CLIRR_MAVEN_PLUGIN_GROUPID ).append( ":" );
 2908  2
         sb.append( CLIRR_MAVEN_PLUGIN_ARTIFACTID ).append( ":" );
 2909  2
         String clirrVersion = CLIRR_MAVEN_PLUGIN_VERSION;
 2910  2
         InputStream resourceAsStream = null;
 2911  
         try
 2912  
         {
 2913  2
             String resource =
 2914  
                 "META-INF/maven/" + CLIRR_MAVEN_PLUGIN_GROUPID + "/" + CLIRR_MAVEN_PLUGIN_ARTIFACTID
 2915  
                     + "/pom.properties";
 2916  2
             resourceAsStream = AbstractFixJavadocMojo.class.getClassLoader().getResourceAsStream( resource );
 2917  
 
 2918  2
             if ( resourceAsStream != null )
 2919  
             {
 2920  0
                 Properties properties = new Properties();
 2921  0
                 properties.load( resourceAsStream );
 2922  
 
 2923  0
                 if ( StringUtils.isNotEmpty( properties.getProperty( "version" ) ) )
 2924  
                 {
 2925  0
                     clirrVersion = properties.getProperty( "version" );
 2926  
                 }
 2927  
             }
 2928  
         }
 2929  0
         catch ( IOException e )
 2930  
         {
 2931  
             // nop
 2932  
         }
 2933  
         finally
 2934  
         {
 2935  2
             IOUtil.close( resourceAsStream );
 2936  2
         }
 2937  
 
 2938  2
         sb.append( clirrVersion ).append( ":" );
 2939  2
         sb.append( CLIRR_MAVEN_PLUGIN_GOAL );
 2940  
 
 2941  2
         return sb.toString();
 2942  
     }
 2943  
 
 2944  
     /**
 2945  
      * Default comment for class.
 2946  
      *
 2947  
      * @param javaClass not null
 2948  
      * @return a default comment for class.
 2949  
      */
 2950  
     private static String getDefaultClassJavadocComment( final JavaClass javaClass )
 2951  
     {
 2952  4
         StringBuffer sb = new StringBuffer();
 2953  
 
 2954  4
         sb.append( "<p>" );
 2955  4
         if ( Arrays.asList( javaClass.getModifiers() ).contains( "abstract" ) )
 2956  
         {
 2957  0
             sb.append( "Abstract " );
 2958  
         }
 2959  
 
 2960  4
         sb.append( javaClass.getName() );
 2961  
 
 2962  4
         if ( !javaClass.isInterface() )
 2963  
         {
 2964  2
             sb.append( " class." );
 2965  
         }
 2966  
         else
 2967  
         {
 2968  2
             sb.append( " interface." );
 2969  
         }
 2970  
 
 2971  4
         sb.append( "</p>" );
 2972  
 
 2973  4
         return sb.toString();
 2974  
     }
 2975  
 
 2976  
     /**
 2977  
      * Default comment for method with taking care of getter/setter in the javaMethod name.
 2978  
      *
 2979  
      * @param javaMethod not null
 2980  
      * @return a default comment for method.
 2981  
      */
 2982  
     private static String getDefaultMethodJavadocComment( final JavaMethod javaMethod )
 2983  
     {
 2984  20
         StringBuffer sb = new StringBuffer();
 2985  
 
 2986  20
         if ( javaMethod.isConstructor() )
 2987  
         {
 2988  5
             sb.append( "<p>Constructor for " );
 2989  5
             sb.append( javaMethod.getName() ).append( ".</p>" );
 2990  5
             return sb.toString();
 2991  
         }
 2992  
 
 2993  15
         if ( javaMethod.getName().length() > 3
 2994  
             && ( javaMethod.getName().startsWith( "get" ) || javaMethod.getName().startsWith( "set" ) ) )
 2995  
         {
 2996  0
             String field = StringUtils.lowercaseFirstLetter( javaMethod.getName().substring( 3 ) );
 2997  
 
 2998  0
             JavaClass clazz = javaMethod.getParentClass();
 2999  
 
 3000  0
             if ( clazz.getFieldByName( field ) == null )
 3001  
             {
 3002  0
                 sb.append( "<p>" ).append( javaMethod.getName() ).append( "</p>" );
 3003  0
                 return sb.toString();
 3004  
             }
 3005  
 
 3006  0
             sb.append( "<p>" );
 3007  0
             if ( javaMethod.getName().startsWith( "get" ) )
 3008  
             {
 3009  0
                 sb.append( "Getter " );
 3010  
             }
 3011  0
             if ( javaMethod.getName().startsWith( "set" ) )
 3012  
             {
 3013  0
                 sb.append( "Setter " );
 3014  
             }
 3015  0
             sb.append( "for the field <code>" ).append( field ).append( "</code>." );
 3016  0
             sb.append( "</p>" );
 3017  
 
 3018  0
             return sb.toString();
 3019  
         }
 3020  
 
 3021  15
         sb.append( "<p>" ).append( javaMethod.getName() ).append( "</p>" );
 3022  
 
 3023  15
         return sb.toString();
 3024  
     }
 3025  
 
 3026  
     /**
 3027  
      * Try to find if a Javadoc comment has an {@link #INHERITED_TAG} for instance:
 3028  
      * <pre>
 3029  
      * &#47;&#42;&#42; {&#64;inheritDoc} &#42;&#47;
 3030  
      * </pre>
 3031  
      * or
 3032  
      * <pre>
 3033  
      * &#47;&#42;&#42;
 3034  
      * &#32;&#42; {&#64;inheritDoc}
 3035  
      * &#32;&#42;&#47;
 3036  
      * </pre>
 3037  
      *
 3038  
      * @param content not null
 3039  
      * @return <code>true</code> if the content has an inherited tag, <code>false</code> otherwise.
 3040  
      */
 3041  
     private static boolean hasInheritedTag( final String content )
 3042  
     {
 3043  18
         final String inheritedTagPattern =
 3044  
             "^\\s*(\\/\\*\\*)?(\\s*(\\*)?)*(\\{)@inheritDoc\\s*(\\})(\\s*(\\*)?)*(\\*\\/)?$";
 3045  18
         return Pattern.matches( inheritedTagPattern, StringUtils.removeDuplicateWhitespace( content ) );
 3046  
     }
 3047  
 
 3048  
     /**
 3049  
      * Workaround for QDOX-146 about whitespace.
 3050  
      * Ideally we want to use <code>entity.getComment()</code>
 3051  
      * <br/>
 3052  
      * For instance, with the following snippet:
 3053  
      * <br/>
 3054  
      *
 3055  
      * <code>
 3056  
      * <font color="#808080">1</font>&nbsp;<font color="#ffffff"></font><br />
 3057  
      * <font color="#808080">2</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3058  
      * <font color="#3f5fbf">&#47;&#42;&#42;</font><br />
 3059  
      * <font color="#808080">3</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3060  
      * <font color="#3f5fbf">&#42;&nbsp;Dummy&nbsp;Javadoc&nbsp;comment.</font><br />
 3061  
      * <font color="#808080">4</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3062  
      * <font color="#3f5fbf">&#42;&nbsp;</font><font color="#7f9fbf">@param&nbsp;</font>
 3063  
      * <font color="#3f5fbf">s&nbsp;a&nbsp;String</font><br />
 3064  
      * <font color="#808080">5</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3065  
      * <font color="#3f5fbf">&#42;&#47;</font><br />
 3066  
      * <font color="#808080">6</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3067  
      * <font color="#7f0055"><b>public&nbsp;</b></font><font color="#7f0055"><b>void&nbsp;</b></font>
 3068  
      * <font color="#000000">dummyMethod</font><font color="#000000">(&nbsp;</font>
 3069  
      * <font color="#000000">String&nbsp;s&nbsp;</font><font color="#000000">){}</font><br />
 3070  
      * </code>
 3071  
      *
 3072  
      * <br/>
 3073  
      * The return will be:
 3074  
      * <br/>
 3075  
      *
 3076  
      * <code>
 3077  
      * <font color="#808080">1</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3078  
      * <font color="#3f5fbf">&#42;&nbsp;Dummy&nbsp;Javadoc&nbsp;comment.</font><br />
 3079  
      * </code>
 3080  
      *
 3081  
      * @param javaClassContent original class content not null
 3082  
      * @param entity not null
 3083  
      * @return the javadoc comment for the entity without any tags.
 3084  
      * @throws IOException if any
 3085  
      */
 3086  
     private static String getJavadocComment( final String javaClassContent, final AbstractJavaEntity entity )
 3087  
         throws IOException
 3088  
     {
 3089  40
         if ( entity.getComment() == null )
 3090  
         {
 3091  0
             return "";
 3092  
         }
 3093  
 
 3094  40
         String originalJavadoc = extractOriginalJavadocContent( javaClassContent, entity );
 3095  
 
 3096  40
         StringBuffer sb = new StringBuffer();
 3097  40
         BufferedReader lr = new BufferedReader( new StringReader( originalJavadoc ) );
 3098  
         String line;
 3099  101
         while ( ( line = lr.readLine() ) != null )
 3100  
         {
 3101  77
             if ( StringUtils.removeDuplicateWhitespace( line.trim() ).startsWith( "* @" )
 3102  
                 || StringUtils.removeDuplicateWhitespace( line.trim() ).startsWith( "*@" ) )
 3103  
             {
 3104  0
                 break;
 3105  
             }
 3106  61
             sb.append( line ).append( EOL );
 3107  
         }
 3108  
 
 3109  40
         return trimRight( sb.toString() );
 3110  
     }
 3111  
 
 3112  
     /**
 3113  
      * Work around for QDOX-146 about whitespace.
 3114  
      * Ideally we want to use <code>docletTag.getValue()</code>
 3115  
      * <br/>
 3116  
      * For instance, with the following snippet:
 3117  
      * <br/>
 3118  
      *
 3119  
      * <code>
 3120  
      * <font color="#808080">1</font>&nbsp;<font color="#ffffff"></font><br />
 3121  
      * <font color="#808080">2</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3122  
      * <font color="#3f5fbf">&#47;&#42;&#42;</font><br />
 3123  
      * <font color="#808080">3</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3124  
      * <font color="#3f5fbf">&#42;&nbsp;Dummy&nbsp;Javadoc&nbsp;comment.</font><br />
 3125  
      * <font color="#808080">4</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3126  
      * <font color="#3f5fbf">&#42;&nbsp;</font><font color="#7f9fbf">@param&nbsp;</font>
 3127  
      * <font color="#3f5fbf">s&nbsp;a&nbsp;String</font><br />
 3128  
      * <font color="#808080">5</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3129  
      * <font color="#3f5fbf">&#42;&#47;</font><br />
 3130  
      * <font color="#808080">6</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3131  
      * <font color="#7f0055"><b>public&nbsp;</b></font><font color="#7f0055"><b>void&nbsp;</b></font>
 3132  
      * <font color="#000000">dummyMethod</font><font color="#000000">(&nbsp;</font>
 3133  
      * <font color="#000000">String&nbsp;s&nbsp;</font><font color="#000000">){}</font><br />
 3134  
      * </code>
 3135  
      *
 3136  
      * <br/>
 3137  
      * The return will be:
 3138  
      * <br/>
 3139  
      *
 3140  
      * <code>
 3141  
      * <font color="#808080">1</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3142  
      * <font color="#3f5fbf">&#42;&nbsp;</font><font color="#7f9fbf">@param&nbsp;</font>
 3143  
      * <font color="#3f5fbf">s&nbsp;a&nbsp;String</font><br />
 3144  
      * </code>
 3145  
      *
 3146  
      * @param javaClassContent original class content not null
 3147  
      * @param entity not null
 3148  
      * @param docletTag not null
 3149  
      * @return the javadoc comment for the entity without Javadoc tags.
 3150  
      * @throws IOException if any
 3151  
      */
 3152  
     private static String getJavadocComment( final String javaClassContent,
 3153  
                                              final AbstractInheritableJavaEntity entity, final DocletTag docletTag )
 3154  
         throws IOException
 3155  
     {
 3156  41
         if ( docletTag.getValue() == null )
 3157  
         {
 3158  0
             return "";
 3159  
         }
 3160  41
         if ( docletTag.getParameters().length == 0 )
 3161  
         {
 3162  1
             return "";
 3163  
         }
 3164  
 
 3165  40
         String originalJavadoc = extractOriginalJavadocContent( javaClassContent, entity );
 3166  
 
 3167  40
         String[] params = fixQdox173( docletTag.getParameters() );
 3168  40
         String paramValue = params[0];
 3169  
 
 3170  40
         StringBuffer sb = new StringBuffer();
 3171  40
         BufferedReader lr = new BufferedReader( new StringReader( originalJavadoc ) );
 3172  
         String line;
 3173  40
         boolean found = false;
 3174  311
         while ( ( line = lr.readLine() ) != null )
 3175  
         {
 3176  271
             if ( StringUtils.removeDuplicateWhitespace( line.trim() ).startsWith(
 3177  
                                                                                   "* @" + docletTag.getName()
 3178  
                                                                                       + " " + paramValue )
 3179  
                 || StringUtils.removeDuplicateWhitespace( line.trim() ).startsWith(
 3180  
                                                                                     "*@" + docletTag.getName()
 3181  
                                                                                         + " " + paramValue ) )
 3182  
             {
 3183  40
                 sb.append( line ).append( EOL );
 3184  40
                 found = true;
 3185  
             }
 3186  
             else
 3187  
             {
 3188  231
                 if ( StringUtils.removeDuplicateWhitespace( line.trim() ).startsWith( "* @" )
 3189  
                     || StringUtils.removeDuplicateWhitespace( line.trim() ).startsWith( "*@" ) )
 3190  
                 {
 3191  79
                     found = false;
 3192  
                 }
 3193  231
                 if ( found )
 3194  
                 {
 3195  19
                     sb.append( line ).append( EOL );
 3196  
                 }
 3197  
             }
 3198  
         }
 3199  
 
 3200  40
         return trimRight( sb.toString() );
 3201  
     }
 3202  
 
 3203  
     /**
 3204  
      * Extract the original Javadoc and others comments up to {@link #START_JAVADOC} form the entity. This method
 3205  
      * takes care of the Javadoc indentation. All javadoc lines will be trimmed on right.
 3206  
      * <br/>
 3207  
      * For instance, with the following snippet:
 3208  
      * <br/>
 3209  
      *
 3210  
      * <code>
 3211  
      * <font color="#808080">1</font>&nbsp;<font color="#ffffff"></font><br />
 3212  
      * <font color="#808080">2</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3213  
      * <font color="#3f5fbf">&#47;&#42;&#42;</font><br />
 3214  
      * <font color="#808080">3</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3215  
      * <font color="#3f5fbf">&#42;&nbsp;Dummy&nbsp;Javadoc&nbsp;comment.</font><br />
 3216  
      * <font color="#808080">4</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3217  
      * <font color="#3f5fbf">&#42;&nbsp;</font><font color="#7f9fbf">@param&nbsp;</font>
 3218  
      * <font color="#3f5fbf">s&nbsp;a&nbsp;String</font><br />
 3219  
      * <font color="#808080">5</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3220  
      * <font color="#3f5fbf">&#42;&#47;</font><br />
 3221  
      * <font color="#808080">6</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3222  
      * <font color="#7f0055"><b>public&nbsp;</b></font><font color="#7f0055"><b>void&nbsp;</b></font>
 3223  
      * <font color="#000000">dummyMethod</font><font color="#000000">(&nbsp;</font>
 3224  
      * <font color="#000000">String&nbsp;s&nbsp;</font><font color="#000000">){}</font><br />
 3225  
      * </code>
 3226  
      *
 3227  
      * <br/>
 3228  
      * The return will be:
 3229  
      * <br/>
 3230  
      *
 3231  
      * <code>
 3232  
      * <font color="#808080">1</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3233  
      * <font color="#3f5fbf">&#47;&#42;&#42;</font><br />
 3234  
      * <font color="#808080">2</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3235  
      * <font color="#3f5fbf">&#42;&nbsp;Dummy&nbsp;Javadoc&nbsp;comment.</font><br />
 3236  
      * <font color="#808080">3</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3237  
      * <font color="#3f5fbf">&#42;&nbsp;</font><font color="#7f9fbf">@param&nbsp;</font>
 3238  
      * <font color="#3f5fbf">s&nbsp;a&nbsp;String</font><br />
 3239  
      * <font color="#808080">4</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3240  
      * <font color="#3f5fbf">&#42;&#47;</font><br />
 3241  
      * </code>
 3242  
      *
 3243  
      * @param javaClassContent not null
 3244  
      * @param entity not null
 3245  
      * @return return the original javadoc as String for the current entity
 3246  
      * @throws IOException if any
 3247  
      */
 3248  
     private static String extractOriginalJavadoc( final String javaClassContent, final AbstractJavaEntity entity )
 3249  
         throws IOException
 3250  
     {
 3251  126
         if ( entity.getComment() == null )
 3252  
         {
 3253  0
             return "";
 3254  
         }
 3255  
 
 3256  126
         String[] javaClassContentLines = getLines( javaClassContent );
 3257  126
         List list = new LinkedList();
 3258  761
         for ( int i = entity.getLineNumber() - 2; i >= 0; i-- )
 3259  
         {
 3260  761
             String line = javaClassContentLines[i];
 3261  
 
 3262  761
             list.add( trimRight( line ) );
 3263  761
             if ( line.trim().startsWith( START_JAVADOC ) )
 3264  
             {
 3265  126
                 break;
 3266  
             }
 3267  
         }
 3268  
 
 3269  126
         Collections.reverse( list );
 3270  
 
 3271  126
         return StringUtils.join( list.iterator(), EOL );
 3272  
     }
 3273  
 
 3274  
     /**
 3275  
      * Extract the Javadoc comment between {@link #START_JAVADOC} and {@link #END_JAVADOC} form the entity. This method
 3276  
      * takes care of the Javadoc indentation. All javadoc lines will be trimmed on right.
 3277  
      * <br/>
 3278  
      * For instance, with the following snippet:
 3279  
      * <br/>
 3280  
      *
 3281  
      * <code>
 3282  
      * <font color="#808080">1</font>&nbsp;<font color="#ffffff"></font><br />
 3283  
      * <font color="#808080">2</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3284  
      * <font color="#3f5fbf">&#47;&#42;&#42;</font><br />
 3285  
      * <font color="#808080">3</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3286  
      * <font color="#3f5fbf">&#42;&nbsp;Dummy&nbsp;Javadoc&nbsp;comment.</font><br />
 3287  
      * <font color="#808080">4</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3288  
      * <font color="#3f5fbf">&#42;&nbsp;</font><font color="#7f9fbf">@param&nbsp;</font>
 3289  
      * <font color="#3f5fbf">s&nbsp;a&nbsp;String</font><br />
 3290  
      * <font color="#808080">5</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3291  
      * <font color="#3f5fbf">&#42;&#47;</font><br />
 3292  
      * <font color="#808080">6</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3293  
      * <font color="#7f0055"><b>public&nbsp;</b></font><font color="#7f0055"><b>void&nbsp;</b></font>
 3294  
      * <font color="#000000">dummyMethod</font><font color="#000000">(&nbsp;</font>
 3295  
      * <font color="#000000">String&nbsp;s&nbsp;</font><font color="#000000">){}</font><br />
 3296  
      * </code>
 3297  
      *
 3298  
      * <br/>
 3299  
      * The return will be:
 3300  
      * <br/>
 3301  
      *
 3302  
      * <code>
 3303  
      * <font color="#808080">1</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3304  
      * <font color="#3f5fbf">&#42;&nbsp;Dummy&nbsp;Javadoc&nbsp;comment.</font><br />
 3305  
      * <font color="#808080">2</font>&nbsp;<font color="#ffffff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font>
 3306  
      * <font color="#3f5fbf">&#42;&nbsp;</font><font color="#7f9fbf">@param&nbsp;</font>
 3307  
      * <font color="#3f5fbf">s&nbsp;a&nbsp;String</font><br />
 3308  
      * </code>
 3309  
      *
 3310  
      * @param javaClassContent not null
 3311  
      * @param entity not null
 3312  
      * @return return the original javadoc as String for the current entity
 3313  
      * @throws IOException if any
 3314  
      */
 3315  
     private static String extractOriginalJavadocContent( final String javaClassContent,
 3316  
                                                          final AbstractJavaEntity entity )
 3317  
         throws IOException
 3318  
     {
 3319  81
         if ( entity.getComment() == null )
 3320  
         {
 3321  0
             return "";
 3322  
         }
 3323  
 
 3324  81
         String originalJavadoc = extractOriginalJavadoc( javaClassContent, entity );
 3325  81
         if ( originalJavadoc.indexOf( START_JAVADOC ) != -1 )
 3326  
         {
 3327  81
             originalJavadoc =
 3328  
                 originalJavadoc.substring( originalJavadoc.indexOf( START_JAVADOC ) + START_JAVADOC.length() );
 3329  
         }
 3330  81
         if ( originalJavadoc.indexOf( END_JAVADOC ) != -1 )
 3331  
         {
 3332  81
             originalJavadoc = originalJavadoc.substring( 0, originalJavadoc.indexOf( END_JAVADOC ) );
 3333  
         }
 3334  81
         if ( originalJavadoc.startsWith( "\r\n" ) )
 3335  
         {
 3336  0
             originalJavadoc = originalJavadoc.substring( 2 );
 3337  
         }
 3338  81
         else if ( originalJavadoc.startsWith( "\n" ) || originalJavadoc.startsWith( "\r" ) )
 3339  
         {
 3340  75
             originalJavadoc = originalJavadoc.substring( 1 );
 3341  
         }
 3342  
 
 3343  81
         return trimRight( originalJavadoc );
 3344  
     }
 3345  
 
 3346  
     /**
 3347  
      * @param content not null
 3348  
      * @return the content without last lines containing javadoc separator (ie <code> * </code>)
 3349  
      * @throws IOException if any
 3350  
      * @see #getJavadocComment(String, AbstractInheritableJavaEntity, DocletTag)
 3351  
      */
 3352  
     private static String removeLastEmptyJavadocLines( final String content )
 3353  
         throws IOException
 3354  
     {
 3355  110
         if ( content.indexOf( EOL ) == -1 )
 3356  
         {
 3357  46
             return content;
 3358  
         }
 3359  
 
 3360  64
         String[] lines = getLines( content );
 3361  64
         if ( lines.length == 1 )
 3362  
         {
 3363  0
             return content;
 3364  
         }
 3365  
 
 3366  64
         List linesList = new LinkedList();
 3367  64
         linesList.addAll( Arrays.asList( lines ) );
 3368  
 
 3369  64
         Collections.reverse( linesList );
 3370  
 
 3371  64
         for ( Iterator it = linesList.iterator(); it.hasNext(); )
 3372  
         {
 3373  84
             String line = (String) it.next();
 3374  
 
 3375  84
             if ( line.trim().equals( "*" ) )
 3376  
             {
 3377  20
                 it.remove();
 3378  
             }
 3379  
             else
 3380  
             {
 3381  
                 break;
 3382  
             }
 3383  20
         }
 3384  
 
 3385  64
         Collections.reverse( linesList );
 3386  
 
 3387  64
         return StringUtils.join( linesList.iterator(), EOL );
 3388  
     }
 3389  
 
 3390  
     /**
 3391  
      * @param content not null
 3392  
      * @return the javadoc comment with the given indentation
 3393  
      * @throws IOException if any
 3394  
      * @see #getJavadocComment(String, AbstractInheritableJavaEntity, DocletTag)
 3395  
      */
 3396  
     private static String alignIndentationJavadocLines( final String content, final String indent )
 3397  
         throws IOException
 3398  
     {
 3399  67
         String[] lines = getLines( content );
 3400  
 
 3401  67
         StringBuffer sb = new StringBuffer();
 3402  145
         for ( int i = 0; i < lines.length; i++ )
 3403  
         {
 3404  78
             String line = lines[i];
 3405  78
             if ( !line.trim().startsWith( "*" ) )
 3406  
             {
 3407  1
                 line = "*" + line;
 3408  
             }
 3409  78
             sb.append( indent ).append( " " ).append( trimLeft( line ) );
 3410  78
             if ( i < lines.length - 1 )
 3411  
             {
 3412  12
                 sb.append( EOL );
 3413  
             }
 3414  
         }
 3415  
 
 3416  67
         return sb.toString();
 3417  
     }
 3418  
 
 3419  
     /**
 3420  
      * Autodetect the indentation of a given line:
 3421  
      * <pre>
 3422  
      * autodetectIndentation( null ) = "";
 3423  
      * autodetectIndentation( "a" ) = "";
 3424  
      * autodetectIndentation( "    a" ) = "    ";
 3425  
      * autodetectIndentation( "\ta" ) = "\t";
 3426  
      * </pre>
 3427  
      *
 3428  
      * @param line not null
 3429  
      * @return the indentation for the given line.
 3430  
      */
 3431  
     private static String autodetectIndentation( final String line )
 3432  
     {
 3433  805
         if ( StringUtils.isEmpty( line ) )
 3434  
         {
 3435  109
             return "";
 3436  
         }
 3437  
 
 3438  696
         return line.substring( 0, line.indexOf( trimLeft( line ) ) );
 3439  
     }
 3440  
 
 3441  
     /**
 3442  
      * @param content not null
 3443  
      * @return an array of all content lines
 3444  
      * @throws IOException if any
 3445  
      */
 3446  
     private static String[] getLines( final String content )
 3447  
         throws IOException
 3448  
     {
 3449  293
         List lines = new LinkedList();
 3450  
 
 3451  293
         BufferedReader reader = new BufferedReader( new StringReader( content ) );
 3452  293
         String line = reader.readLine();
 3453  23180
         while ( line != null )
 3454  
         {
 3455  22887
             lines.add( line );
 3456  22887
             line = reader.readLine();
 3457  
         }
 3458  
 
 3459  293
         return (String[]) lines.toArray( new String[0] );
 3460  
     }
 3461  
 
 3462  
     /**
 3463  
      * Trim a given line on the left:
 3464  
      * <pre>
 3465  
      * trimLeft( null ) = "";
 3466  
      * trimLeft( "  " ) = "";
 3467  
      * trimLeft( "a" ) = "a";
 3468  
      * trimLeft( "    a" ) = "a";
 3469  
      * trimLeft( "\ta" ) = "a";
 3470  
      * trimLeft( "    a    " ) = "a    ";
 3471  
      * </pre>
 3472  
      *
 3473  
      * @param text
 3474  
      * @return the text trimmed on left side or empty if text is null.
 3475  
      */
 3476  
     private static String trimLeft( final String text )
 3477  
     {
 3478  782
         if ( StringUtils.isEmpty( text ) || StringUtils.isEmpty( text.trim() ) )
 3479  
         {
 3480  3
             return "";
 3481  
         }
 3482  
 
 3483  779
         String textTrimmed = text.trim();
 3484  779
         return text.substring( text.indexOf( textTrimmed ), text.length() );
 3485  
     }
 3486  
 
 3487  
     /**
 3488  
      * Trim a given line on the right:
 3489  
      * <pre>
 3490  
      * trimRight( null ) = "";
 3491  
      * trimRight( "  " ) = "";
 3492  
      * trimRight( "a" ) = "a";
 3493  
      * trimRight( "a\t" ) = "a";
 3494  
      * trimRight( "    a    " ) = "    a";
 3495  
      * </pre>
 3496  
      *
 3497  
      * @param text
 3498  
      * @return the text trimmed on tight side or empty if text is null.
 3499  
      */
 3500  
     private static String trimRight( final String text )
 3501  
     {
 3502  974
         if ( StringUtils.isEmpty( text ) || StringUtils.isEmpty( text.trim() ) )
 3503  
         {
 3504  3
             return "";
 3505  
         }
 3506  
 
 3507  971
         String textTrimmed = text.trim();
 3508  971
         return text.substring( 0, text.indexOf( textTrimmed ) + textTrimmed.length() );
 3509  
     }
 3510  
 
 3511  
     /**
 3512  
      * Workaroung for QDOX-173 about generic.
 3513  
      *
 3514  
      * @param params not null
 3515  
      * @return the wanted params.
 3516  
      */
 3517  
     private static String[] fixQdox173( String[] params )
 3518  
     {
 3519  89
         if ( params == null || params.length == 0 )
 3520  
         {
 3521  0
             return params;
 3522  
         }
 3523  89
         if ( params.length < 3 )
 3524  
         {
 3525  25
             return params;
 3526  
         }
 3527  
 
 3528  64
         if ( params[0].trim().equals( "<" ) && params[2].trim().equals( ">" ) )
 3529  
         {
 3530  14
             String param = params[1];
 3531  14
             List l = new ArrayList( Arrays.asList( params ) );
 3532  14
             l.set( 1, "<" + param + ">" );
 3533  14
             l.remove( 0 );
 3534  14
             l.remove( 1 );
 3535  
 
 3536  14
             return (String[]) l.toArray( new String[0] );
 3537  
         }
 3538  
 
 3539  50
         return params;
 3540  
     }
 3541  
 
 3542  
     /**
 3543  
      * Wrapper class for the entity's tags.
 3544  
      */
 3545  2
     private class JavaEntityTags
 3546  
     {
 3547  
         private final AbstractInheritableJavaEntity entity;
 3548  
 
 3549  
         private final boolean isJavaMethod;
 3550  
 
 3551  
         /** List of tag names. */
 3552  
         private List namesTags;
 3553  
 
 3554  
         /** Map with java parameter as key and original Javadoc lines as values. */
 3555  
         private Map tagParams;
 3556  
 
 3557  
         /** Original javadoc lines. */
 3558  
         private String tagReturn;
 3559  
 
 3560  
         /** Map with java throw as key and original Javadoc lines as values. */
 3561  
         private Map tagThrows;
 3562  
 
 3563  
         /** Original javadoc lines for unknown tags. */
 3564  
         private List unknownsTags;
 3565  
 
 3566  
         public JavaEntityTags( AbstractInheritableJavaEntity entity, boolean isJavaMethod )
 3567  17
         {
 3568  17
             this.entity = entity;
 3569  17
             this.isJavaMethod = isJavaMethod;
 3570  17
             this.namesTags = new LinkedList();
 3571  17
             this.tagParams = new LinkedHashMap();
 3572  17
             this.tagThrows = new LinkedHashMap();
 3573  17
             this.unknownsTags = new LinkedList();
 3574  17
         }
 3575  
 
 3576  
         public List getNamesTags()
 3577  
         {
 3578  49
             return namesTags;
 3579  
         }
 3580  
 
 3581  
         public String getJavadocReturnTag()
 3582  
         {
 3583  25
             return tagReturn;
 3584  
         }
 3585  
 
 3586  
         public void setJavadocReturnTag( String s )
 3587  
         {
 3588  8
             tagReturn = s;
 3589  8
         }
 3590  
 
 3591  
         public List getUnknownTags()
 3592  
         {
 3593  0
             return unknownsTags;
 3594  
         }
 3595  
 
 3596  
         public void putJavadocParamTag( String paramName, String originalJavadocTag )
 3597  
         {
 3598  18
             tagParams.put( paramName, originalJavadocTag );
 3599  18
         }
 3600  
 
 3601  
         public String getJavadocParamTag( String paramName )
 3602  
         {
 3603  14
             return getJavadocParamTag( paramName, false );
 3604  
         }
 3605  
 
 3606  
         public String getJavadocParamTag( String paramName, boolean nullable )
 3607  
         {
 3608  36
             String originalJavadocTag = (String) tagParams.get( paramName );
 3609  36
             if ( !nullable && originalJavadocTag == null && getLog().isWarnEnabled() )
 3610  
             {
 3611  0
                 getLog().warn( getMessage( paramName, "javaEntityTags.tagParams" ) );
 3612  
             }
 3613  
 
 3614  36
             return originalJavadocTag;
 3615  
         }
 3616  
 
 3617  
         public void putJavadocThrowsTag( String paramName, String originalJavadocTag )
 3618  
         {
 3619  9
             tagThrows.put( paramName, originalJavadocTag );
 3620  9
         }
 3621  
 
 3622  
         public String getJavadocThrowsTag( String paramName )
 3623  
         {
 3624  5
             return getJavadocThrowsTag( paramName, false );
 3625  
         }
 3626  
 
 3627  
         public String getJavadocThrowsTag( String paramName, boolean nullable )
 3628  
         {
 3629  7
             String originalJavadocTag = (String) tagThrows.get( paramName );
 3630  7
             if ( !nullable && originalJavadocTag == null && getLog().isWarnEnabled() )
 3631  
             {
 3632  0
                 getLog().warn( getMessage( paramName, "javaEntityTags.tagThrows" ) );
 3633  
             }
 3634  
 
 3635  7
             return originalJavadocTag;
 3636  
         }
 3637  
 
 3638  
         private String getMessage( String paramName, String mapName )
 3639  
         {
 3640  0
             StringBuffer msg = new StringBuffer();
 3641  0
             msg.append( "No param '" ).append( paramName );
 3642  0
             msg.append( "' key found in " + mapName + " for the entity: " );
 3643  0
             if ( isJavaMethod )
 3644  
             {
 3645  0
                 JavaMethod javaMethod = (JavaMethod) entity;
 3646  0
                 msg.append( getJavaMethodAsString( javaMethod ) );
 3647  0
             }
 3648  
             else
 3649  
             {
 3650  0
                 JavaClass javaClass = (JavaClass) entity;
 3651  0
                 msg.append( javaClass.getFullyQualifiedName() );
 3652  
             }
 3653  
 
 3654  0
             return msg.toString();
 3655  
         }
 3656  
 
 3657  
         /** {@inheritDoc} */
 3658  
         public String toString()
 3659  
         {
 3660  0
             StringBuffer sb = new StringBuffer();
 3661  
 
 3662  0
             sb.append( "namesTags=" ).append( namesTags ).append( "\n" );
 3663  0
             sb.append( "tagParams=" ).append( tagParams ).append( "\n" );
 3664  0
             sb.append( "tagReturn=" ).append( tagReturn ).append( "\n" );
 3665  0
             sb.append( "tagThrows=" ).append( tagThrows ).append( "\n" );
 3666  0
             sb.append( "unknownsTags=" ).append( unknownsTags ).append( "\n" );
 3667  
 
 3668  0
             return sb.toString();
 3669  
         }
 3670  
     }
 3671  
 }