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.TokenTypes;
11 import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
12 import java.util.regex.Pattern;
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 public final class NonStaticMethodCheck extends AbstractCheck {
32
33
34
35
36 private static final Pattern EMPTY = Pattern.compile("^$");
37
38
39
40
41
42 private Pattern exclude;
43
44
45
46
47 public NonStaticMethodCheck() {
48 this.exclude = NonStaticMethodCheck.EMPTY;
49 }
50
51
52
53
54
55
56 public void setExcludeFileNamePattern(final String excl) {
57 this.exclude = Pattern.compile(excl);
58 }
59
60 @Override
61 public int[] getDefaultTokens() {
62 return new int[] {
63 TokenTypes.METHOD_DEF,
64 };
65 }
66
67 @Override
68 public int[] getAcceptableTokens() {
69 return this.getDefaultTokens();
70 }
71
72 @Override
73 public int[] getRequiredTokens() {
74 return this.getDefaultTokens();
75 }
76
77 @Override
78 @SuppressWarnings("deprecation")
79 public void visitToken(final DetailAST ast) {
80 if (this.exclude.matcher(this.getFileContents().getFileName())
81 .find()) {
82 return;
83 }
84 if (TokenTypes.CLASS_DEF == ast.getParent().getParent().getType()) {
85 this.checkClassMethod(ast);
86 }
87 }
88
89 private void checkClassMethod(final DetailAST method) {
90 final DetailAST modifiers = method
91 .findFirstToken(TokenTypes.MODIFIERS);
92 if (modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) != null) {
93 return;
94 }
95 final BranchContains checker = new BranchContains(method);
96 final boolean onlythrow =
97 checker.check(TokenTypes.LITERAL_THROW)
98 && !checker.check(TokenTypes.LCURLY)
99 && this.countSemiColons(method) == 1;
100 final boolean skip = AnnotationUtil.containsAnnotation(method, "Override")
101 || isInAbstractOrNativeMethod(method)
102 || onlythrow;
103 if (!skip
104 && !checker.check(TokenTypes.LITERAL_THIS)
105 && !checker.check(TokenTypes.LITERAL_SUPER)) {
106 this.log(
107 method.getLineNo(),
108 "This method must be static, because it does not refer to \"this\""
109 );
110 }
111 }
112
113 private static boolean isInAbstractOrNativeMethod(final DetailAST method) {
114 final BranchContains checker = new BranchContains(
115 method.findFirstToken(TokenTypes.MODIFIERS)
116 );
117 return checker.check(TokenTypes.ABSTRACT)
118 || checker.check(TokenTypes.LITERAL_NATIVE);
119 }
120
121 @SuppressWarnings("deprecation")
122 private int countSemiColons(final DetailAST method) {
123 final DetailAST openingbrace = method.findFirstToken(TokenTypes.SLIST);
124 int count = 0;
125 if (openingbrace != null) {
126 final DetailAST closingbrace =
127 openingbrace.findFirstToken(TokenTypes.RCURLY);
128 final int lastline = closingbrace.getLineNo();
129 final int firstline = openingbrace.getLineNo();
130 final FileContents contents = this.getFileContents();
131 for (int line = firstline - 1; line < lastline; line += 1) {
132 if (!contents.lineIsBlank(line)
133 && !contents.lineIsComment(line)
134 && contents.getLine(line).contains(";")) {
135 count += 1;
136 }
137 }
138 }
139 return count;
140 }
141 }