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