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