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.io.File;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.List;
27 import java.util.Set;
28 import java.util.function.Predicate;
29 import java.util.regex.Pattern;
30 import java.util.stream.Collectors;
31 import java.util.stream.Stream;
32
33 import com.puppycrawl.tools.checkstyle.api.DetailAST;
34 import com.puppycrawl.tools.checkstyle.api.FullIdent;
35 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
36 import com.puppycrawl.tools.checkstyle.checks.naming.AccessModifierOption;
37
38
39
40
41
42 public final class CheckUtil {
43
44
45
46 private static final int BASE_2 = 2;
47
48
49 private static final int BASE_8 = 8;
50
51
52 private static final int BASE_10 = 10;
53
54
55 private static final int BASE_16 = 16;
56
57
58 private static final Pattern UNDERSCORE_PATTERN = Pattern.compile("_");
59
60
61 private static final Pattern ALL_NEW_LINES = Pattern.compile("\\R");
62
63
64 private static final char PACKAGE_SEPARATOR = '.';
65
66
67 private CheckUtil() {
68 }
69
70
71
72
73
74
75
76
77 public static boolean isEqualsMethod(DetailAST ast) {
78 boolean equalsMethod = false;
79
80 if (ast.getType() == TokenTypes.METHOD_DEF) {
81 final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
82 final boolean staticOrAbstract =
83 modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) != null
84 || modifiers.findFirstToken(TokenTypes.ABSTRACT) != null;
85
86 if (!staticOrAbstract) {
87 final DetailAST nameNode = ast.findFirstToken(TokenTypes.IDENT);
88 final String name = nameNode.getText();
89
90 if ("equals".equals(name)) {
91
92 final DetailAST paramsNode = ast.findFirstToken(TokenTypes.PARAMETERS);
93 equalsMethod = paramsNode.getChildCount() == 1;
94 }
95 }
96 }
97 return equalsMethod;
98 }
99
100
101
102
103
104
105
106
107
108
109 public static double parseDouble(String text, int type) {
110 String txt = UNDERSCORE_PATTERN.matcher(text).replaceAll("");
111 final double result;
112 switch (type) {
113 case TokenTypes.NUM_FLOAT:
114 case TokenTypes.NUM_DOUBLE:
115 result = Double.parseDouble(txt);
116 break;
117 case TokenTypes.NUM_INT:
118 case TokenTypes.NUM_LONG:
119 int radix = BASE_10;
120 if (txt.startsWith("0x") || txt.startsWith("0X")) {
121 radix = BASE_16;
122 txt = txt.substring(2);
123 }
124 else if (txt.startsWith("0b") || txt.startsWith("0B")) {
125 radix = BASE_2;
126 txt = txt.substring(2);
127 }
128 else if (txt.startsWith("0")) {
129 radix = BASE_8;
130 }
131 result = parseNumber(txt, radix, type);
132 break;
133 default:
134 result = Double.NaN;
135 break;
136 }
137 return result;
138 }
139
140
141
142
143
144
145
146
147
148
149
150
151
152 private static double parseNumber(final String text, final int radix, final int type) {
153 String txt = text;
154 if (txt.endsWith("L") || txt.endsWith("l")) {
155 txt = txt.substring(0, txt.length() - 1);
156 }
157 final double result;
158
159 final boolean negative = txt.charAt(0) == '-';
160 if (type == TokenTypes.NUM_INT) {
161 if (negative) {
162 result = Integer.parseInt(txt, radix);
163 }
164 else {
165 result = Integer.parseUnsignedInt(txt, radix);
166 }
167 }
168 else {
169 if (negative) {
170 result = Long.parseLong(txt, radix);
171 }
172 else {
173 result = Long.parseUnsignedLong(txt, radix);
174 }
175 }
176
177 return result;
178 }
179
180
181
182
183
184
185
186 public static DetailAST getFirstNode(final DetailAST node) {
187 DetailAST currentNode = node;
188 DetailAST child = node.getFirstChild();
189 while (child != null) {
190 final DetailAST newNode = getFirstNode(child);
191 if (isBeforeInSource(newNode, currentNode)) {
192 currentNode = newNode;
193 }
194 child = child.getNextSibling();
195 }
196
197 return currentNode;
198 }
199
200
201
202
203
204
205
206
207 public static boolean isBeforeInSource(DetailAST ast1, DetailAST ast2) {
208 return ast1.getLineNo() < ast2.getLineNo()
209 || TokenUtil.areOnSameLine(ast1, ast2)
210 && ast1.getColumnNo() < ast2.getColumnNo();
211 }
212
213
214
215
216
217
218
219 public static List<String> getTypeParameterNames(final DetailAST node) {
220 final DetailAST typeParameters =
221 node.findFirstToken(TokenTypes.TYPE_PARAMETERS);
222
223 final List<String> typeParameterNames = new ArrayList<>();
224 if (typeParameters != null) {
225 final DetailAST typeParam =
226 typeParameters.findFirstToken(TokenTypes.TYPE_PARAMETER);
227 typeParameterNames.add(
228 typeParam.findFirstToken(TokenTypes.IDENT).getText());
229
230 DetailAST sibling = typeParam.getNextSibling();
231 while (sibling != null) {
232 if (sibling.getType() == TokenTypes.TYPE_PARAMETER) {
233 typeParameterNames.add(
234 sibling.findFirstToken(TokenTypes.IDENT).getText());
235 }
236 sibling = sibling.getNextSibling();
237 }
238 }
239
240 return typeParameterNames;
241 }
242
243
244
245
246
247
248
249 public static List<DetailAST> getTypeParameters(final DetailAST node) {
250 final DetailAST typeParameters =
251 node.findFirstToken(TokenTypes.TYPE_PARAMETERS);
252
253 final List<DetailAST> typeParams = new ArrayList<>();
254 if (typeParameters != null) {
255 final DetailAST typeParam =
256 typeParameters.findFirstToken(TokenTypes.TYPE_PARAMETER);
257 typeParams.add(typeParam);
258
259 DetailAST sibling = typeParam.getNextSibling();
260 while (sibling != null) {
261 if (sibling.getType() == TokenTypes.TYPE_PARAMETER) {
262 typeParams.add(sibling);
263 }
264 sibling = sibling.getNextSibling();
265 }
266 }
267
268 return typeParams;
269 }
270
271
272
273
274
275
276
277 public static boolean isNonVoidMethod(DetailAST methodDefAst) {
278 boolean returnValue = false;
279 if (methodDefAst.getType() == TokenTypes.METHOD_DEF) {
280 final DetailAST typeAST = methodDefAst.findFirstToken(TokenTypes.TYPE);
281 if (typeAST.findFirstToken(TokenTypes.LITERAL_VOID) == null) {
282 returnValue = true;
283 }
284 }
285 return returnValue;
286 }
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304 public static boolean isReceiverParameter(DetailAST parameterDefAst) {
305 return parameterDefAst.findFirstToken(TokenTypes.IDENT) == null;
306 }
307
308
309
310
311
312
313
314
315
316 public static AccessModifierOption getAccessModifierFromModifiersToken(DetailAST ast) {
317 final DetailAST modsToken = ast.findFirstToken(TokenTypes.MODIFIERS);
318 AccessModifierOption accessModifier =
319 getAccessModifierFromModifiersTokenDirectly(modsToken);
320
321 if (accessModifier == AccessModifierOption.PACKAGE) {
322 if (ScopeUtil.isInEnumBlock(ast) && ast.getType() == TokenTypes.CTOR_DEF) {
323 accessModifier = AccessModifierOption.PRIVATE;
324 }
325 else if (ScopeUtil.isInInterfaceOrAnnotationBlock(ast)) {
326 accessModifier = AccessModifierOption.PUBLIC;
327 }
328 }
329
330 return accessModifier;
331 }
332
333
334
335
336
337
338
339
340
341 private static AccessModifierOption getAccessModifierFromModifiersTokenDirectly(
342 DetailAST modifiersToken) {
343 if (modifiersToken == null) {
344 throw new IllegalArgumentException("expected non-null AST-token with type 'MODIFIERS'");
345 }
346
347 AccessModifierOption accessModifier = AccessModifierOption.PACKAGE;
348 for (DetailAST token = modifiersToken.getFirstChild(); token != null;
349 token = token.getNextSibling()) {
350 final int tokenType = token.getType();
351 if (tokenType == TokenTypes.LITERAL_PUBLIC) {
352 accessModifier = AccessModifierOption.PUBLIC;
353 }
354 else if (tokenType == TokenTypes.LITERAL_PROTECTED) {
355 accessModifier = AccessModifierOption.PROTECTED;
356 }
357 else if (tokenType == TokenTypes.LITERAL_PRIVATE) {
358 accessModifier = AccessModifierOption.PRIVATE;
359 }
360 }
361 return accessModifier;
362 }
363
364
365
366
367
368
369
370 public static AccessModifierOption getSurroundingAccessModifier(DetailAST node) {
371 AccessModifierOption returnValue = null;
372 for (DetailAST token = node;
373 returnValue == null && !TokenUtil.isRootNode(token);
374 token = token.getParent()) {
375 final int type = token.getType();
376 if (type == TokenTypes.CLASS_DEF
377 || type == TokenTypes.INTERFACE_DEF
378 || type == TokenTypes.ANNOTATION_DEF
379 || type == TokenTypes.ENUM_DEF) {
380 returnValue = getAccessModifierFromModifiersToken(token);
381 }
382 else if (type == TokenTypes.LITERAL_NEW) {
383 break;
384 }
385 }
386
387 return returnValue;
388 }
389
390
391
392
393
394
395
396 public static Set<String> parseClassNames(String... classNames) {
397 return Arrays.stream(classNames)
398 .flatMap(className -> Stream.of(className, CommonUtil.baseClassName(className)))
399 .filter(Predicate.not(String::isEmpty))
400 .collect(Collectors.toUnmodifiableSet());
401 }
402
403
404
405
406
407
408
409
410
411
412 public static String stripIndentAndInitialNewLineFromTextBlock(String textBlockContent) {
413 final String contentWithInitialNewLineRemoved =
414 ALL_NEW_LINES.matcher(textBlockContent).replaceFirst("");
415 final List<String> lines =
416 Arrays.asList(ALL_NEW_LINES.split(contentWithInitialNewLineRemoved));
417 final int indent = getSmallestIndent(lines);
418 final String suffix = "";
419
420 return lines.stream()
421 .map(line -> stripIndentAndTrailingWhitespaceFromLine(line, indent))
422 .collect(Collectors.joining(System.lineSeparator(), suffix, suffix));
423 }
424
425
426
427
428
429
430
431
432
433 private static String stripIndentAndTrailingWhitespaceFromLine(String line, int indent) {
434 final int lastNonWhitespace = lastIndexOfNonWhitespace(line);
435 String returnString = "";
436 if (lastNonWhitespace > 0) {
437 returnString = line.substring(indent, lastNonWhitespace);
438 }
439 return returnString;
440 }
441
442
443
444
445
446
447
448
449 private static int getSmallestIndent(Collection<String> lines) {
450 return lines.stream()
451 .mapToInt(CommonUtil::indexOfNonWhitespace)
452 .min()
453 .orElse(0);
454 }
455
456
457
458
459
460
461
462 private static int lastIndexOfNonWhitespace(String line) {
463 int length;
464 for (length = line.length(); length > 0; length--) {
465 if (!Character.isWhitespace(line.charAt(length - 1))) {
466 break;
467 }
468 }
469 return length;
470 }
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487 public static int typeDeclarationNameMatchingCount(String patternClass,
488 String classToBeMatched) {
489 final int length = Math.min(classToBeMatched.length(), patternClass.length());
490 int result = 0;
491 for (int i = 0; i < length && patternClass.charAt(i) == classToBeMatched.charAt(i); ++i) {
492 if (patternClass.charAt(i) == PACKAGE_SEPARATOR) {
493 result = i;
494 }
495 }
496 return result;
497 }
498
499
500
501
502
503
504
505
506
507
508
509 public static String getQualifiedTypeDeclarationName(String packageName,
510 String outerClassQualifiedName,
511 String className) {
512 final String qualifiedClassName;
513
514 if (outerClassQualifiedName == null) {
515 if (packageName == null) {
516 qualifiedClassName = className;
517 }
518 else {
519 qualifiedClassName = packageName + PACKAGE_SEPARATOR + className;
520 }
521 }
522 else {
523 qualifiedClassName = outerClassQualifiedName + PACKAGE_SEPARATOR + className;
524 }
525 return qualifiedClassName;
526 }
527
528
529
530
531
532
533
534
535 public static String extractQualifiedName(DetailAST ast) {
536 return FullIdent.createFullIdent(ast).getText();
537 }
538
539
540
541
542
543
544
545
546
547
548
549
550 public static String getShortNameOfAnonInnerClass(DetailAST literalNewAst) {
551 DetailAST parentAst = literalNewAst;
552 while (TokenUtil.isOfType(parentAst, TokenTypes.LITERAL_NEW, TokenTypes.DOT)) {
553 parentAst = parentAst.getParent();
554 }
555 final DetailAST firstChild = parentAst.getFirstChild();
556 return extractQualifiedName(firstChild);
557 }
558
559
560
561
562
563
564
565 public static boolean isPackageInfo(String filePath) {
566 return "package-info.java".equals(new File(filePath).getName());
567 }
568 }