1
2
3
4 package net.sourceforge.pmd.lang.java.symboltable;
5
6 import java.util.ArrayList;
7 import java.util.LinkedHashSet;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Set;
11
12 import net.sourceforge.pmd.lang.ast.Node;
13 import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression;
14 import net.sourceforge.pmd.lang.java.ast.ASTArgumentList;
15 import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral;
16 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration;
17 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
18 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
19 import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
20 import net.sourceforge.pmd.lang.java.ast.ASTExtendsList;
21 import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
22 import net.sourceforge.pmd.lang.java.ast.ASTFormalParameters;
23 import net.sourceforge.pmd.lang.java.ast.ASTImplementsList;
24 import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
25 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
26 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator;
27 import net.sourceforge.pmd.lang.java.ast.ASTName;
28 import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
29 import net.sourceforge.pmd.lang.java.ast.ASTType;
30 import net.sourceforge.pmd.lang.java.ast.ASTTypeParameter;
31 import net.sourceforge.pmd.lang.java.ast.ASTTypeParameters;
32 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
33 import net.sourceforge.pmd.lang.java.ast.JavaParserTreeConstants;
34 import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
35 import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
36 import net.sourceforge.pmd.lang.symboltable.Scope;
37
38
39
40
41
42 public class ClassScope extends AbstractJavaScope {
43
44
45 private static ThreadLocal<Integer> anonymousInnerClassCounter = new ThreadLocal<Integer>() {
46 protected Integer initialValue() {
47 return Integer.valueOf(1);
48 }
49 };
50
51 private String className;
52
53 private boolean isEnum;
54
55 public ClassScope(String className) {
56 this.className = className;
57 anonymousInnerClassCounter.set(Integer.valueOf(1));
58 }
59
60
61
62
63
64
65
66
67 public ClassScope() {
68
69
70 int v = anonymousInnerClassCounter.get().intValue();
71 this.className = "Anonymous$" + v;
72 anonymousInnerClassCounter.set(v + 1);
73 }
74
75 public void setIsEnum(boolean isEnum) {
76 this.isEnum = isEnum;
77 }
78
79 public Map<ClassNameDeclaration, List<NameOccurrence>> getClassDeclarations() {
80 return getDeclarations(ClassNameDeclaration.class);
81 }
82
83 public Map<MethodNameDeclaration, List<NameOccurrence>> getMethodDeclarations() {
84 return getDeclarations(MethodNameDeclaration.class);
85 }
86
87 public Map<VariableNameDeclaration, List<NameOccurrence>> getVariableDeclarations() {
88 return getDeclarations(VariableNameDeclaration.class);
89 }
90
91 public NameDeclaration addNameOccurrence(NameOccurrence occurrence) {
92 JavaNameOccurrence javaOccurrence = (JavaNameOccurrence) occurrence;
93 NameDeclaration decl = findVariableHere(javaOccurrence);
94 if (decl != null && (javaOccurrence.isMethodOrConstructorInvocation() || javaOccurrence.isMethodReference())) {
95 List<NameOccurrence> nameOccurrences = getMethodDeclarations().get(decl);
96 if (nameOccurrences == null) {
97
98 } else {
99 nameOccurrences.add(javaOccurrence);
100 Node n = javaOccurrence.getLocation();
101 if (n instanceof ASTName) {
102 ((ASTName) n).setNameDeclaration(decl);
103 }
104 }
105
106 } else if (decl != null && !javaOccurrence.isThisOrSuper()) {
107 List<NameOccurrence> nameOccurrences = getVariableDeclarations().get(decl);
108 if (nameOccurrences == null) {
109
110
111
112 for (ClassNameDeclaration innerClass : getClassDeclarations().keySet()) {
113 Scope innerClassScope = innerClass.getScope();
114 if (innerClassScope.contains(javaOccurrence)) {
115 innerClassScope.addNameOccurrence(javaOccurrence);
116 }
117 }
118 } else {
119 nameOccurrences.add(javaOccurrence);
120 Node n = javaOccurrence.getLocation();
121 if (n instanceof ASTName) {
122 ((ASTName) n).setNameDeclaration(decl);
123 }
124 }
125 }
126 return decl;
127 }
128
129 public String getClassName() {
130 return this.className;
131 }
132
133 protected NameDeclaration findVariableHere(JavaNameOccurrence occurrence) {
134 Map<MethodNameDeclaration, List<NameOccurrence>> methodDeclarations = getMethodDeclarations();
135 Map<VariableNameDeclaration, List<NameOccurrence>> variableDeclarations = getVariableDeclarations();
136 if (occurrence.isThisOrSuper() || occurrence.getImage() != null && occurrence.getImage().equals(className)) {
137 if (variableDeclarations.isEmpty() && methodDeclarations.isEmpty()) {
138
139
140
141
142 return null;
143 }
144
145
146
147
148
149
150
151
152
153 if (!variableDeclarations.isEmpty()) {
154 return variableDeclarations.keySet().iterator().next();
155 }
156 return methodDeclarations.keySet().iterator().next();
157 }
158
159 if (occurrence.isMethodOrConstructorInvocation()) {
160 for (MethodNameDeclaration mnd : methodDeclarations.keySet()) {
161 if (mnd.getImage().equals(occurrence.getImage())) {
162 List<TypedNameDeclaration> parameterTypes = determineParameterTypes(mnd);
163 List<TypedNameDeclaration> argumentTypes = determineArgumentTypes(occurrence, parameterTypes);
164
165 if (!mnd.isVarargs()
166 && occurrence.getArgumentCount() == mnd.getParameterCount()
167 && (!getEnclosingScope(SourceFileScope.class).hasAuxclasspath() || parameterTypes
168 .equals(argumentTypes))) {
169 return mnd;
170 } else if (mnd.isVarargs()) {
171 int varArgIndex = parameterTypes.size() - 1;
172 TypedNameDeclaration varArgType = parameterTypes.get(varArgIndex);
173
174
175
176
177
178 if ((varArgIndex == 0 || argumentTypes.size() >= varArgIndex)
179 && (!getEnclosingScope(SourceFileScope.class).hasAuxclasspath() || parameterTypes
180 .subList(0, varArgIndex).equals(argumentTypes.subList(0, varArgIndex)))) {
181
182 if (!getEnclosingScope(SourceFileScope.class).hasAuxclasspath()) {
183 return mnd;
184 }
185
186 boolean sameType = true;
187 for (int i = varArgIndex; i < argumentTypes.size(); i++) {
188 if (!varArgType.equals(argumentTypes.get(i))) {
189 sameType = false;
190 break;
191 }
192 }
193 if (sameType) {
194 return mnd;
195 }
196 }
197 }
198 }
199 }
200 if (isEnum && "valueOf".equals(occurrence.getImage())) {
201 return createBuiltInMethodDeclaration("valueOf", 1);
202 }
203 return null;
204 }
205 if (occurrence.isMethodReference()) {
206 for (MethodNameDeclaration mnd : methodDeclarations.keySet()) {
207 if (mnd.getImage().equals(occurrence.getImage())) {
208 return mnd;
209 }
210 }
211 return null;
212 }
213
214 List<String> images = new ArrayList<String>();
215 if (occurrence.getImage() != null) {
216 images.add(occurrence.getImage());
217 if (occurrence.getImage().startsWith(className)) {
218 images.add(clipClassName(occurrence.getImage()));
219 }
220 }
221 ImageFinderFunction finder = new ImageFinderFunction(images);
222 Applier.apply(finder, variableDeclarations.keySet().iterator());
223 NameDeclaration result = finder.getDecl();
224
225
226 Map<ClassNameDeclaration, List<NameOccurrence>> classDeclarations = getClassDeclarations();
227 if (result == null && !classDeclarations.isEmpty()) {
228 for (ClassNameDeclaration innerClass : getClassDeclarations().keySet()) {
229 Applier.apply(finder, innerClass.getScope().getDeclarations().keySet().iterator());
230 result = finder.getDecl();
231 if (result != null) {
232 break;
233 }
234 }
235 }
236 return result;
237 }
238
239
240
241
242
243
244
245
246
247 private MethodNameDeclaration createBuiltInMethodDeclaration(final String methodName, final int parameterCount) {
248 ASTMethodDeclaration methodDeclaration = new ASTMethodDeclaration(JavaParserTreeConstants.JJTMETHODDECLARATION);
249 methodDeclaration.setPublic(true);
250 methodDeclaration.setScope(this);
251
252 ASTMethodDeclarator methodDeclarator = new ASTMethodDeclarator(JavaParserTreeConstants.JJTMETHODDECLARATOR);
253 methodDeclarator.setImage(methodName);
254 methodDeclarator.setScope(this);
255
256 ASTFormalParameters formalParameters = new ASTFormalParameters(JavaParserTreeConstants.JJTFORMALPARAMETERS);
257 formalParameters.setScope(this);
258
259 methodDeclaration.jjtAddChild(methodDeclarator, 0);
260 methodDeclarator.jjtSetParent(methodDeclaration);
261 methodDeclarator.jjtAddChild(formalParameters, 0);
262 formalParameters.jjtSetParent(methodDeclarator);
263
264 for (int i = 0; i < parameterCount; i++) {
265 ASTFormalParameter formalParameter = new ASTFormalParameter(JavaParserTreeConstants.JJTFORMALPARAMETER);
266 formalParameters.jjtAddChild(formalParameter, i);
267 formalParameter.jjtSetParent(formalParameters);
268
269 ASTType type = new ASTType(JavaParserTreeConstants.JJTTYPE);
270 formalParameter.jjtAddChild(type, 0);
271 type.jjtSetParent(formalParameter);
272 ASTVariableDeclaratorId variableDeclaratorId = new ASTVariableDeclaratorId(JavaParserTreeConstants.JJTVARIABLEDECLARATORID);
273 variableDeclaratorId.setImage("arg" + i);
274 formalParameter.jjtAddChild(variableDeclaratorId, 1);
275 variableDeclaratorId.jjtSetParent(formalParameter);
276 }
277
278 MethodNameDeclaration mnd = new MethodNameDeclaration(methodDeclarator);
279 return mnd;
280 }
281
282
283
284
285
286
287
288
289
290 private List<TypedNameDeclaration> determineParameterTypes(MethodNameDeclaration mnd) {
291 List<TypedNameDeclaration> parameterTypes = new ArrayList<TypedNameDeclaration>();
292 List<ASTFormalParameter> parameters = mnd.getMethodNameDeclaratorNode().findDescendantsOfType(
293 ASTFormalParameter.class);
294 for (ASTFormalParameter p : parameters) {
295 String typeImage = p.getTypeNode().getTypeImage();
296
297
298
299
300 typeImage = qualifyTypeName(typeImage);
301 Node declaringNode = getEnclosingScope(SourceFileScope.class).getQualifiedTypeNames().get(typeImage);
302 Class<?> resolvedType = this.getEnclosingScope(SourceFileScope.class).resolveType(typeImage);
303 if (resolvedType == null) {
304 resolvedType = resolveGenericType(p, typeImage);
305 }
306 parameterTypes.add(new SimpleTypedNameDeclaration(typeImage, resolvedType, determineSuper(declaringNode)));
307 }
308 return parameterTypes;
309 }
310
311 private String qualifyTypeName(String typeImage) {
312 if (typeImage == null) {
313 return null;
314 }
315
316 Set<String> qualifiedNames = new LinkedHashSet<String>();
317 qualifiedNames.addAll(this.getEnclosingScope(SourceFileScope.class).getQualifiedTypeNames().keySet());
318 qualifiedNames.addAll(this.getEnclosingScope(SourceFileScope.class).getExplicitImports());
319
320 int nameLength = typeImage.length();
321
322 for (String qualified : qualifiedNames) {
323 int fullLength = qualified.length();
324 if (qualified.endsWith(typeImage)
325 && (fullLength == nameLength || qualified.substring(0, fullLength - nameLength).endsWith("."))) {
326 return qualified;
327 }
328 }
329 return typeImage;
330 }
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345 private List<TypedNameDeclaration> determineArgumentTypes(JavaNameOccurrence occurrence,
346 List<TypedNameDeclaration> parameterTypes) {
347 List<TypedNameDeclaration> argumentTypes = new ArrayList<TypedNameDeclaration>();
348 Map<String, Node> qualifiedTypeNames = getEnclosingScope(SourceFileScope.class).getQualifiedTypeNames();
349 ASTArgumentList arguments = null;
350 Node nextSibling = null;
351 if (occurrence.getLocation() instanceof ASTPrimarySuffix) {
352 nextSibling = getNextSibling(occurrence.getLocation());
353 } else {
354 nextSibling = getNextSibling(occurrence.getLocation().jjtGetParent());
355 }
356 if (nextSibling != null) {
357 arguments = nextSibling.getFirstDescendantOfType(ASTArgumentList.class);
358 }
359
360 if (arguments != null) {
361 for (int i = 0; i < arguments.jjtGetNumChildren(); i++) {
362 Node argument = arguments.jjtGetChild(i);
363 Node child = null;
364 if (argument.jjtGetNumChildren() > 0 && argument.jjtGetChild(0).jjtGetNumChildren() > 0
365 && argument.jjtGetChild(0).jjtGetChild(0).jjtGetNumChildren() > 0) {
366 child = argument.jjtGetChild(0).jjtGetChild(0).jjtGetChild(0);
367 }
368 TypedNameDeclaration type = null;
369 if (child instanceof ASTName) {
370 ASTName name = (ASTName) child;
371 Scope s = name.getScope();
372 while (s != null) {
373 if (s.contains(new JavaNameOccurrence(name, name.getImage()))) {
374 break;
375 }
376 s = s.getParent();
377 }
378 if (s != null) {
379 Map<VariableNameDeclaration, List<NameOccurrence>> vars = s
380 .getDeclarations(VariableNameDeclaration.class);
381 for (VariableNameDeclaration d : vars.keySet()) {
382
383 if (d.getImage().equals(name.getImage()) && d.getTypeImage() != null) {
384 String typeName = d.getTypeImage();
385 typeName = qualifyTypeName(typeName);
386 Node declaringNode = qualifiedTypeNames.get(typeName);
387 type = new SimpleTypedNameDeclaration(typeName, this.getEnclosingScope(
388 SourceFileScope.class).resolveType(typeName), determineSuper(declaringNode));
389 break;
390 }
391 }
392 }
393 } else if (child instanceof ASTLiteral) {
394 ASTLiteral literal = (ASTLiteral) child;
395 if (literal.isCharLiteral()) {
396 type = new SimpleTypedNameDeclaration("char", literal.getType());
397 } else if (literal.isStringLiteral()) {
398 type = new SimpleTypedNameDeclaration("String", literal.getType());
399 } else if (literal.isFloatLiteral()) {
400 type = new SimpleTypedNameDeclaration("float", literal.getType());
401 } else if (literal.isDoubleLiteral()) {
402 type = new SimpleTypedNameDeclaration("double", literal.getType());
403 } else if (literal.isIntLiteral()) {
404 type = new SimpleTypedNameDeclaration("int", literal.getType());
405 } else if (literal.isLongLiteral()) {
406 type = new SimpleTypedNameDeclaration("long", literal.getType());
407 } else if (literal.jjtGetNumChildren() == 1 && literal.jjtGetChild(0) instanceof ASTBooleanLiteral) {
408 type = new SimpleTypedNameDeclaration("boolean", Boolean.TYPE);
409 }
410 } else if (child instanceof ASTAllocationExpression
411 && child.jjtGetChild(0) instanceof ASTClassOrInterfaceType) {
412 ASTClassOrInterfaceType classInterface = (ASTClassOrInterfaceType) child.jjtGetChild(0);
413 type = convertToSimpleType(classInterface);
414 }
415 if (type == null && !parameterTypes.isEmpty()) {
416
417
418
419
420
421
422
423
424
425
426 if (parameterTypes.size() > i) {
427 type = parameterTypes.get(i);
428 } else {
429 type = parameterTypes.get(parameterTypes.size() - 1);
430
431
432
433
434
435 }
436 }
437 if (type != null && type.getType() == null) {
438 Class<?> typeBound = resolveGenericType(argument, type.getTypeImage());
439 if (typeBound != null) {
440 type = new SimpleTypedNameDeclaration(type.getTypeImage(), typeBound);
441 }
442 }
443 argumentTypes.add(type);
444 }
445 }
446 return argumentTypes;
447 }
448
449 private SimpleTypedNameDeclaration determineSuper(Node declaringNode) {
450 SimpleTypedNameDeclaration result = null;
451 if (declaringNode instanceof ASTClassOrInterfaceDeclaration) {
452 ASTClassOrInterfaceDeclaration classDeclaration = (ASTClassOrInterfaceDeclaration) declaringNode;
453 ASTImplementsList implementsList = classDeclaration.getFirstChildOfType(ASTImplementsList.class);
454 if (implementsList != null) {
455 List<ASTClassOrInterfaceType> types = implementsList.findChildrenOfType(ASTClassOrInterfaceType.class);
456 SimpleTypedNameDeclaration type = convertToSimpleType(types);
457 result = type;
458 }
459 ASTExtendsList extendsList = classDeclaration.getFirstChildOfType(ASTExtendsList.class);
460 if (extendsList != null) {
461 List<ASTClassOrInterfaceType> types = extendsList.findChildrenOfType(ASTClassOrInterfaceType.class);
462 SimpleTypedNameDeclaration type = convertToSimpleType(types);
463 if (result == null) {
464 result = type;
465 } else {
466 result.addNext(type);
467 }
468 }
469 }
470 return result;
471 }
472
473 private SimpleTypedNameDeclaration convertToSimpleType(List<ASTClassOrInterfaceType> types) {
474 SimpleTypedNameDeclaration result = null;
475 for (ASTClassOrInterfaceType t : types) {
476 SimpleTypedNameDeclaration type = convertToSimpleType(t);
477 if (result == null) {
478 result = type;
479 } else {
480 result.addNext(type);
481 }
482 }
483 return result;
484 }
485
486 private SimpleTypedNameDeclaration convertToSimpleType(ASTClassOrInterfaceType t) {
487 String typeImage = t.getImage();
488 typeImage = qualifyTypeName(typeImage);
489 Node declaringNode = getEnclosingScope(SourceFileScope.class).getQualifiedTypeNames().get(typeImage);
490 return new SimpleTypedNameDeclaration(typeImage, this.getEnclosingScope(SourceFileScope.class).resolveType(
491 typeImage), determineSuper(declaringNode));
492 }
493
494
495
496
497
498
499
500
501
502
503
504
505 private Class<?> resolveGenericType(Node argument, String typeImage) {
506 List<ASTTypeParameter> types = new ArrayList<ASTTypeParameter>();
507
508 ASTClassOrInterfaceBodyDeclaration firstParentOfType =
509 argument.getFirstParentOfType(ASTClassOrInterfaceBodyDeclaration.class);
510 if (firstParentOfType != null) {
511 types.addAll(firstParentOfType.findDescendantsOfType(ASTTypeParameter.class));
512 }
513
514
515 ASTClassOrInterfaceDeclaration enclosingClassOrEnum = argument
516 .getFirstParentOfType(ASTClassOrInterfaceDeclaration.class);
517 if (enclosingClassOrEnum == null) {
518 argument.getFirstParentOfType(ASTEnumDeclaration.class);
519 }
520 ASTTypeParameters classLevelTypeParameters = null;
521 if (enclosingClassOrEnum != null) {
522 classLevelTypeParameters = enclosingClassOrEnum.getFirstChildOfType(ASTTypeParameters.class);
523 }
524 if (classLevelTypeParameters != null) {
525 types.addAll(classLevelTypeParameters.findDescendantsOfType(ASTTypeParameter.class));
526 }
527 return resolveGenericType(typeImage, types);
528 }
529
530 private Class<?> resolveGenericType(String typeImage, List<ASTTypeParameter> types) {
531 for (ASTTypeParameter type : types) {
532 if (typeImage.equals(type.getImage())) {
533 ASTClassOrInterfaceType bound = type.getFirstDescendantOfType(ASTClassOrInterfaceType.class);
534 if (bound != null && bound.getType() != null) {
535 return bound.getType();
536 }
537 if (bound != null) {
538 return this.getEnclosingScope(SourceFileScope.class).resolveType(bound.getImage());
539 }
540 }
541 }
542 return null;
543 }
544
545 private Node getNextSibling(Node current) {
546 Node nextSibling = null;
547 for (int i = 0; i < current.jjtGetParent().jjtGetNumChildren() - 1; i++) {
548 if (current.jjtGetParent().jjtGetChild(i) == current) {
549 nextSibling = current.jjtGetParent().jjtGetChild(i + 1);
550 break;
551 }
552 }
553 return nextSibling;
554 }
555
556 public String toString() {
557 StringBuilder res = new StringBuilder("ClassScope (").append(className).append("): ");
558 Map<ClassNameDeclaration, List<NameOccurrence>> classDeclarations = getClassDeclarations();
559 if (classDeclarations.isEmpty()) {
560 res.append("Inner Classes ").append(glomNames(classDeclarations.keySet())).append("; ");
561 }
562 Map<MethodNameDeclaration, List<NameOccurrence>> methodDeclarations = getMethodDeclarations();
563 if (!methodDeclarations.isEmpty()) {
564 for (MethodNameDeclaration mnd : methodDeclarations.keySet()) {
565 res.append(mnd.toString());
566 int usages = methodDeclarations.get(mnd).size();
567 res.append("(begins at line ").append(mnd.getNode().getBeginLine()).append(", ").append(usages)
568 .append(" usages)");
569 res.append(", ");
570 }
571 }
572 Map<VariableNameDeclaration, List<NameOccurrence>> variableDeclarations = getVariableDeclarations();
573 if (!variableDeclarations.isEmpty()) {
574 res.append("Variables ").append(glomNames(variableDeclarations.keySet()));
575 }
576 return res.toString();
577 }
578
579 private String clipClassName(String s) {
580 return s.substring(s.indexOf('.') + 1);
581 }
582 }