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