1 /**
2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3 */
4 package net.sourceforge.pmd.symboltable;
5
6 import net.sourceforge.pmd.ast.ASTName;
7 import net.sourceforge.pmd.ast.SimpleNode;
8 import net.sourceforge.pmd.util.Applier;
9
10 import java.util.ArrayList;
11 import java.util.HashMap;
12 import java.util.List;
13 import java.util.Map;
14
15 public class ClassScope extends AbstractScope {
16
17 protected Map<ClassNameDeclaration, List<NameOccurrence>> classNames = new HashMap<ClassNameDeclaration, List<NameOccurrence>>();
18 protected Map<MethodNameDeclaration, List<NameOccurrence>> methodNames = new HashMap<MethodNameDeclaration, List<NameOccurrence>>();
19 protected Map<VariableNameDeclaration, List<NameOccurrence>> variableNames = new HashMap<VariableNameDeclaration, List<NameOccurrence>>();
20
21
22 private static ThreadLocal<Integer> anonymousInnerClassCounter = new ThreadLocal<Integer>() {
23 protected Integer initialValue() { return Integer.valueOf(1); }
24 };
25
26 private String className;
27
28 public ClassScope(String className) {
29 this.className = className;
30 anonymousInnerClassCounter.set(Integer.valueOf(1));
31 }
32
33 /**
34 * This is only for anonymous inner classes
35 * <p/>
36 * FIXME - should have name like Foo$1, not Anonymous$1
37 * to get this working right, the parent scope needs
38 * to be passed in when instantiating a ClassScope
39 */
40 public ClassScope() {
41
42 int v = anonymousInnerClassCounter.get().intValue();
43 this.className = "Anonymous$" + v;
44 anonymousInnerClassCounter.set(v + 1);
45 }
46
47 public void addDeclaration(VariableNameDeclaration variableDecl) {
48 if (variableNames.containsKey(variableDecl)) {
49 throw new RuntimeException(variableDecl + " is already in the symbol table");
50 }
51 variableNames.put(variableDecl, new ArrayList<NameOccurrence>());
52 }
53
54 public NameDeclaration addVariableNameOccurrence(NameOccurrence occurrence) {
55 NameDeclaration decl = findVariableHere(occurrence);
56 if (decl != null && occurrence.isMethodOrConstructorInvocation()) {
57 List<NameOccurrence> nameOccurrences = methodNames.get(decl);
58 if (nameOccurrences == null) {
59
60 } else {
61 nameOccurrences.add(occurrence);
62 SimpleNode n = occurrence.getLocation();
63 if (n instanceof ASTName) {
64 ((ASTName) n).setNameDeclaration(decl);
65 }
66 }
67
68 } else if (decl != null && !occurrence.isThisOrSuper()) {
69 List<NameOccurrence> nameOccurrences = variableNames.get(decl);
70 if (nameOccurrences == null) {
71
72 } else {
73 nameOccurrences.add(occurrence);
74 SimpleNode n = occurrence.getLocation();
75 if (n instanceof ASTName) {
76 ((ASTName) n).setNameDeclaration(decl);
77 }
78 }
79 }
80 return decl;
81 }
82
83 public Map<VariableNameDeclaration, List<NameOccurrence>> getVariableDeclarations() {
84 VariableUsageFinderFunction f = new VariableUsageFinderFunction(variableNames);
85 Applier.apply(f, variableNames.keySet().iterator());
86 return f.getUsed();
87 }
88
89 public Map<MethodNameDeclaration, List<NameOccurrence>> getMethodDeclarations() {
90 return methodNames;
91 }
92
93 public Map<ClassNameDeclaration, List<NameOccurrence>> getClassDeclarations() {
94 return classNames;
95 }
96
97 public ClassScope getEnclosingClassScope() {
98 return this;
99 }
100
101 public String getClassName() {
102 return this.className;
103 }
104
105 public void addDeclaration(MethodNameDeclaration decl) {
106 methodNames.put(decl, new ArrayList<NameOccurrence>());
107 }
108
109 public void addDeclaration(ClassNameDeclaration decl) {
110 classNames.put(decl, new ArrayList<NameOccurrence>());
111 }
112
113 protected NameDeclaration findVariableHere(NameOccurrence occurrence) {
114 if (occurrence.isThisOrSuper() || occurrence.getImage().equals(className)) {
115 if (variableNames.isEmpty() && methodNames.isEmpty()) {
116
117
118
119
120 return null;
121 }
122
123
124
125
126
127
128
129
130 if (!variableNames.isEmpty()) {
131 return variableNames.keySet().iterator().next();
132 }
133 return methodNames.keySet().iterator().next();
134 }
135
136 if (occurrence.isMethodOrConstructorInvocation()) {
137 for (MethodNameDeclaration mnd: methodNames.keySet()) {
138 if (mnd.getImage().equals(occurrence.getImage())) {
139 int args = occurrence.getArgumentCount();
140 if (args == mnd.getParameterCount() || (mnd.isVarargs() && args >= mnd.getParameterCount() - 1)) {
141
142
143
144
145 return mnd;
146 }
147 }
148 }
149 return null;
150 }
151
152 List<String> images = new ArrayList<String>();
153 images.add(occurrence.getImage());
154 if (occurrence.getImage().startsWith(className)) {
155 images.add(clipClassName(occurrence.getImage()));
156 }
157 ImageFinderFunction finder = new ImageFinderFunction(images);
158 Applier.apply(finder, variableNames.keySet().iterator());
159 return finder.getDecl();
160 }
161
162 public String toString() {
163 String res = "ClassScope (" + className + "): ";
164 if (!classNames.isEmpty()) res += "(" + glomNames(classNames.keySet()) + ")";
165 if (!methodNames.isEmpty()) {
166 for (MethodNameDeclaration mnd: methodNames.keySet()) {
167 res += mnd.toString();
168 int usages = methodNames.get(mnd).size();
169 res += "(begins at line " + mnd.getNode().getBeginLine() + ", " + usages + " usages)";
170 res += ",";
171 }
172 }
173 if (!variableNames.isEmpty()) res += "(" + glomNames(variableNames.keySet()) + ")";
174 return res;
175 }
176
177 private String clipClassName(String in) {
178 return in.substring(in.indexOf('.') + 1);
179 }
180 }