1
2
3
4 package net.sourceforge.pmd.lang.java.symboltable;
5
6 import java.util.ArrayList;
7 import java.util.List;
8 import java.util.Map;
9
10 import net.sourceforge.pmd.lang.ast.Node;
11 import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression;
12 import net.sourceforge.pmd.lang.java.ast.ASTArgumentList;
13 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
14 import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
15 import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
16 import net.sourceforge.pmd.lang.java.ast.ASTName;
17 import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
18 import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
19 import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
20 import net.sourceforge.pmd.lang.symboltable.Scope;
21
22
23
24
25
26 public class ClassScope extends AbstractJavaScope {
27
28
29 private static ThreadLocal<Integer> anonymousInnerClassCounter = new ThreadLocal<Integer>() {
30 protected Integer initialValue() { return Integer.valueOf(1); }
31 };
32
33 private String className;
34
35 public ClassScope(String className) {
36 this.className = className;
37 anonymousInnerClassCounter.set(Integer.valueOf(1));
38 }
39
40
41
42
43
44
45
46
47 public ClassScope() {
48
49 int v = anonymousInnerClassCounter.get().intValue();
50 this.className = "Anonymous$" + v;
51 anonymousInnerClassCounter.set(v + 1);
52 }
53
54 public Map<ClassNameDeclaration, List<NameOccurrence>> getClassDeclarations() {
55 return getDeclarations(ClassNameDeclaration.class);
56 }
57
58 public Map<MethodNameDeclaration, List<NameOccurrence>> getMethodDeclarations() {
59 return getDeclarations(MethodNameDeclaration.class);
60 }
61
62 public Map<VariableNameDeclaration, List<NameOccurrence>> getVariableDeclarations() {
63 return getDeclarations(VariableNameDeclaration.class);
64 }
65
66 public NameDeclaration addNameOccurrence(NameOccurrence occurrence) {
67 JavaNameOccurrence javaOccurrence = (JavaNameOccurrence)occurrence;
68 NameDeclaration decl = findVariableHere(javaOccurrence);
69 if (decl != null && (javaOccurrence.isMethodOrConstructorInvocation() || javaOccurrence.isMethodReference())) {
70 List<NameOccurrence> nameOccurrences = getMethodDeclarations().get(decl);
71 if (nameOccurrences == null) {
72
73 } else {
74 nameOccurrences.add(javaOccurrence);
75 Node n = javaOccurrence.getLocation();
76 if (n instanceof ASTName) {
77 ((ASTName) n).setNameDeclaration(decl);
78 }
79 }
80
81 } else if (decl != null && !javaOccurrence.isThisOrSuper()) {
82 List<NameOccurrence> nameOccurrences = getVariableDeclarations().get(decl);
83 if (nameOccurrences == null) {
84
85
86
87 for (ClassNameDeclaration innerClass : getClassDeclarations().keySet()) {
88 Scope innerClassScope = innerClass.getScope();
89 if (innerClassScope.contains(javaOccurrence)) {
90 innerClassScope.addNameOccurrence(javaOccurrence);
91 }
92 }
93 } else {
94 nameOccurrences.add(javaOccurrence);
95 Node n = javaOccurrence.getLocation();
96 if (n instanceof ASTName) {
97 ((ASTName) n).setNameDeclaration(decl);
98 }
99 }
100 }
101 return decl;
102 }
103
104 public String getClassName() {
105 return this.className;
106 }
107
108 protected NameDeclaration findVariableHere(JavaNameOccurrence occurrence) {
109 Map<MethodNameDeclaration, List<NameOccurrence>> methodDeclarations = getMethodDeclarations();
110 Map<VariableNameDeclaration, List<NameOccurrence>> variableDeclarations = getVariableDeclarations();
111 if (occurrence.isThisOrSuper() ||
112 (occurrence.getImage() != null && occurrence.getImage().equals(className))) {
113 if (variableDeclarations.isEmpty() && methodDeclarations.isEmpty()) {
114
115
116
117
118 return null;
119 }
120
121
122
123
124
125
126
127
128 if (!variableDeclarations.isEmpty()) {
129 return variableDeclarations.keySet().iterator().next();
130 }
131 return methodDeclarations.keySet().iterator().next();
132 }
133
134 if (occurrence.isMethodOrConstructorInvocation()) {
135 for (MethodNameDeclaration mnd: methodDeclarations.keySet()) {
136 if (mnd.getImage().equals(occurrence.getImage())) {
137 List<TypedNameDeclaration> parameterTypes = determineParameterTypes(mnd);
138 List<TypedNameDeclaration> argumentTypes = determineArgumentTypes(occurrence, parameterTypes);
139
140 if (!mnd.isVarargs()
141 && occurrence.getArgumentCount() == mnd.getParameterCount()
142 && parameterTypes.equals(argumentTypes)) {
143 return mnd;
144 } else if (mnd.isVarargs()) {
145 int varArgIndex = parameterTypes.size() - 1;
146 TypedNameDeclaration varArgType = parameterTypes.get(varArgIndex);
147 if (parameterTypes.subList(0, varArgIndex).equals(argumentTypes.subList(0, varArgIndex))) {
148 boolean sameType = true;
149 for (int i = varArgIndex; i < argumentTypes.size(); i++) {
150 if (!varArgType.equals(argumentTypes.get(i))) {
151 sameType = false;
152 break;
153 }
154 }
155 if (sameType) {
156 return mnd;
157 }
158 }
159 }
160 }
161 }
162 return null;
163 }
164 if (occurrence.isMethodReference()) {
165 for (MethodNameDeclaration mnd: methodDeclarations.keySet()) {
166 if (mnd.getImage().equals(occurrence.getImage())) {
167 return mnd;
168 }
169 }
170 return null;
171 }
172
173 List<String> images = new ArrayList<String>();
174 if (occurrence.getImage() != null) {
175 images.add(occurrence.getImage());
176 if (occurrence.getImage().startsWith(className)) {
177 images.add(clipClassName(occurrence.getImage()));
178 }
179 }
180 ImageFinderFunction finder = new ImageFinderFunction(images);
181 Applier.apply(finder, variableDeclarations.keySet().iterator());
182 NameDeclaration result = finder.getDecl();
183
184
185 Map<ClassNameDeclaration, List<NameOccurrence>> classDeclarations = getClassDeclarations();
186 if (result == null && !classDeclarations.isEmpty()) {
187 for (ClassNameDeclaration innerClass : getClassDeclarations().keySet()) {
188 Applier.apply(finder, innerClass.getScope().getDeclarations().keySet().iterator());
189 result = finder.getDecl();
190 if (result != null) {
191 break;
192 }
193 }
194 }
195 return result;
196 }
197
198
199
200
201
202
203
204 private List<TypedNameDeclaration> determineParameterTypes(MethodNameDeclaration mnd) {
205 List<TypedNameDeclaration> parameterTypes = new ArrayList<TypedNameDeclaration>();
206 List<ASTFormalParameter> parameters = mnd.getMethodNameDeclaratorNode().findDescendantsOfType(ASTFormalParameter.class);
207 for (ASTFormalParameter p : parameters) {
208 Class<?> resolvedType = this.getEnclosingScope(SourceFileScope.class).resolveType(p.getTypeNode().getTypeImage());
209 parameterTypes.add(new SimpleTypedNameDeclaration(p.getTypeNode().getTypeImage(), resolvedType));
210 }
211 return parameterTypes;
212 }
213
214
215
216
217
218
219
220
221
222
223 private List<TypedNameDeclaration> determineArgumentTypes(JavaNameOccurrence occurrence, List<TypedNameDeclaration> parameterTypes) {
224 List<TypedNameDeclaration> argumentTypes = new ArrayList<TypedNameDeclaration>();
225 ASTArgumentList arguments = null;
226 Node nextSibling = null;
227 if (occurrence.getLocation() instanceof ASTPrimarySuffix) {
228 nextSibling = getNextSibling(occurrence.getLocation());
229 } else {
230 nextSibling = getNextSibling(occurrence.getLocation().jjtGetParent());
231 }
232 if (nextSibling != null) {
233 arguments = nextSibling.getFirstDescendantOfType(ASTArgumentList.class);
234 }
235
236 if (arguments != null) {
237 for (int i = 0; i < arguments.jjtGetNumChildren(); i++) {
238 Node argument = arguments.jjtGetChild(i);
239 Node child = null;
240 if (argument.jjtGetNumChildren() > 0 && argument.jjtGetChild(0).jjtGetNumChildren() > 0
241 && argument.jjtGetChild(0).jjtGetChild(0).jjtGetNumChildren() > 0) {
242 child = argument.jjtGetChild(0).jjtGetChild(0).jjtGetChild(0);
243 }
244 TypedNameDeclaration type = null;
245 if (child instanceof ASTName) {
246 ASTName name = (ASTName)child;
247 Scope s = name.getScope();
248 while (s != null) {
249 if (s.contains(new JavaNameOccurrence(name, name.getImage()))) {
250 break;
251 }
252 s = s.getParent();
253 }
254 if (s != null) {
255 Map<VariableNameDeclaration, List<NameOccurrence>> vars = s.getDeclarations(VariableNameDeclaration.class);
256 for (VariableNameDeclaration d : vars.keySet()) {
257 if (d.getImage().equals(name.getImage())) {
258 type = new SimpleTypedNameDeclaration(d.getTypeImage(),
259 this.getEnclosingScope(SourceFileScope.class).resolveType(d.getTypeImage()));
260 break;
261 }
262 }
263 }
264 } else if (child instanceof ASTLiteral) {
265 ASTLiteral literal = (ASTLiteral)child;
266 if (literal.isCharLiteral()) {
267 type = new SimpleTypedNameDeclaration("char", literal.getType());
268 } else if (literal.isStringLiteral()) {
269 type = new SimpleTypedNameDeclaration("String", literal.getType());
270 } else if (literal.isFloatLiteral()) {
271 type = new SimpleTypedNameDeclaration("float", literal.getType());
272 } else if (literal.isDoubleLiteral()) {
273 type = new SimpleTypedNameDeclaration("double", literal.getType());
274 } else if (literal.isIntLiteral()) {
275 type = new SimpleTypedNameDeclaration("int", literal.getType());
276 } else if (literal.isLongLiteral()) {
277 type = new SimpleTypedNameDeclaration("long", literal.getType());
278 }
279 } else if (child instanceof ASTAllocationExpression && child.jjtGetChild(0) instanceof ASTClassOrInterfaceType) {
280 ASTClassOrInterfaceType classInterface = (ASTClassOrInterfaceType)child.jjtGetChild(0);
281 String typeImage = classInterface.getImage();
282 type = new SimpleTypedNameDeclaration(typeImage,
283 this.getEnclosingScope(SourceFileScope.class).resolveType(typeImage));
284 }
285 if (type == null && parameterTypes.size() > i) {
286
287
288
289
290 type = parameterTypes.get(i);
291 }
292 argumentTypes.add(type);
293 }
294 }
295 return argumentTypes;
296 }
297
298 private Node getNextSibling(Node current) {
299 Node nextSibling = null;
300 for (int i = 0; i < current.jjtGetParent().jjtGetNumChildren() - 1; i++) {
301 if (current.jjtGetParent().jjtGetChild(i) == current) {
302 nextSibling = current.jjtGetParent().jjtGetChild(i + 1);
303 break;
304 }
305 }
306 return nextSibling;
307 }
308
309 public String toString() {
310 StringBuilder res = new StringBuilder("ClassScope (").append(className).append("): ");
311 Map<ClassNameDeclaration, List<NameOccurrence>> classDeclarations = getClassDeclarations();
312 if (classDeclarations.isEmpty()) {
313 res.append("Inner Classes ").append(glomNames(classDeclarations.keySet())).append("; ");
314 }
315 Map<MethodNameDeclaration, List<NameOccurrence>> methodDeclarations = getMethodDeclarations();
316 if (!methodDeclarations.isEmpty()) {
317 for (MethodNameDeclaration mnd: methodDeclarations.keySet()) {
318 res.append(mnd.toString());
319 int usages = methodDeclarations.get(mnd).size();
320 res.append("(begins at line ").append(mnd.getNode().getBeginLine()).append(", ").append(usages).append(" usages)");
321 res.append(", ");
322 }
323 }
324 Map<VariableNameDeclaration, List<NameOccurrence>> variableDeclarations = getVariableDeclarations();
325 if (!variableDeclarations.isEmpty()) {
326 res.append("Variables ").append(glomNames(variableDeclarations.keySet()));
327 }
328 return res.toString();
329 }
330
331 private String clipClassName(String s) {
332 return s.substring(s.indexOf('.') + 1);
333 }
334 }