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 @FileStatefulCheck
81 public class IllegalInstantiationCheck
82 extends AbstractCheck {
83
84
85
86
87
88 public static final String MSG_KEY = "instantiation.avoid";
89
90
91 private static final String JAVA_LANG = "java.lang.";
92
93
94 private final Set<FullIdent> imports = new HashSet<>();
95
96
97 private final Set<String> classNames = new HashSet<>();
98
99
100 private final Set<DetailAST> instantiations = new HashSet<>();
101
102
103 private Set<String> classes = new HashSet<>();
104
105
106 private String pkgName;
107
108 @Override
109 public int[] getDefaultTokens() {
110 return getAcceptableTokens();
111 }
112
113 @Override
114 public int[] getAcceptableTokens() {
115 return new int[] {
116 TokenTypes.IMPORT,
117 TokenTypes.LITERAL_NEW,
118 TokenTypes.PACKAGE_DEF,
119 TokenTypes.CLASS_DEF,
120 };
121 }
122
123 @Override
124 public int[] getRequiredTokens() {
125 return new int[] {
126 TokenTypes.IMPORT,
127 TokenTypes.LITERAL_NEW,
128 TokenTypes.PACKAGE_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 private void processImport(DetailAST ast) {
182 final FullIdent name = FullIdent.createFullIdentBelow(ast);
183
184
185 imports.add(name);
186 }
187
188
189
190
191
192 private void processPackageDef(DetailAST ast) {
193 final DetailAST packageNameAST = ast.getLastChild()
194 .getPreviousSibling();
195 final FullIdent packageIdent =
196 FullIdent.createFullIdent(packageNameAST);
197 pkgName = packageIdent.getText();
198 }
199
200
201
202
203
204 private void processLiteralNew(DetailAST ast) {
205 if (ast.getParent().getType() != TokenTypes.METHOD_REF) {
206 instantiations.add(ast);
207 }
208 }
209
210
211
212
213
214
215 private void postProcessLiteralNew(DetailAST newTokenAst) {
216 final DetailAST typeNameAst = newTokenAst.getFirstChild();
217 final DetailAST nameSibling = typeNameAst.getNextSibling();
218 if (nameSibling.getType() != TokenTypes.ARRAY_DECLARATOR) {
219
220 final FullIdent typeIdent = FullIdent.createFullIdent(typeNameAst);
221 final String typeName = typeIdent.getText();
222 final String fqClassName = getIllegalInstantiation(typeName);
223 if (fqClassName != null) {
224 log(newTokenAst, MSG_KEY, fqClassName);
225 }
226 }
227 }
228
229
230
231
232
233
234
235 private String getIllegalInstantiation(String className) {
236 String fullClassName = null;
237
238 if (classes.contains(className)) {
239 fullClassName = className;
240 }
241 else {
242 final int pkgNameLen;
243
244 if (pkgName == null) {
245 pkgNameLen = 0;
246 }
247 else {
248 pkgNameLen = pkgName.length();
249 }
250
251 for (String illegal : classes) {
252 if (isSamePackage(className, pkgNameLen, illegal)
253 || isStandardClass(className, illegal)) {
254 fullClassName = illegal;
255 }
256 else {
257 fullClassName = checkImportStatements(className);
258 }
259
260 if (fullClassName != null) {
261 break;
262 }
263 }
264 }
265 return fullClassName;
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 private boolean isSamePackage(String className, int pkgNameLen, String illegal) {
299
300
301
302
303
304
305
306 return pkgName != null
307 && className.length() == illegal.length() - pkgNameLen - 1
308 && illegal.charAt(pkgNameLen) == '.'
309 && illegal.endsWith(className)
310 && illegal.startsWith(pkgName);
311 }
312
313
314
315
316
317
318
319 private boolean isStandardClass(String className, String illegal) {
320 boolean isStandardClass = false;
321
322 if (illegal.length() - JAVA_LANG.length() == className.length()
323 && illegal.endsWith(className)
324 && illegal.startsWith(JAVA_LANG)) {
325
326
327
328
329
330
331 final boolean isSameFile = classNames.contains(className);
332
333 if (!isSameFile) {
334 isStandardClass = true;
335 }
336 }
337 return isStandardClass;
338 }
339
340
341
342
343
344 public void setClasses(String... names) {
345 classes = Arrays.stream(names).collect(Collectors.toSet());
346 }
347
348 }