1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package com.puppycrawl.tools.checkstyle.checks.coding;
20
21 import antlr.collections.AST;
22 import com.google.common.collect.Sets;
23 import com.puppycrawl.tools.checkstyle.api.Check;
24 import com.puppycrawl.tools.checkstyle.api.DetailAST;
25 import com.puppycrawl.tools.checkstyle.api.FullIdent;
26 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
27 import com.puppycrawl.tools.checkstyle.api.Utils;
28 import java.util.Set;
29 import java.util.StringTokenizer;
30
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 public class IllegalInstantiationCheck
63 extends Check
64 {
65
66
67
68
69
70 public static final String MSG_KEY = "instantiation.avoid";
71
72
73 private final Set<String> illegalClasses = Sets.newHashSet();
74
75
76 private String pkgName;
77
78
79 private final Set<FullIdent> imports = Sets.newHashSet();
80
81
82 private final Set<String> classNames = Sets.newHashSet();
83
84
85 private final Set<DetailAST> instantiations = Sets.newHashSet();
86
87 @Override
88 public int[] getDefaultTokens()
89 {
90 return new int[] {
91 TokenTypes.IMPORT,
92 TokenTypes.LITERAL_NEW,
93 TokenTypes.PACKAGE_DEF,
94 TokenTypes.CLASS_DEF,
95 };
96 }
97
98 @Override
99 public int[] getAcceptableTokens()
100 {
101
102 return new int[] {};
103 }
104
105 @Override
106 public int[] getRequiredTokens()
107 {
108 return new int[] {
109 TokenTypes.IMPORT,
110 TokenTypes.LITERAL_NEW,
111 TokenTypes.PACKAGE_DEF,
112 };
113 }
114
115 @Override
116 public void beginTree(DetailAST rootAST)
117 {
118 super.beginTree(rootAST);
119 pkgName = null;
120 imports.clear();
121 instantiations.clear();
122 classNames.clear();
123 }
124
125 @Override
126 public void visitToken(DetailAST ast)
127 {
128 switch (ast.getType()) {
129 case TokenTypes.LITERAL_NEW:
130 processLiteralNew(ast);
131 break;
132 case TokenTypes.PACKAGE_DEF:
133 processPackageDef(ast);
134 break;
135 case TokenTypes.IMPORT:
136 processImport(ast);
137 break;
138 case TokenTypes.CLASS_DEF:
139 processClassDef(ast);
140 break;
141 default:
142 throw new IllegalArgumentException("Unknown type " + ast);
143 }
144 }
145
146 @Override
147 public void finishTree(DetailAST rootAST)
148 {
149 for (DetailAST literalNewAST : instantiations) {
150 postprocessLiteralNew(literalNewAST);
151 }
152 }
153
154
155
156
157
158
159
160 private void processClassDef(DetailAST ast)
161 {
162 final DetailAST identToken = ast.findFirstToken(TokenTypes.IDENT);
163 final String className = identToken.getText();
164 classNames.add(className);
165 }
166
167
168
169
170
171 private void processImport(DetailAST ast)
172 {
173 final FullIdent name = FullIdent.createFullIdentBelow(ast);
174 if (name != null) {
175
176
177 imports.add(name);
178 }
179 }
180
181
182
183
184
185 private void processPackageDef(DetailAST ast)
186 {
187 final DetailAST packageNameAST = ast.getLastChild()
188 .getPreviousSibling();
189 final FullIdent packageIdent =
190 FullIdent.createFullIdent(packageNameAST);
191 pkgName = packageIdent.getText();
192 }
193
194
195
196
197
198 private void processLiteralNew(DetailAST ast)
199 {
200 if (ast.getParent().getType() == TokenTypes.METHOD_REF) {
201 return;
202 }
203 instantiations.add(ast);
204 }
205
206
207
208
209
210
211 private void postprocessLiteralNew(DetailAST ast)
212 {
213 final DetailAST typeNameAST = ast.getFirstChild();
214 final AST nameSibling = typeNameAST.getNextSibling();
215 if (nameSibling != null
216 && nameSibling.getType() == TokenTypes.ARRAY_DECLARATOR)
217 {
218
219 return;
220 }
221
222 final FullIdent typeIdent = FullIdent.createFullIdent(typeNameAST);
223 final String typeName = typeIdent.getText();
224 final int lineNo = ast.getLineNo();
225 final int colNo = ast.getColumnNo();
226 final String fqClassName = getIllegalInstantiation(typeName);
227 if (fqClassName != null) {
228 log(lineNo, colNo, MSG_KEY, fqClassName);
229 }
230 }
231
232
233
234
235
236
237
238 private String getIllegalInstantiation(String className)
239 {
240 final String javlang = "java.lang.";
241
242 if (illegalClasses.contains(className)) {
243 return className;
244 }
245
246 final int clsNameLen = className.length();
247 final int pkgNameLen = pkgName == null ? 0 : pkgName.length();
248
249 for (String illegal : illegalClasses) {
250 final int illegalLen = illegal.length();
251
252
253 if (illegalLen - javlang.length() == clsNameLen
254 && illegal.endsWith(className)
255 && illegal.startsWith(javlang))
256 {
257
258
259
260
261
262
263 final boolean isSameFile = classNames.contains(className);
264
265 boolean isSamePackage = false;
266 try {
267 final ClassLoader classLoader = getClassLoader();
268 if (classLoader != null) {
269 final String fqName = pkgName + "." + className;
270 classLoader.loadClass(fqName);
271
272 isSamePackage = true;
273 }
274 }
275 catch (final ClassNotFoundException ex) {
276
277 isSamePackage = false;
278 }
279
280 if (!(isSameFile || isSamePackage)) {
281 return illegal;
282 }
283 }
284
285
286
287
288
289
290
291
292 if (pkgName != null
293 && clsNameLen == illegalLen - pkgNameLen - 1
294 && illegal.charAt(pkgNameLen) == '.'
295 && illegal.endsWith(className)
296 && illegal.startsWith(pkgName))
297 {
298 return illegal;
299 }
300
301 for (FullIdent importLineText : imports) {
302 final String importArg = importLineText.getText();
303 if (importArg.endsWith(".*")) {
304 final String fqClass =
305 importArg.substring(0, importArg.length() - 1)
306 + className;
307
308
309 if (illegalClasses.contains(fqClass)) {
310 return fqClass;
311 }
312 }
313 else {
314 if (Utils.baseClassname(importArg).equals(className)
315 && illegalClasses.contains(importArg))
316 {
317 return importArg;
318 }
319 }
320 }
321 }
322 return null;
323 }
324
325
326
327
328
329 public void setClasses(String classNames)
330 {
331 illegalClasses.clear();
332 final StringTokenizer tok = new StringTokenizer(classNames, ",");
333 while (tok.hasMoreTokens()) {
334 illegalClasses.add(tok.nextToken());
335 }
336 }
337 }