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;
21
22 import java.util.Optional;
23 import java.util.Set;
24 import java.util.regex.Pattern;
25
26 import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
27 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
28 import com.puppycrawl.tools.checkstyle.api.DetailAST;
29 import com.puppycrawl.tools.checkstyle.api.FullIdent;
30 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 @FileStatefulCheck
66 public class UncommentedMainCheck
67 extends AbstractCheck {
68
69
70
71
72
73 public static final String MSG_KEY = "uncommented.main";
74
75
76 private static final Set<String> STRING_PARAMETER_NAMES = Set.of(
77 String[].class.getCanonicalName(),
78 String.class.getCanonicalName(),
79 String[].class.getSimpleName(),
80 String.class.getSimpleName()
81 );
82
83
84
85
86
87 private Pattern excludedClasses = Pattern.compile("^$");
88
89 private String currentClass;
90
91 private FullIdent packageName;
92
93 private int classDepth;
94
95
96
97
98
99
100
101
102 public void setExcludedClasses(Pattern excludedClasses) {
103 this.excludedClasses = excludedClasses;
104 }
105
106 @Override
107 public int[] getAcceptableTokens() {
108 return getRequiredTokens();
109 }
110
111 @Override
112 public int[] getDefaultTokens() {
113 return getRequiredTokens();
114 }
115
116 @Override
117 public int[] getRequiredTokens() {
118 return new int[] {
119 TokenTypes.METHOD_DEF,
120 TokenTypes.CLASS_DEF,
121 TokenTypes.PACKAGE_DEF,
122 TokenTypes.RECORD_DEF,
123 };
124 }
125
126 @Override
127 public void beginTree(DetailAST rootAST) {
128 packageName = FullIdent.createFullIdent(null);
129 classDepth = 0;
130 }
131
132 @Override
133 public void leaveToken(DetailAST ast) {
134 if (ast.getType() == TokenTypes.CLASS_DEF) {
135 classDepth--;
136 }
137 }
138
139 @Override
140 public void visitToken(DetailAST ast) {
141 switch (ast.getType()) {
142 case TokenTypes.PACKAGE_DEF:
143 visitPackageDef(ast);
144 break;
145 case TokenTypes.RECORD_DEF:
146 case TokenTypes.CLASS_DEF:
147 visitClassOrRecordDef(ast);
148 break;
149 case TokenTypes.METHOD_DEF:
150 visitMethodDef(ast);
151 break;
152 default:
153 throw new IllegalStateException(ast.toString());
154 }
155 }
156
157
158
159
160
161
162 private void visitPackageDef(DetailAST packageDef) {
163 packageName = FullIdent.createFullIdent(packageDef.getLastChild()
164 .getPreviousSibling());
165 }
166
167
168
169
170
171
172 private void visitClassOrRecordDef(DetailAST classOrRecordDef) {
173
174
175 if (classDepth == 0) {
176 final DetailAST ident = classOrRecordDef.findFirstToken(TokenTypes.IDENT);
177 currentClass = packageName.getText() + "." + ident.getText();
178 classDepth++;
179 }
180 }
181
182
183
184
185
186
187
188 private void visitMethodDef(DetailAST method) {
189 if (classDepth == 1
190
191 && checkClassName()
192 && checkName(method)
193 && checkModifiers(method)
194 && checkType(method)
195 && checkParams(method)) {
196 log(method, MSG_KEY);
197 }
198 }
199
200
201
202
203
204
205 private boolean checkClassName() {
206 return !excludedClasses.matcher(currentClass).find();
207 }
208
209
210
211
212
213
214
215 private static boolean checkName(DetailAST method) {
216 final DetailAST ident = method.findFirstToken(TokenTypes.IDENT);
217 return "main".equals(ident.getText());
218 }
219
220
221
222
223
224
225
226 private static boolean checkModifiers(DetailAST method) {
227 final DetailAST modifiers =
228 method.findFirstToken(TokenTypes.MODIFIERS);
229
230 return modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) != null
231 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
232 }
233
234
235
236
237
238
239
240 private static boolean checkType(DetailAST method) {
241 final DetailAST type =
242 method.findFirstToken(TokenTypes.TYPE).getFirstChild();
243 return type.getType() == TokenTypes.LITERAL_VOID;
244 }
245
246
247
248
249
250
251
252 private static boolean checkParams(DetailAST method) {
253 boolean checkPassed = false;
254 final DetailAST params = method.findFirstToken(TokenTypes.PARAMETERS);
255
256 if (params.getChildCount() == 1) {
257 final DetailAST parameterType = params.getFirstChild().findFirstToken(TokenTypes.TYPE);
258 final boolean isArrayDeclaration =
259 parameterType.findFirstToken(TokenTypes.ARRAY_DECLARATOR) != null;
260 final Optional<DetailAST> varargs = Optional.ofNullable(
261 params.getFirstChild().findFirstToken(TokenTypes.ELLIPSIS));
262
263 if (isArrayDeclaration || varargs.isPresent()) {
264 checkPassed = isStringType(parameterType.getFirstChild());
265 }
266 }
267 return checkPassed;
268 }
269
270
271
272
273
274
275
276 private static boolean isStringType(DetailAST typeAst) {
277 final FullIdent type = FullIdent.createFullIdent(typeAst);
278 return STRING_PARAMETER_NAMES.contains(type.getText());
279 }
280
281 }