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.coding;
21
22 import java.util.Arrays;
23 import java.util.HashSet;
24 import java.util.Set;
25 import java.util.stream.Collectors;
26
27 import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
28 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
29 import com.puppycrawl.tools.checkstyle.api.DetailAST;
30 import com.puppycrawl.tools.checkstyle.api.FullIdent;
31 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
32 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84 @FileStatefulCheck
85 public class IllegalInstantiationCheck
86 extends AbstractCheck {
87
88
89
90
91
92 public static final String MSG_KEY = "instantiation.avoid";
93
94
95 private static final String JAVA_LANG = "java.lang.";
96
97
98 private final Set<FullIdent> imports = new HashSet<>();
99
100
101 private final Set<String> classNames = new HashSet<>();
102
103
104 private final Set<DetailAST> instantiations = new HashSet<>();
105
106
107 private Set<String> classes = new HashSet<>();
108
109
110 private String pkgName;
111
112 @Override
113 public int[] getDefaultTokens() {
114 return getRequiredTokens();
115 }
116
117 @Override
118 public int[] getAcceptableTokens() {
119 return getRequiredTokens();
120 }
121
122 @Override
123 public int[] getRequiredTokens() {
124 return new int[] {
125 TokenTypes.IMPORT,
126 TokenTypes.LITERAL_NEW,
127 TokenTypes.PACKAGE_DEF,
128 TokenTypes.CLASS_DEF,
129 };
130 }
131
132 @Override
133 public void beginTree(DetailAST rootAST) {
134 pkgName = null;
135 imports.clear();
136 instantiations.clear();
137 classNames.clear();
138 }
139
140 @Override
141 public void visitToken(DetailAST ast) {
142 switch (ast.getType()) {
143 case TokenTypes.LITERAL_NEW:
144 processLiteralNew(ast);
145 break;
146 case TokenTypes.PACKAGE_DEF:
147 processPackageDef(ast);
148 break;
149 case TokenTypes.IMPORT:
150 processImport(ast);
151 break;
152 case TokenTypes.CLASS_DEF:
153 processClassDef(ast);
154 break;
155 default:
156 throw new IllegalArgumentException("Unknown type " + ast);
157 }
158 }
159
160 @Override
161 public void finishTree(DetailAST rootAST) {
162 instantiations.forEach(this::postProcessLiteralNew);
163 }
164
165
166
167
168
169
170
171 private void processClassDef(DetailAST ast) {
172 final DetailAST identToken = ast.findFirstToken(TokenTypes.IDENT);
173 final String className = identToken.getText();
174 classNames.add(className);
175 }
176
177
178
179
180
181
182 private void processImport(DetailAST ast) {
183 final FullIdent name = FullIdent.createFullIdentBelow(ast);
184
185
186 imports.add(name);
187 }
188
189
190
191
192
193
194 private void processPackageDef(DetailAST ast) {
195 final DetailAST packageNameAST = ast.getLastChild()
196 .getPreviousSibling();
197 final FullIdent packageIdent =
198 FullIdent.createFullIdent(packageNameAST);
199 pkgName = packageIdent.getText();
200 }
201
202
203
204
205
206
207 private void processLiteralNew(DetailAST ast) {
208 if (ast.getParent().getType() != TokenTypes.METHOD_REF) {
209 instantiations.add(ast);
210 }
211 }
212
213
214
215
216
217
218
219 private void postProcessLiteralNew(DetailAST newTokenAst) {
220 final DetailAST typeNameAst = newTokenAst.getFirstChild();
221 final DetailAST nameSibling = typeNameAst.getNextSibling();
222 if (nameSibling.getType() != TokenTypes.ARRAY_DECLARATOR) {
223
224 final FullIdent typeIdent = FullIdent.createFullIdent(typeNameAst);
225 final String typeName = typeIdent.getText();
226 final String fqClassName = getIllegalInstantiation(typeName);
227 if (fqClassName != null) {
228 log(newTokenAst, MSG_KEY, fqClassName);
229 }
230 }
231 }
232
233
234
235
236
237
238
239
240 private String getIllegalInstantiation(String className) {
241 String fullClassName = null;
242
243 if (classes.contains(className)) {
244 fullClassName = className;
245 }
246 else {
247 final int pkgNameLen;
248
249 if (pkgName == null) {
250 pkgNameLen = 0;
251 }
252 else {
253 pkgNameLen = pkgName.length();
254 }
255
256 for (String illegal : classes) {
257 if (isSamePackage(className, pkgNameLen, illegal)
258 || isStandardClass(className, illegal)) {
259 fullClassName = illegal;
260 }
261 else {
262 fullClassName = checkImportStatements(className);
263 }
264
265 if (fullClassName != null) {
266 break;
267 }
268 }
269 }
270 return fullClassName;
271 }
272
273
274
275
276
277
278
279 private String checkImportStatements(String className) {
280 String illegalType = null;
281
282 for (FullIdent importLineText : imports) {
283 String importArg = importLineText.getText();
284 if (importArg.endsWith(".*")) {
285 importArg = importArg.substring(0, importArg.length() - 1)
286 + className;
287 }
288 if (CommonUtil.baseClassName(importArg).equals(className)
289 && classes.contains(importArg)) {
290 illegalType = importArg;
291 break;
292 }
293 }
294 return illegalType;
295 }
296
297
298
299
300
301
302
303
304
305 private boolean isSamePackage(String className, int pkgNameLen, String illegal) {
306
307
308
309
310
311
312
313 return pkgName != null
314 && className.length() == illegal.length() - pkgNameLen - 1
315 && illegal.charAt(pkgNameLen) == '.'
316 && illegal.endsWith(className)
317 && illegal.startsWith(pkgName);
318 }
319
320
321
322
323
324
325
326
327 private boolean isStandardClass(String className, String illegal) {
328 boolean isStandardClass = false;
329
330 if (illegal.length() - JAVA_LANG.length() == className.length()
331 && illegal.endsWith(className)
332 && illegal.startsWith(JAVA_LANG)) {
333
334
335
336
337
338
339 final boolean isSameFile = classNames.contains(className);
340
341 if (!isSameFile) {
342 isStandardClass = true;
343 }
344 }
345 return isStandardClass;
346 }
347
348
349
350
351
352
353
354 public void setClasses(String... names) {
355 classes = Arrays.stream(names).collect(Collectors.toUnmodifiableSet());
356 }
357
358 }