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.ArrayDeque;
23 import java.util.Deque;
24 import java.util.regex.Pattern;
25
26 import javax.annotation.Nullable;
27
28 import com.puppycrawl.tools.checkstyle.StatelessCheck;
29 import com.puppycrawl.tools.checkstyle.api.DetailAST;
30 import com.puppycrawl.tools.checkstyle.api.DetailNode;
31 import com.puppycrawl.tools.checkstyle.api.JavadocCommentsTokenTypes;
32 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
33 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
34 import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
35
36
37
38
39
40
41
42
43
44
45
46
47
48 @StatelessCheck
49 public class IllegalBlockTagCheck extends AbstractJavadocCheck {
50
51
52
53
54
55 public static final String MSG_ILLEGAL_PATTERN = "illegalblocktag.illegalPattern";
56
57
58 private static final Pattern MATCH_NOTHING = CommonUtil.createPattern("^$");
59
60
61 private Pattern tagTextPattern = MATCH_NOTHING;
62
63
64 @Nullable
65 private String tag;
66
67
68
69
70 public IllegalBlockTagCheck() {
71
72 }
73
74
75
76
77
78
79
80 public void setTag(String tag) {
81 this.tag = tag;
82 }
83
84
85
86
87
88
89
90
91 public void setTagTextPattern(Pattern pattern) {
92 tagTextPattern = pattern;
93 }
94
95
96
97
98
99
100
101
102
103
104
105 @Override
106 public void setViolateExecutionOnNonTightHtml(boolean shouldReportViolation) {
107 super.setViolateExecutionOnNonTightHtml(shouldReportViolation);
108 }
109
110 @Override
111 public int[] getRequiredTokens() {
112 return CommonUtil.EMPTY_INT_ARRAY;
113 }
114
115 @Override
116 public int[] getDefaultTokens() {
117 return getAcceptableTokens();
118 }
119
120 @Override
121 public int[] getAcceptableTokens() {
122 return new int[] {
123 TokenTypes.INTERFACE_DEF,
124 TokenTypes.CLASS_DEF,
125 TokenTypes.ENUM_DEF,
126 TokenTypes.ANNOTATION_DEF,
127 TokenTypes.METHOD_DEF,
128 TokenTypes.CTOR_DEF,
129 TokenTypes.ENUM_CONSTANT_DEF,
130 TokenTypes.ANNOTATION_FIELD_DEF,
131 TokenTypes.RECORD_DEF,
132 TokenTypes.COMPACT_CTOR_DEF,
133 };
134 }
135
136 @Override
137 public int[] getDefaultJavadocTokens() {
138 return new int[] {
139 JavadocCommentsTokenTypes.JAVADOC_BLOCK_TAG,
140 };
141 }
142
143 @Override
144 public int[] getRequiredJavadocTokens() {
145 return getAcceptableJavadocTokens();
146 }
147
148 @Override
149 public void visitToken(DetailAST ast) {
150 final DetailAST javadocComment = findJavadoc(ast);
151 if (javadocComment != null) {
152 super.visitToken(javadocComment);
153 }
154 }
155
156 @Override
157 public void visitJavadocToken(DetailNode ast) {
158 final String tagName = "@" + JavadocUtil.getTagName(ast);
159 if (tagName.equals(tag)) {
160 final String content = getTagContent(ast);
161 if (!tagTextPattern.matcher(content).find()) {
162 log(ast, MSG_ILLEGAL_PATTERN, JavadocUtil.getTagName(ast));
163 }
164 }
165 }
166
167
168
169
170
171
172
173
174 private static String getTagContent(DetailNode javadocBlockTagNode) {
175 final DetailNode tagNodeNextSibling = JavadocUtil.findFirstToken(
176 javadocBlockTagNode.getFirstChild(),
177 JavadocCommentsTokenTypes.TAG_NAME).getNextSibling();
178
179 final int stringBuilderCapacity = 128;
180 final StringBuilder rawTextBuilder = new StringBuilder(stringBuilderCapacity);
181 if (tagNodeNextSibling != null) {
182
183 final Deque<DetailNode> stack = new ArrayDeque<>();
184 stack.push(tagNodeNextSibling);
185
186 while (!stack.isEmpty()) {
187 final DetailNode currentNode = stack.pop();
188
189
190 if (currentNode.getFirstChild() == null) {
191 rawTextBuilder.append(currentNode.getText());
192 }
193
194 final DetailNode nextSibling = currentNode.getNextSibling();
195 final DetailNode firstChild = currentNode.getFirstChild();
196
197 if (nextSibling != null) {
198 stack.push(nextSibling);
199 }
200 if (firstChild != null) {
201 stack.push(firstChild);
202 }
203 }
204 }
205
206 return rawTextBuilder.toString().stripLeading();
207 }
208
209
210
211
212
213
214
215 @Nullable
216 private static DetailAST findJavadoc(DetailAST ast) {
217 DetailAST cmt = ast.findFirstToken(TokenTypes.BLOCK_COMMENT_BEGIN);
218 if (cmt == null) {
219 final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
220 final DetailAST type = ast.findFirstToken(TokenTypes.TYPE);
221
222 if (modifiers != null) {
223 final DetailAST annotation = modifiers.findFirstToken(TokenTypes.ANNOTATION);
224 cmt = modifiers.findFirstToken(TokenTypes.BLOCK_COMMENT_BEGIN);
225 if (annotation != null) {
226 cmt = annotation.findFirstToken(TokenTypes.BLOCK_COMMENT_BEGIN);
227 }
228 }
229 if (cmt == null && type != null) {
230 cmt = type.findFirstToken(TokenTypes.BLOCK_COMMENT_BEGIN);
231 }
232 }
233 return cmt;
234 }
235
236 }