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