1
2
3
4
5 package com.qulice.checkstyle;
6
7 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
8 import com.puppycrawl.tools.checkstyle.api.DetailAST;
9 import com.puppycrawl.tools.checkstyle.api.FileContents;
10 import com.puppycrawl.tools.checkstyle.api.TextBlock;
11 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
12 import java.util.HashSet;
13 import java.util.Set;
14 import java.util.regex.Matcher;
15 import java.util.regex.Pattern;
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 public final class JavadocThrowsCheck extends AbstractCheck {
51
52
53
54
55
56 private static final Pattern TAG = Pattern.compile(
57 "^\\s*(?:\\*|/\\*\\*)?\\s*@(throws|exception)\\s+(\\S+)"
58 );
59
60
61
62
63 public JavadocThrowsCheck() {
64
65 }
66
67 @Override
68 public int[] getDefaultTokens() {
69 return new int[] {
70 TokenTypes.METHOD_DEF,
71 TokenTypes.CTOR_DEF,
72 };
73 }
74
75 @Override
76 public int[] getAcceptableTokens() {
77 return this.getDefaultTokens();
78 }
79
80 @Override
81 public int[] getRequiredTokens() {
82 return this.getDefaultTokens();
83 }
84
85 @Override
86 @SuppressWarnings("deprecation")
87 public void visitToken(final DetailAST ast) {
88 final FileContents contents = this.getFileContents();
89 final TextBlock doc = contents.getJavadocBefore(ast.getLineNo());
90 if (doc == null) {
91 return;
92 }
93 final Set<String> declared = JavadocThrowsCheck.declared(ast);
94 final String[] lines = doc.getText();
95 final int first = doc.getStartLineNo();
96 for (int idx = 0; idx < lines.length; idx += 1) {
97 final Matcher matcher = JavadocThrowsCheck.TAG.matcher(lines[idx]);
98 if (!matcher.find()) {
99 continue;
100 }
101 final String type = matcher.group(2);
102 if (!declared.contains(JavadocThrowsCheck.simple(type))) {
103 this.log(
104 first + idx,
105 "Javadoc ''@{0} {1}'' is not declared in method signature",
106 matcher.group(1),
107 type
108 );
109 }
110 }
111 }
112
113 private static Set<String> declared(final DetailAST ast) {
114 final Set<String> names = new HashSet<>(0);
115 final DetailAST clause = ast.findFirstToken(TokenTypes.LITERAL_THROWS);
116 if (clause != null) {
117 DetailAST child = clause.getFirstChild();
118 while (child != null) {
119 if (child.getType() == TokenTypes.IDENT) {
120 names.add(child.getText());
121 } else if (child.getType() == TokenTypes.DOT) {
122 names.add(JavadocThrowsCheck.rightmost(child));
123 }
124 child = child.getNextSibling();
125 }
126 }
127 return names;
128 }
129
130 private static String simple(final String text) {
131 final int dot = text.lastIndexOf('.');
132 final String result;
133 if (dot < 0) {
134 result = text;
135 } else {
136 result = text.substring(dot + 1);
137 }
138 return result;
139 }
140
141 private static String rightmost(final DetailAST dot) {
142 DetailAST right = dot.getLastChild();
143 while (right.getType() == TokenTypes.DOT) {
144 right = right.getLastChild();
145 }
146 return right.getText();
147 }
148 }