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