View Javadoc

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.Iterator;
13  import java.util.List;
14  import java.util.Map;
15  
16  public class ClassScope extends AbstractScope {
17  
18      protected Map classNames = new HashMap();
19      protected Map methodNames = new HashMap();
20      protected Map variableNames = new HashMap();
21  
22      // FIXME - this breaks given sufficiently nested code
23      private static int anonymousInnerClassCounter = 1;
24      private String className;
25  
26      public ClassScope(String className) {
27          this.className = className;
28          anonymousInnerClassCounter = 1;
29      }
30  
31      /***
32       * This is only for anonymous inner classes
33       * <p/>
34       * FIXME - should have name like Foo$1, not Anonymous$1
35       * to get this working right, the parent scope needs
36       * to be passed in when instantiating a ClassScope
37       */
38      public ClassScope() {
39          //this.className = getParent().getEnclosingClassScope().getClassName() + "$" + String.valueOf(anonymousInnerClassCounter);
40          this.className = "Anonymous$" + String.valueOf(anonymousInnerClassCounter);
41          anonymousInnerClassCounter++;
42      }
43  
44      public void addDeclaration(VariableNameDeclaration variableDecl) {
45          if (variableNames.containsKey(variableDecl)) {
46              throw new RuntimeException(variableDecl + " is already in the symbol table");
47          }
48          variableNames.put(variableDecl, new ArrayList());
49      }
50  
51      public NameDeclaration addVariableNameOccurrence(NameOccurrence occurrence) {
52          NameDeclaration decl = findVariableHere(occurrence);
53          if (decl != null && occurrence.isMethodOrConstructorInvocation()) {
54              List nameOccurrences = (List) methodNames.get(decl);
55              if (nameOccurrences == null) {
56                  // TODO may be a class name: Foo.this.super();
57              } else {
58                  nameOccurrences.add(occurrence);
59                  SimpleNode n = occurrence.getLocation();
60                  if (n instanceof ASTName) {
61                      ((ASTName) n).setNameDeclaration(decl);
62                  } // TODO what to do with PrimarySuffix case?
63              }
64  
65          } else if (decl != null && !occurrence.isThisOrSuper()) {
66              List nameOccurrences = (List) variableNames.get(decl);
67              if (nameOccurrences == null) {
68                  // TODO may be a class name
69              } else {
70                  nameOccurrences.add(occurrence);
71                  SimpleNode n = occurrence.getLocation();
72                  if (n instanceof ASTName) {
73                      ((ASTName) n).setNameDeclaration(decl);
74                  } // TODO what to do with PrimarySuffix case?
75              }
76          }
77          return decl;
78      }
79  
80      public Map getVariableDeclarations() {
81          VariableUsageFinderFunction f = new VariableUsageFinderFunction(variableNames);
82          Applier.apply(f, variableNames.keySet().iterator());
83          return f.getUsed();
84      }
85  
86      public Map getMethodDeclarations() {
87          return methodNames;
88      }
89  
90      public Map getClassDeclarations() {
91          return classNames;
92      }
93  
94      public ClassScope getEnclosingClassScope() {
95          return this;
96      }
97  
98      public String getClassName() {
99          return this.className;
100     }
101 
102     public void addDeclaration(MethodNameDeclaration decl) {
103         methodNames.put(decl, new ArrayList());
104     }
105 
106     public void addDeclaration(ClassNameDeclaration decl) {
107         classNames.put(decl, new ArrayList());
108     }
109 
110     protected NameDeclaration findVariableHere(NameOccurrence occurrence) {
111         if (occurrence.isThisOrSuper() || occurrence.getImage().equals(className)) {
112             if (variableNames.isEmpty() && methodNames.isEmpty()) {
113                 // this could happen if you do this:
114                 // public class Foo {
115                 //  private String x = super.toString();
116                 // }
117                 return null;
118             }
119             // return any name declaration, since all we really want is to get the scope
120             // for example, if there's a
121             // public class Foo {
122             //  private static final int X = 2;
123             //  private int y = Foo.X;
124             // }
125             // we'll look up Foo just to get a handle to the class scope
126             // and then we'll look up X.
127             if (!variableNames.isEmpty()) {
128                 return (NameDeclaration) variableNames.keySet().iterator().next();
129             }
130             return (NameDeclaration) methodNames.keySet().iterator().next();
131         }
132 
133         if (occurrence.isMethodOrConstructorInvocation()) {
134             for (Iterator i = methodNames.keySet().iterator(); i.hasNext();) {
135                 MethodNameDeclaration mnd = (MethodNameDeclaration) i.next();
136                 if (mnd.getImage().equals(occurrence.getImage())) {
137                     int args = occurrence.getArgumentCount();
138                     if (args == mnd.getParameterCount()) {
139                         // FIXME if several methods have the same name
140                         // and parameter count, only one will get caught here
141                         // we need to make some attempt at type lookup and discrimination
142                         // or, failing that, mark this as a usage of all those methods
143                         return mnd;
144                     }
145                 }
146             }
147             return null;
148         }
149 
150         List images = new ArrayList();
151         images.add(occurrence.getImage());
152         if (occurrence.getImage().startsWith(className)) {
153             images.add(clipClassName(occurrence.getImage()));
154         }
155         ImageFinderFunction finder = new ImageFinderFunction(images);
156         Applier.apply(finder, variableNames.keySet().iterator());
157         return finder.getDecl();
158     }
159 
160     public String toString() {
161         String res = "ClassScope (" + className + "): ";
162         if (!classNames.isEmpty()) res += "(" + glomNames(classNames.keySet().iterator()) + ")";
163         if (!methodNames.isEmpty()) {
164             Iterator i = methodNames.keySet().iterator();
165             while (i.hasNext()) {
166                 MethodNameDeclaration mnd = (MethodNameDeclaration) i.next();
167                 res += mnd.toString();
168                 int usages = ((List) 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().iterator()) + ")";
174         return res;
175     }
176 
177     private String clipClassName(String in) {
178         return in.substring(in.indexOf('.') + 1);
179     }
180 }