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