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