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