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