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 @Override
61 public int[] getDefaultTokens() {
62 return new int[] {
63 TokenTypes.METHOD_DEF,
64 TokenTypes.CTOR_DEF,
65 };
66 }
67
68 @Override
69 public int[] getAcceptableTokens() {
70 return this.getDefaultTokens();
71 }
72
73 @Override
74 public int[] getRequiredTokens() {
75 return this.getDefaultTokens();
76 }
77
78 @Override
79 @SuppressWarnings("deprecation")
80 public void visitToken(final DetailAST ast) {
81 final FileContents contents = this.getFileContents();
82 final TextBlock doc = contents.getJavadocBefore(ast.getLineNo());
83 if (doc == null) {
84 return;
85 }
86 final Set<String> declared = JavadocThrowsCheck.declared(ast);
87 final String[] lines = doc.getText();
88 final int first = doc.getStartLineNo();
89 for (int idx = 0; idx < lines.length; idx += 1) {
90 final Matcher matcher = JavadocThrowsCheck.TAG.matcher(lines[idx]);
91 if (!matcher.find()) {
92 continue;
93 }
94 final String type = matcher.group(2);
95 if (!declared.contains(JavadocThrowsCheck.simple(type))) {
96 this.log(
97 first + idx,
98 "Javadoc ''@{0} {1}'' is not declared in method signature",
99 matcher.group(1),
100 type
101 );
102 }
103 }
104 }
105
106
107
108
109
110
111
112 private static Set<String> declared(final DetailAST ast) {
113 final Set<String> names = new HashSet<>(0);
114 final DetailAST clause = ast.findFirstToken(TokenTypes.LITERAL_THROWS);
115 if (clause != null) {
116 DetailAST child = clause.getFirstChild();
117 while (child != null) {
118 if (child.getType() == TokenTypes.IDENT) {
119 names.add(child.getText());
120 } else if (child.getType() == TokenTypes.DOT) {
121 names.add(JavadocThrowsCheck.rightmost(child));
122 }
123 child = child.getNextSibling();
124 }
125 }
126 return names;
127 }
128
129
130
131
132
133
134
135 private static String simple(final String text) {
136 final int dot = text.lastIndexOf('.');
137 final String result;
138 if (dot < 0) {
139 result = text;
140 } else {
141 result = text.substring(dot + 1);
142 }
143 return result;
144 }
145
146
147
148
149
150
151
152 private static String rightmost(final DetailAST dot) {
153 DetailAST right = dot.getLastChild();
154 while (right.getType() == TokenTypes.DOT) {
155 right = right.getLastChild();
156 }
157 return right.getText();
158 }
159 }