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