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.utils;
21
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.regex.Pattern;
26
27 import javax.annotation.Nullable;
28
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.LineColumn;
33 import com.puppycrawl.tools.checkstyle.api.TextBlock;
34 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
35 import com.puppycrawl.tools.checkstyle.checks.javadoc.InvalidJavadocTag;
36 import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTag;
37 import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTagInfo;
38 import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTags;
39 import com.puppycrawl.tools.checkstyle.checks.javadoc.utils.BlockTagUtil;
40 import com.puppycrawl.tools.checkstyle.checks.javadoc.utils.InlineTagUtil;
41 import com.puppycrawl.tools.checkstyle.checks.javadoc.utils.TagInfo;
42
43
44
45
46 public final class JavadocUtil {
47
48
49
50
51 public enum JavadocTagType {
52
53
54 BLOCK,
55
56 INLINE,
57
58 ALL,
59
60 }
61
62
63 private static final Map<String, Integer> TOKEN_NAME_TO_VALUE;
64
65 private static final Map<Integer, String> TOKEN_VALUE_TO_NAME;
66
67
68 private static final String UNKNOWN_JAVADOC_TOKEN_ID_EXCEPTION_MESSAGE = "Unknown javadoc"
69 + " token id. Given id: ";
70
71
72 private static final Pattern NEWLINE = Pattern.compile("\n");
73
74
75 private static final Pattern RETURN = Pattern.compile("\r");
76
77
78 private static final Pattern TAB = Pattern.compile("\t");
79
80
81 static {
82 TOKEN_NAME_TO_VALUE =
83 TokenUtil.nameToValueMapFromPublicIntFields(JavadocCommentsTokenTypes.class);
84 TOKEN_VALUE_TO_NAME = TokenUtil.invertMap(TOKEN_NAME_TO_VALUE);
85 }
86
87
88 private JavadocUtil() {
89 }
90
91
92
93
94
95
96
97
98
99
100 public static JavadocTags getJavadocTags(TextBlock textBlock,
101 JavadocTagType tagType) {
102 final String[] text = textBlock.getText();
103 final List<TagInfo> tags = new ArrayList<>();
104 final boolean isBlockTags = tagType == JavadocTagType.ALL
105 || tagType == JavadocTagType.BLOCK;
106 if (isBlockTags) {
107 tags.addAll(BlockTagUtil.extractBlockTags(text));
108 }
109 final boolean isInlineTags = tagType == JavadocTagType.ALL
110 || tagType == JavadocTagType.INLINE;
111 if (isInlineTags) {
112 tags.addAll(InlineTagUtil.extractInlineTags(text));
113 }
114
115 final List<JavadocTag> validTags = new ArrayList<>();
116 final List<InvalidJavadocTag> invalidTags = new ArrayList<>();
117
118 for (TagInfo tag : tags) {
119 final LineColumn position = tag.getPosition();
120 final int col = position.getColumn();
121
122
123
124 final int line = textBlock.getStartLineNo() + position.getLine() - 1;
125
126 final String tagName = tag.getName();
127 if (JavadocTagInfo.isValidName(tagName)) {
128 validTags.add(
129 new JavadocTag(line, col, tagName, tag.getValue()));
130 }
131 else {
132 invalidTags.add(new InvalidJavadocTag(line, col, tagName));
133 }
134 }
135
136 return new JavadocTags(validTags, invalidTags);
137 }
138
139
140
141
142
143
144
145
146
147 public static boolean isJavadocComment(String commentContent) {
148 boolean result = false;
149
150 if (!commentContent.isEmpty()) {
151 final char docCommentIdentifier = commentContent.charAt(0);
152 result = docCommentIdentifier == '*';
153 }
154
155 return result;
156 }
157
158
159
160
161
162
163
164
165
166 public static boolean isJavadocComment(DetailAST blockCommentBegin) {
167 final String commentContent = getBlockCommentContent(blockCommentBegin);
168 return isJavadocComment(commentContent) && isCorrectJavadocPosition(blockCommentBegin);
169 }
170
171
172
173
174
175
176
177
178 public static String getBlockCommentContent(DetailAST blockCommentBegin) {
179 final DetailAST commentContent = blockCommentBegin.getFirstChild();
180 return commentContent.getText();
181 }
182
183
184
185
186
187
188
189
190 public static String getJavadocCommentContent(DetailAST javadocCommentBegin) {
191 final DetailAST commentContent = javadocCommentBegin.getFirstChild();
192 return commentContent.getText().substring(1);
193 }
194
195
196
197
198
199
200
201 @Nullable
202 public static DetailAST getAttachedJavadocComment(final DetailAST ast) {
203 DetailAST result = null;
204 DetailAST child = ast.getFirstChild();
205 while (result == null && child != null && !isDeclarationBody(child)) {
206 result = findJavadocComment(child);
207 child = child.getNextSibling();
208 }
209 return result;
210 }
211
212
213
214
215
216
217
218 @Nullable
219 private static DetailAST findJavadocComment(DetailAST ast) {
220 DetailAST result = null;
221 if (ast.getType() == TokenTypes.BLOCK_COMMENT_BEGIN && isJavadocComment(ast)) {
222 result = ast;
223 }
224 else {
225 DetailAST child = ast.getFirstChild();
226 while (result == null && child != null) {
227 result = findJavadocComment(child);
228 child = child.getNextSibling();
229 }
230 }
231 return result;
232 }
233
234
235
236
237
238
239
240 private static boolean isDeclarationBody(DetailAST ast) {
241 final int tokenType = ast.getType();
242 return tokenType == TokenTypes.SLIST;
243 }
244
245
246
247
248
249
250
251
252
253
254 public static DetailNode findFirstToken(DetailNode detailNode, int type) {
255 DetailNode returnValue = null;
256 DetailNode node = detailNode.getFirstChild();
257 while (node != null) {
258 if (node.getType() == type) {
259 returnValue = node;
260 break;
261 }
262 node = node.getNextSibling();
263 }
264 return returnValue;
265 }
266
267
268
269
270
271
272
273
274 public static List<DetailNode> getAllNodesOfType(DetailNode detailNode, int type) {
275 final List<DetailNode> nodes = new ArrayList<>();
276 DetailNode node = detailNode.getFirstChild();
277 while (node != null) {
278 if (node.getType() == type) {
279 nodes.add(node);
280 }
281 node = node.getNextSibling();
282 }
283 return nodes;
284 }
285
286
287
288
289
290
291
292
293
294
295 public static boolean isTag(DetailNode ast, String expectedTagName) {
296 final DetailNode htmlTagStart = findFirstToken(ast,
297 JavadocCommentsTokenTypes.HTML_TAG_START);
298 boolean isTag = false;
299 if (htmlTagStart != null) {
300 final String tagName = findFirstToken(htmlTagStart,
301 JavadocCommentsTokenTypes.TAG_NAME).getText();
302 isTag = expectedTagName.equalsIgnoreCase(tagName);
303 }
304 return isTag;
305 }
306
307
308
309
310
311
312
313
314 public static DetailNode getNextSibling(DetailNode node, int tokenType) {
315 DetailNode nextSibling = node.getNextSibling();
316 while (nextSibling != null && nextSibling.getType() != tokenType) {
317 nextSibling = nextSibling.getNextSibling();
318 }
319 return nextSibling;
320 }
321
322
323
324
325
326
327
328
329
330 public static String getTokenName(int id) {
331 final String name = TOKEN_VALUE_TO_NAME.get(id);
332 if (name == null) {
333 throw new IllegalArgumentException(UNKNOWN_JAVADOC_TOKEN_ID_EXCEPTION_MESSAGE + id);
334 }
335 return name;
336 }
337
338
339
340
341
342
343
344
345
346 public static int getTokenId(String name) {
347 final Integer id = TOKEN_NAME_TO_VALUE.get(name);
348 if (id == null) {
349 throw new IllegalArgumentException("Unknown javadoc token name. Given name " + name);
350 }
351 return id;
352 }
353
354
355
356
357
358
359
360
361
362 public static String getTagName(DetailNode javadocTagSection) {
363 return findFirstToken(javadocTagSection.getFirstChild(),
364 JavadocCommentsTokenTypes.TAG_NAME).getText();
365 }
366
367
368
369
370
371
372
373 public static String escapeAllControlChars(String text) {
374 final String textWithoutNewlines = NEWLINE.matcher(text).replaceAll("\\\\n");
375 final String textWithoutReturns = RETURN.matcher(textWithoutNewlines).replaceAll("\\\\r");
376 return TAB.matcher(textWithoutReturns).replaceAll("\\\\t");
377 }
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396 public static boolean isCorrectJavadocPosition(DetailAST blockComment) {
397
398 DetailAST sibling = blockComment.getNextSibling();
399 while (sibling != null) {
400 if (sibling.getType() == TokenTypes.BLOCK_COMMENT_BEGIN) {
401 if (isJavadocComment(getBlockCommentContent(sibling))) {
402
403 break;
404 }
405 sibling = sibling.getNextSibling();
406 }
407 else if (sibling.getType() == TokenTypes.SINGLE_LINE_COMMENT) {
408 sibling = sibling.getNextSibling();
409 }
410 else {
411
412 sibling = null;
413 }
414 }
415 return sibling == null
416 && (BlockCommentPosition.isOnType(blockComment)
417 || BlockCommentPosition.isOnMember(blockComment)
418 || BlockCommentPosition.isOnPackage(blockComment));
419 }
420
421 }