1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.checks.javadoc;
21
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.ListIterator;
28 import java.util.Set;
29 import java.util.regex.MatchResult;
30 import java.util.regex.Matcher;
31 import java.util.regex.Pattern;
32
33 import com.puppycrawl.tools.checkstyle.StatelessCheck;
34 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
35 import com.puppycrawl.tools.checkstyle.api.DetailAST;
36 import com.puppycrawl.tools.checkstyle.api.FileContents;
37 import com.puppycrawl.tools.checkstyle.api.FullIdent;
38 import com.puppycrawl.tools.checkstyle.api.TextBlock;
39 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
40 import com.puppycrawl.tools.checkstyle.checks.naming.AccessModifierOption;
41 import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
42 import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
43 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
44 import com.puppycrawl.tools.checkstyle.utils.UnmodifiableCollectionUtil;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193 @StatelessCheck
194 public class JavadocMethodCheck extends AbstractCheck {
195
196
197
198
199
200 public static final String MSG_CLASS_INFO = "javadoc.classInfo";
201
202
203
204
205
206 public static final String MSG_UNUSED_TAG_GENERAL = "javadoc.unusedTagGeneral";
207
208
209
210
211
212 public static final String MSG_INVALID_INHERIT_DOC = "javadoc.invalidInheritDoc";
213
214
215
216
217
218 public static final String MSG_UNUSED_TAG = "javadoc.unusedTag";
219
220
221
222
223
224 public static final String MSG_EXPECTED_TAG = "javadoc.expectedTag";
225
226
227
228
229
230 public static final String MSG_RETURN_EXPECTED = "javadoc.return.expected";
231
232
233
234
235
236 public static final String MSG_DUPLICATE_TAG = "javadoc.duplicateTag";
237
238
239 private static final String ELEMENT_START = "<";
240
241
242 private static final String ELEMENT_END = ">";
243
244
245 private static final Pattern MATCH_JAVADOC_ARG = CommonUtil.createPattern(
246 "^\\s*(?>\\*|\\/\\*\\*)?\\s*@(throws|exception|param)\\s+(\\S+)\\s+\\S*");
247
248 private static final Pattern MATCH_JAVADOC_ARG_MISSING_DESCRIPTION =
249 CommonUtil.createPattern("^\\s*(?>\\*|\\/\\*\\*)?\\s*@(throws|exception|param)\\s+"
250 + "(\\S[^*]*)(?:(\\s+|\\*\\/))?");
251
252
253 private static final Pattern MATCH_JAVADOC_MULTILINE_CONT =
254 CommonUtil.createPattern("(\\*\\/|@|[^\\s\\*])");
255
256
257 private static final String END_JAVADOC = "*/";
258
259 private static final String NEXT_TAG = "@";
260
261
262 private static final Pattern MATCH_JAVADOC_NOARG =
263 CommonUtil.createPattern("^\\s*(?>\\*|\\/\\*\\*)?\\s*@(return|see)\\s+\\S");
264
265 private static final Pattern MATCH_JAVADOC_NOARG_INLINE_RETURN =
266 CommonUtil.createPattern("^\\s*(?>\\*|\\/\\*\\*)?\\s*\\{?@(return|see)\\s+\\S");
267
268 private static final Pattern MATCH_JAVADOC_NOARG_MULTILINE_START =
269 CommonUtil.createPattern("^\\s*(?>\\*|\\/\\*\\*)?\\s*@(return|see)\\s*$");
270
271 private static final Pattern MATCH_JAVADOC_NOARG_CURLY =
272 CommonUtil.createPattern("\\{\\s*@(inheritDoc)\\s*\\}");
273
274
275
276
277 private boolean allowInlineReturn;
278
279
280 private AccessModifierOption[] accessModifiers = {
281 AccessModifierOption.PUBLIC,
282 AccessModifierOption.PROTECTED,
283 AccessModifierOption.PACKAGE,
284 AccessModifierOption.PRIVATE,
285 };
286
287
288
289
290 private boolean validateThrows;
291
292
293
294
295
296 private boolean allowMissingParamTags;
297
298
299
300
301
302 private boolean allowMissingReturnTag;
303
304
305 private Set<String> allowedAnnotations = Set.of("Override");
306
307
308
309
310
311
312
313 public void setAllowInlineReturn(boolean value) {
314 allowInlineReturn = value;
315 }
316
317
318
319
320
321
322
323 public void setValidateThrows(boolean value) {
324 validateThrows = value;
325 }
326
327
328
329
330
331
332
333 public void setAllowedAnnotations(String... userAnnotations) {
334 allowedAnnotations = Set.of(userAnnotations);
335 }
336
337
338
339
340
341
342
343 public void setAccessModifiers(AccessModifierOption... accessModifiers) {
344 this.accessModifiers =
345 UnmodifiableCollectionUtil.copyOfArray(accessModifiers, accessModifiers.length);
346 }
347
348
349
350
351
352
353
354
355 public void setAllowMissingParamTags(boolean flag) {
356 allowMissingParamTags = flag;
357 }
358
359
360
361
362
363
364
365
366 public void setAllowMissingReturnTag(boolean flag) {
367 allowMissingReturnTag = flag;
368 }
369
370 @Override
371 public final int[] getRequiredTokens() {
372 return CommonUtil.EMPTY_INT_ARRAY;
373 }
374
375 @Override
376 public int[] getDefaultTokens() {
377 return getAcceptableTokens();
378 }
379
380 @Override
381 public int[] getAcceptableTokens() {
382 return new int[] {
383 TokenTypes.METHOD_DEF,
384 TokenTypes.CTOR_DEF,
385 TokenTypes.ANNOTATION_FIELD_DEF,
386 TokenTypes.COMPACT_CTOR_DEF,
387 };
388 }
389
390 @Override
391 public final void visitToken(DetailAST ast) {
392 processAST(ast);
393 }
394
395
396
397
398
399
400
401
402 @SuppressWarnings("deprecation")
403 private void processAST(DetailAST ast) {
404 if (shouldCheck(ast)) {
405 final FileContents contents = getFileContents();
406 final TextBlock textBlock = contents.getJavadocBefore(ast.getLineNo());
407
408 if (textBlock != null) {
409 checkComment(ast, textBlock);
410 }
411 }
412 }
413
414
415
416
417
418
419
420 private boolean shouldCheck(final DetailAST ast) {
421 final AccessModifierOption surroundingAccessModifier = CheckUtil
422 .getSurroundingAccessModifier(ast);
423 final AccessModifierOption accessModifier = CheckUtil
424 .getAccessModifierFromModifiersToken(ast);
425 return Arrays.stream(accessModifiers)
426 .anyMatch(modifier -> modifier == surroundingAccessModifier)
427 && Arrays.stream(accessModifiers).anyMatch(modifier -> modifier == accessModifier);
428 }
429
430
431
432
433
434
435
436 private void checkComment(DetailAST ast, TextBlock comment) {
437 final List<JavadocTag> tags = getMethodTags(comment);
438
439 if (!hasShortCircuitTag(ast, tags)) {
440 if (ast.getType() == TokenTypes.ANNOTATION_FIELD_DEF) {
441 checkReturnTag(tags, ast.getLineNo(), true);
442 }
443 else {
444 final Iterator<JavadocTag> it = tags.iterator();
445
446 boolean hasInheritDocTag = false;
447 while (!hasInheritDocTag && it.hasNext()) {
448 hasInheritDocTag = it.next().isInheritDocTag();
449 }
450 final boolean reportExpectedTags = !hasInheritDocTag
451 && !AnnotationUtil.containsAnnotation(ast, allowedAnnotations);
452
453
454 if (ast.getType() == TokenTypes.COMPACT_CTOR_DEF) {
455 checkRecordParamTags(tags, ast, reportExpectedTags);
456 }
457 else {
458 checkParamTags(tags, ast, reportExpectedTags);
459 }
460 final List<ExceptionInfo> throwed =
461 combineExceptionInfo(getThrows(ast), getThrowed(ast));
462 checkThrowsTags(tags, throwed, reportExpectedTags);
463 if (CheckUtil.isNonVoidMethod(ast)) {
464 checkReturnTag(tags, ast.getLineNo(), reportExpectedTags);
465 }
466 }
467 }
468 tags.stream().filter(javadocTag -> !javadocTag.isSeeOrInheritDocTag())
469 .forEach(javadocTag -> log(javadocTag.getLineNo(), MSG_UNUSED_TAG_GENERAL));
470 }
471
472
473
474
475
476
477
478 private static List<DetailAST> getRecordComponents(final DetailAST recordDef) {
479 final List<DetailAST> components = new ArrayList<>();
480 final DetailAST recordDecl = recordDef.findFirstToken(TokenTypes.RECORD_COMPONENTS);
481
482 DetailAST child = recordDecl.getFirstChild();
483 while (child != null) {
484 if (child.getType() == TokenTypes.RECORD_COMPONENT_DEF) {
485 components.add(child.findFirstToken(TokenTypes.IDENT));
486 }
487 child = child.getNextSibling();
488 }
489 return components;
490 }
491
492
493
494
495
496
497
498 private static DetailAST getRecordDef(DetailAST ast) {
499 DetailAST current = ast;
500 while (current.getType() != TokenTypes.RECORD_DEF) {
501 current = current.getParent();
502 }
503 return current;
504 }
505
506
507
508
509
510
511
512
513
514 private boolean hasShortCircuitTag(final DetailAST ast, final List<JavadocTag> tags) {
515 boolean result = true;
516
517 if (tags.size() == 1
518 && tags.get(0).isInheritDocTag()) {
519
520 if (!JavadocTagInfo.INHERIT_DOC.isValidOn(ast)) {
521 log(ast, MSG_INVALID_INHERIT_DOC);
522 }
523 }
524 else {
525 result = false;
526 }
527 return result;
528 }
529
530
531
532
533
534
535
536
537 private List<JavadocTag> getMethodTags(TextBlock comment) {
538 Pattern matchJavadocNoArg = MATCH_JAVADOC_NOARG;
539 if (allowInlineReturn) {
540 matchJavadocNoArg = MATCH_JAVADOC_NOARG_INLINE_RETURN;
541 }
542 final String[] lines = comment.getText();
543 final List<JavadocTag> tags = new ArrayList<>();
544 int currentLine = comment.getStartLineNo() - 1;
545 final int startColumnNumber = comment.getStartColNo();
546
547 for (int i = 0; i < lines.length; i++) {
548 currentLine++;
549 final Matcher javadocArgMatcher =
550 MATCH_JAVADOC_ARG.matcher(lines[i]);
551 final Matcher javadocArgMissingDescriptionMatcher =
552 MATCH_JAVADOC_ARG_MISSING_DESCRIPTION.matcher(lines[i]);
553 final Matcher javadocNoargMatcher =
554 matchJavadocNoArg.matcher(lines[i]);
555 final Matcher noargCurlyMatcher =
556 MATCH_JAVADOC_NOARG_CURLY.matcher(lines[i]);
557 final Matcher noargMultilineStart =
558 MATCH_JAVADOC_NOARG_MULTILINE_START.matcher(lines[i]);
559
560 if (javadocArgMatcher.find()) {
561 final int col = calculateTagColumn(javadocArgMatcher, i, startColumnNumber);
562 tags.add(new JavadocTag(currentLine, col, javadocArgMatcher.group(1),
563 javadocArgMatcher.group(2)));
564 }
565 else if (javadocArgMissingDescriptionMatcher.find()) {
566 final int col = calculateTagColumn(javadocArgMissingDescriptionMatcher, i,
567 startColumnNumber);
568 tags.add(new JavadocTag(currentLine, col,
569 javadocArgMissingDescriptionMatcher.group(1),
570 javadocArgMissingDescriptionMatcher.group(2)));
571 }
572 else if (javadocNoargMatcher.find()) {
573 final int col = calculateTagColumn(javadocNoargMatcher, i, startColumnNumber);
574 tags.add(new JavadocTag(currentLine, col, javadocNoargMatcher.group(1)));
575 }
576 else if (noargCurlyMatcher.find()) {
577 tags.add(new JavadocTag(currentLine, 0, noargCurlyMatcher.group(1)));
578 }
579 else if (noargMultilineStart.find()) {
580 tags.addAll(getMultilineNoArgTags(noargMultilineStart, lines, i, currentLine));
581 }
582 }
583 return tags;
584 }
585
586
587
588
589
590
591
592
593
594 private static int calculateTagColumn(MatchResult javadocTagMatchResult,
595 int lineNumber, int startColumnNumber) {
596 int col = javadocTagMatchResult.start(1) - 1;
597 if (lineNumber == 0) {
598 col += startColumnNumber;
599 }
600 return col;
601 }
602
603
604
605
606
607
608
609
610
611
612 private static List<JavadocTag> getMultilineNoArgTags(final Matcher noargMultilineStart,
613 final String[] lines, final int lineIndex, final int tagLine) {
614 int remIndex = lineIndex;
615 Matcher multilineCont;
616
617 do {
618 remIndex++;
619 multilineCont = MATCH_JAVADOC_MULTILINE_CONT.matcher(lines[remIndex]);
620 } while (!multilineCont.find());
621
622 final List<JavadocTag> tags = new ArrayList<>();
623 final String lFin = multilineCont.group(1);
624 if (!NEXT_TAG.equals(lFin)
625 && !END_JAVADOC.equals(lFin)) {
626 final String param1 = noargMultilineStart.group(1);
627 final int col = noargMultilineStart.start(1) - 1;
628
629 tags.add(new JavadocTag(tagLine, col, param1));
630 }
631
632 return tags;
633 }
634
635
636
637
638
639
640
641 private static List<DetailAST> getParameters(DetailAST ast) {
642 final DetailAST params = ast.findFirstToken(TokenTypes.PARAMETERS);
643 final List<DetailAST> returnValue = new ArrayList<>();
644
645 DetailAST child = params.getFirstChild();
646 while (child != null) {
647 final DetailAST ident = child.findFirstToken(TokenTypes.IDENT);
648 if (ident != null) {
649 returnValue.add(ident);
650 }
651 child = child.getNextSibling();
652 }
653 return returnValue;
654 }
655
656
657
658
659
660
661
662 private static List<ExceptionInfo> getThrows(DetailAST ast) {
663 final List<ExceptionInfo> returnValue = new ArrayList<>();
664 final DetailAST throwsAST = ast
665 .findFirstToken(TokenTypes.LITERAL_THROWS);
666 if (throwsAST != null) {
667 DetailAST child = throwsAST.getFirstChild();
668 while (child != null) {
669 if (child.getType() == TokenTypes.IDENT
670 || child.getType() == TokenTypes.DOT) {
671 returnValue.add(getExceptionInfo(child));
672 }
673 child = child.getNextSibling();
674 }
675 }
676 return returnValue;
677 }
678
679
680
681
682
683
684
685 private static List<ExceptionInfo> getThrowed(DetailAST methodAst) {
686 final List<ExceptionInfo> returnValue = new ArrayList<>();
687 final List<DetailAST> throwLiterals = findTokensInAstByType(methodAst,
688 TokenTypes.LITERAL_THROW);
689 for (DetailAST throwAst : throwLiterals) {
690 if (!isInIgnoreBlock(methodAst, throwAst)) {
691 final DetailAST newAst = throwAst.getFirstChild().getFirstChild();
692 if (newAst.getType() == TokenTypes.LITERAL_NEW) {
693 final DetailAST child = newAst.getFirstChild();
694 returnValue.add(getExceptionInfo(child));
695 }
696 }
697 }
698 return returnValue;
699 }
700
701
702
703
704
705
706
707 private static ExceptionInfo getExceptionInfo(DetailAST ast) {
708 final FullIdent ident = FullIdent.createFullIdent(ast);
709 final DetailAST firstClassNameNode = getFirstClassNameNode(ast);
710 return new ExceptionInfo(firstClassNameNode,
711 new ClassInfo(new Token(ident)));
712 }
713
714
715
716
717
718
719
720 private static DetailAST getFirstClassNameNode(DetailAST ast) {
721 DetailAST startNode = ast;
722 while (startNode.getType() == TokenTypes.DOT) {
723 startNode = startNode.getFirstChild();
724 }
725 return startNode;
726 }
727
728
729
730
731
732
733
734
735
736
737 private static boolean isInIgnoreBlock(DetailAST methodBodyAst, DetailAST throwAst) {
738 DetailAST ancestor = throwAst;
739 while (ancestor != methodBodyAst) {
740 if (ancestor.getType() == TokenTypes.LAMBDA
741 || ancestor.getType() == TokenTypes.OBJBLOCK
742 || ancestor.findFirstToken(TokenTypes.LITERAL_CATCH) != null) {
743
744
745 break;
746 }
747 if (ancestor.getType() == TokenTypes.LITERAL_CATCH
748 || ancestor.getType() == TokenTypes.LITERAL_FINALLY) {
749
750
751 ancestor = ancestor.getParent();
752 }
753 ancestor = ancestor.getParent();
754 }
755 return ancestor != methodBodyAst;
756 }
757
758
759
760
761
762
763
764
765 private static List<ExceptionInfo> combineExceptionInfo(Collection<ExceptionInfo> first,
766 Iterable<ExceptionInfo> second) {
767 final List<ExceptionInfo> result = new ArrayList<>(first);
768 for (ExceptionInfo exceptionInfo : second) {
769 if (result.stream().noneMatch(item -> isExceptionInfoSame(item, exceptionInfo))) {
770 result.add(exceptionInfo);
771 }
772 }
773 return result;
774 }
775
776
777
778
779
780
781
782
783
784 public static List<DetailAST> findTokensInAstByType(DetailAST root, int astType) {
785 final List<DetailAST> result = new ArrayList<>();
786
787 DetailAST curNode = root;
788 do {
789
790 if (curNode.getType() == astType) {
791 result.add(curNode);
792 }
793
794 if (curNode.hasChildren()) {
795 curNode = curNode.getFirstChild();
796 continue;
797 }
798
799 while (curNode.getNextSibling() == null) {
800 curNode = curNode.getParent();
801 }
802
803 if (curNode != root) {
804 curNode = curNode.getNextSibling();
805 }
806 } while (curNode != root);
807 return result;
808 }
809
810
811
812
813
814
815
816
817
818
819 private void checkRecordParamTags(final List<JavadocTag> tags,
820 final DetailAST compactDef, boolean reportExpectedTags) {
821
822 final DetailAST parent = getRecordDef(compactDef);
823 final List<DetailAST> params = getRecordComponents(parent);
824
825 final ListIterator<JavadocTag> tagIt = tags.listIterator();
826 while (tagIt.hasNext()) {
827 final JavadocTag tag = tagIt.next();
828
829 if (!tag.isParamTag()) {
830 continue;
831 }
832
833 tagIt.remove();
834
835 final String arg1 = tag.getFirstArg();
836 final boolean found = removeMatchingParam(params, arg1);
837
838 if (!found) {
839 log(tag.getLineNo(), tag.getColumnNo(), MSG_UNUSED_TAG,
840 JavadocTagInfo.PARAM.getText(), arg1);
841 }
842 }
843
844 if (!allowMissingParamTags && reportExpectedTags) {
845 for (DetailAST param : params) {
846 log(compactDef, MSG_EXPECTED_TAG,
847 JavadocTagInfo.PARAM.getText(), param.getText());
848 }
849 }
850 }
851
852
853
854
855
856
857
858
859
860 private void checkParamTags(final List<JavadocTag> tags,
861 final DetailAST parent, boolean reportExpectedTags) {
862 final List<DetailAST> params = getParameters(parent);
863 final List<DetailAST> typeParams = CheckUtil
864 .getTypeParameters(parent);
865
866
867 final ListIterator<JavadocTag> tagIt = tags.listIterator();
868 while (tagIt.hasNext()) {
869 final JavadocTag tag = tagIt.next();
870
871 if (!tag.isParamTag()) {
872 continue;
873 }
874
875 tagIt.remove();
876
877 final String arg1 = tag.getFirstArg();
878 boolean found = removeMatchingParam(params, arg1);
879
880 if (arg1.endsWith(ELEMENT_END)) {
881 found = searchMatchingTypeParameter(typeParams,
882 arg1.substring(1, arg1.length() - 1));
883 }
884
885
886 if (!found) {
887 log(tag.getLineNo(), tag.getColumnNo(), MSG_UNUSED_TAG,
888 JavadocTagInfo.PARAM.getText(), arg1);
889 }
890 }
891
892
893
894 if (!allowMissingParamTags && reportExpectedTags) {
895 for (DetailAST param : params) {
896 log(param, MSG_EXPECTED_TAG,
897 JavadocTagInfo.PARAM.getText(), param.getText());
898 }
899
900 for (DetailAST typeParam : typeParams) {
901 log(typeParam, MSG_EXPECTED_TAG,
902 JavadocTagInfo.PARAM.getText(),
903 ELEMENT_START + typeParam.findFirstToken(TokenTypes.IDENT).getText()
904 + ELEMENT_END);
905 }
906 }
907 }
908
909
910
911
912
913
914
915
916
917
918 private static boolean searchMatchingTypeParameter(Iterable<DetailAST> typeParams,
919 String requiredTypeName) {
920
921 final Iterator<DetailAST> typeParamsIt = typeParams.iterator();
922 boolean found = false;
923 while (typeParamsIt.hasNext()) {
924 final DetailAST typeParam = typeParamsIt.next();
925 if (typeParam.findFirstToken(TokenTypes.IDENT).getText()
926 .equals(requiredTypeName)) {
927 found = true;
928 typeParamsIt.remove();
929 break;
930 }
931 }
932 return found;
933 }
934
935
936
937
938
939
940
941
942 private static boolean removeMatchingParam(Iterable<DetailAST> params, String paramName) {
943 boolean found = false;
944 final Iterator<DetailAST> paramIt = params.iterator();
945 while (paramIt.hasNext()) {
946 final DetailAST param = paramIt.next();
947 if (param.getText().equals(paramName)) {
948 found = true;
949 paramIt.remove();
950 break;
951 }
952 }
953 return found;
954 }
955
956
957
958
959
960
961
962
963
964
965 private void checkReturnTag(List<JavadocTag> tags, int lineNo,
966 boolean reportExpectedTags) {
967
968
969 boolean found = false;
970 final ListIterator<JavadocTag> it = tags.listIterator();
971 while (it.hasNext()) {
972 final JavadocTag javadocTag = it.next();
973 if (javadocTag.isReturnTag()) {
974 if (found) {
975 log(javadocTag.getLineNo(), javadocTag.getColumnNo(),
976 MSG_DUPLICATE_TAG,
977 JavadocTagInfo.RETURN.getText());
978 }
979 found = true;
980 it.remove();
981 }
982 }
983
984
985
986 if (!found && !allowMissingReturnTag && reportExpectedTags) {
987 log(lineNo, MSG_RETURN_EXPECTED);
988 }
989 }
990
991
992
993
994
995
996
997
998
999 private void checkThrowsTags(List<JavadocTag> tags,
1000 List<ExceptionInfo> throwsList, boolean reportExpectedTags) {
1001
1002 final ListIterator<JavadocTag> tagIt = tags.listIterator();
1003 while (tagIt.hasNext()) {
1004 final JavadocTag tag = tagIt.next();
1005
1006 if (!tag.isThrowsTag()) {
1007 continue;
1008 }
1009 tagIt.remove();
1010
1011
1012 processThrows(throwsList, tag.getFirstArg());
1013 }
1014
1015
1016 if (validateThrows && reportExpectedTags) {
1017 throwsList.stream().filter(exceptionInfo -> !exceptionInfo.isFound())
1018 .forEach(exceptionInfo -> {
1019 final Token token = exceptionInfo.getName();
1020 log(exceptionInfo.getAst(),
1021 MSG_EXPECTED_TAG,
1022 JavadocTagInfo.THROWS.getText(), token.getText());
1023 });
1024 }
1025 }
1026
1027
1028
1029
1030
1031
1032
1033 private static void processThrows(Iterable<ExceptionInfo> throwsIterable,
1034 String documentedClassName) {
1035 for (ExceptionInfo exceptionInfo : throwsIterable) {
1036 if (isClassNamesSame(exceptionInfo.getName().getText(),
1037 documentedClassName)) {
1038 exceptionInfo.setFound();
1039 break;
1040 }
1041 }
1042 }
1043
1044
1045
1046
1047
1048
1049
1050
1051 private static boolean isExceptionInfoSame(ExceptionInfo info1, ExceptionInfo info2) {
1052 return isClassNamesSame(info1.getName().getText(),
1053 info2.getName().getText());
1054 }
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064 private static boolean isClassNamesSame(String class1, String class2) {
1065 final String class1ShortName = class1
1066 .substring(class1.lastIndexOf('.') + 1);
1067 final String class2ShortName = class2
1068 .substring(class2.lastIndexOf('.') + 1);
1069 return class1ShortName.equals(class2ShortName);
1070 }
1071
1072
1073
1074
1075 private static class ClassInfo {
1076
1077
1078 private final Token name;
1079
1080
1081
1082
1083
1084
1085
1086 protected ClassInfo(final Token className) {
1087 name = className;
1088 }
1089
1090
1091
1092
1093
1094
1095 public final Token getName() {
1096 return name;
1097 }
1098
1099 }
1100
1101
1102
1103
1104 private static final class Token {
1105
1106
1107 private final String text;
1108
1109
1110
1111
1112
1113
1114 private Token(FullIdent fullIdent) {
1115 text = fullIdent.getText();
1116 }
1117
1118
1119
1120
1121
1122
1123 public String getText() {
1124 return text;
1125 }
1126
1127 }
1128
1129
1130 private static final class ExceptionInfo {
1131
1132
1133 private final DetailAST ast;
1134
1135
1136 private final ClassInfo classInfo;
1137
1138 private boolean found;
1139
1140
1141
1142
1143
1144
1145
1146 private ExceptionInfo(DetailAST ast, ClassInfo classInfo) {
1147 this.ast = ast;
1148 this.classInfo = classInfo;
1149 }
1150
1151
1152
1153
1154
1155
1156 private DetailAST getAst() {
1157 return ast;
1158 }
1159
1160
1161 private void setFound() {
1162 found = true;
1163 }
1164
1165
1166
1167
1168
1169
1170 private boolean isFound() {
1171 return found;
1172 }
1173
1174
1175
1176
1177
1178
1179 private Token getName() {
1180 return classInfo.getName();
1181 }
1182
1183 }
1184
1185 }