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