001: /**
002: * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003: */package net.sourceforge.pmd.symboltable;
004:
005: import net.sourceforge.pmd.ast.ASTName;
006: import net.sourceforge.pmd.ast.SimpleNode;
007: import net.sourceforge.pmd.util.Applier;
008:
009: import java.util.ArrayList;
010: import java.util.HashMap;
011: import java.util.List;
012: import java.util.Map;
013:
014: public class ClassScope extends AbstractScope {
015:
016: protected Map<ClassNameDeclaration, List<NameOccurrence>> classNames = new HashMap<ClassNameDeclaration, List<NameOccurrence>>();
017: protected Map<MethodNameDeclaration, List<NameOccurrence>> methodNames = new HashMap<MethodNameDeclaration, List<NameOccurrence>>();
018: protected Map<VariableNameDeclaration, List<NameOccurrence>> variableNames = new HashMap<VariableNameDeclaration, List<NameOccurrence>>();
019:
020: // FIXME - this breaks given sufficiently nested code
021: private static ThreadLocal<Integer> anonymousInnerClassCounter = new ThreadLocal<Integer>() {
022: protected Integer initialValue() {
023: return Integer.valueOf(1);
024: }
025: };
026:
027: private String className;
028:
029: public ClassScope(String className) {
030: this .className = className;
031: anonymousInnerClassCounter.set(Integer.valueOf(1));
032: }
033:
034: /**
035: * This is only for anonymous inner classes
036: * <p/>
037: * FIXME - should have name like Foo$1, not Anonymous$1
038: * to get this working right, the parent scope needs
039: * to be passed in when instantiating a ClassScope
040: */
041: public ClassScope() {
042: //this.className = getParent().getEnclosingClassScope().getClassName() + "$" + String.valueOf(anonymousInnerClassCounter);
043: int v = anonymousInnerClassCounter.get().intValue();
044: this .className = "Anonymous$" + v;
045: anonymousInnerClassCounter.set(v + 1);
046: }
047:
048: public void addDeclaration(VariableNameDeclaration variableDecl) {
049: if (variableNames.containsKey(variableDecl)) {
050: throw new RuntimeException(variableDecl
051: + " is already in the symbol table");
052: }
053: variableNames
054: .put(variableDecl, new ArrayList<NameOccurrence>());
055: }
056:
057: public NameDeclaration addVariableNameOccurrence(
058: NameOccurrence occurrence) {
059: NameDeclaration decl = findVariableHere(occurrence);
060: if (decl != null
061: && occurrence.isMethodOrConstructorInvocation()) {
062: List<NameOccurrence> nameOccurrences = methodNames
063: .get(decl);
064: if (nameOccurrences == null) {
065: // TODO may be a class name: Foo.this.super();
066: } else {
067: nameOccurrences.add(occurrence);
068: SimpleNode n = occurrence.getLocation();
069: if (n instanceof ASTName) {
070: ((ASTName) n).setNameDeclaration(decl);
071: } // TODO what to do with PrimarySuffix case?
072: }
073:
074: } else if (decl != null && !occurrence.isThisOrSuper()) {
075: List<NameOccurrence> nameOccurrences = variableNames
076: .get(decl);
077: if (nameOccurrences == null) {
078: // TODO may be a class name
079: } else {
080: nameOccurrences.add(occurrence);
081: SimpleNode n = occurrence.getLocation();
082: if (n instanceof ASTName) {
083: ((ASTName) n).setNameDeclaration(decl);
084: } // TODO what to do with PrimarySuffix case?
085: }
086: }
087: return decl;
088: }
089:
090: public Map<VariableNameDeclaration, List<NameOccurrence>> getVariableDeclarations() {
091: VariableUsageFinderFunction f = new VariableUsageFinderFunction(
092: variableNames);
093: Applier.apply(f, variableNames.keySet().iterator());
094: return f.getUsed();
095: }
096:
097: public Map<MethodNameDeclaration, List<NameOccurrence>> getMethodDeclarations() {
098: return methodNames;
099: }
100:
101: public Map<ClassNameDeclaration, List<NameOccurrence>> getClassDeclarations() {
102: return classNames;
103: }
104:
105: public ClassScope getEnclosingClassScope() {
106: return this ;
107: }
108:
109: public String getClassName() {
110: return this .className;
111: }
112:
113: public void addDeclaration(MethodNameDeclaration decl) {
114: methodNames.put(decl, new ArrayList<NameOccurrence>());
115: }
116:
117: public void addDeclaration(ClassNameDeclaration decl) {
118: classNames.put(decl, new ArrayList<NameOccurrence>());
119: }
120:
121: protected NameDeclaration findVariableHere(NameOccurrence occurrence) {
122: if (occurrence.isThisOrSuper()
123: || occurrence.getImage().equals(className)) {
124: if (variableNames.isEmpty() && methodNames.isEmpty()) {
125: // this could happen if you do this:
126: // public class Foo {
127: // private String x = super.toString();
128: // }
129: return null;
130: }
131: // return any name declaration, since all we really want is to get the scope
132: // for example, if there's a
133: // public class Foo {
134: // private static final int X = 2;
135: // private int y = Foo.X;
136: // }
137: // we'll look up Foo just to get a handle to the class scope
138: // and then we'll look up X.
139: if (!variableNames.isEmpty()) {
140: return variableNames.keySet().iterator().next();
141: }
142: return methodNames.keySet().iterator().next();
143: }
144:
145: if (occurrence.isMethodOrConstructorInvocation()) {
146: for (MethodNameDeclaration mnd : methodNames.keySet()) {
147: if (mnd.getImage().equals(occurrence.getImage())) {
148: int args = occurrence.getArgumentCount();
149: if (args == mnd.getParameterCount()) {
150: // FIXME if several methods have the same name
151: // and parameter count, only one will get caught here
152: // we need to make some attempt at type lookup and discrimination
153: // or, failing that, mark this as a usage of all those methods
154: return mnd;
155: }
156: }
157: }
158: return null;
159: }
160:
161: List<String> images = new ArrayList<String>();
162: images.add(occurrence.getImage());
163: if (occurrence.getImage().startsWith(className)) {
164: images.add(clipClassName(occurrence.getImage()));
165: }
166: ImageFinderFunction finder = new ImageFinderFunction(images);
167: Applier.apply(finder, variableNames.keySet().iterator());
168: return finder.getDecl();
169: }
170:
171: public String toString() {
172: String res = "ClassScope (" + className + "): ";
173: if (!classNames.isEmpty())
174: res += "(" + glomNames(classNames.keySet()) + ")";
175: if (!methodNames.isEmpty()) {
176: for (MethodNameDeclaration mnd : methodNames.keySet()) {
177: res += mnd.toString();
178: int usages = methodNames.get(mnd).size();
179: res += "(begins at line "
180: + mnd.getNode().getBeginLine() + ", " + usages
181: + " usages)";
182: res += ",";
183: }
184: }
185: if (!variableNames.isEmpty())
186: res += "(" + glomNames(variableNames.keySet()) + ")";
187: return res;
188: }
189:
190: private String clipClassName(String in) {
191: return in.substring(in.indexOf('.') + 1);
192: }
193: }
|