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