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