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