View Javadoc
1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.plsql.symboltable;
5   
6   import java.util.ArrayList;
7   import java.util.List;
8   import java.util.Map;
9   import java.util.logging.Level;
10  import java.util.logging.Logger;
11  
12  import net.sourceforge.pmd.lang.ast.Node;
13  import net.sourceforge.pmd.lang.plsql.ast.ASTName;
14  import net.sourceforge.pmd.lang.plsql.ast.AbstractPLSQLNode;
15  import net.sourceforge.pmd.lang.symboltable.AbstractScope;
16  import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
17  import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
18  
19  public class ClassScope extends AbstractScope {
20      private final static Logger LOGGER = Logger.getLogger(ClassScope.class.getName());
21  
22      // FIXME - this breaks given sufficiently nested code
23      private static ThreadLocal<Integer> anonymousInnerClassCounter = new ThreadLocal<Integer>() {
24          protected Integer initialValue() {
25              return Integer.valueOf(1);
26          }
27      };
28  
29      private String className;
30  
31      public ClassScope(String className) {
32          this.className = AbstractPLSQLNode.getCanonicalImage(className);
33          anonymousInnerClassCounter.set(Integer.valueOf(1));
34      }
35  
36      /**
37       * This is only for anonymous inner classes
38       * <p/>
39       * FIXME - should have name like Foo$1, not Anonymous$1 to get this working
40       * right, the parent scope needs to be passed in when instantiating a
41       * ClassScope
42       */
43      public ClassScope() {
44          // this.className = getParent().getEnclosingClassScope().getClassName()
45          // + "$" + String.valueOf(anonymousInnerClassCounter);
46          int v = anonymousInnerClassCounter.get().intValue();
47          this.className = "Anonymous$" + v;
48          anonymousInnerClassCounter.set(v + 1);
49      }
50  
51      @Override
52      public void addDeclaration(NameDeclaration declaration) {
53          if (declaration instanceof VariableNameDeclaration && getDeclarations().keySet().contains(declaration)) {
54              throw new RuntimeException(declaration + " is already in the symbol table");
55          }
56          super.addDeclaration(declaration);
57      }
58  
59      @Override
60      public NameDeclaration addNameOccurrence(NameOccurrence occ) {
61          PLSQLNameOccurrence occurrence = (PLSQLNameOccurrence) occ;
62          NameDeclaration decl = findVariableHere(occurrence);
63          Map<MethodNameDeclaration, List<NameOccurrence>> methodNames = getMethodDeclarations();
64          if (decl != null && occurrence.isMethodOrConstructorInvocation()) {
65              List<NameOccurrence> nameOccurrences = methodNames.get(decl);
66              if (nameOccurrences == null) {
67                  // TODO may be a class name: Foo.this.super();
68              } else {
69                  nameOccurrences.add(occurrence);
70                  Node n = occurrence.getLocation();
71                  if (n instanceof ASTName) {
72                      ((ASTName) n).setNameDeclaration(decl);
73                  } // TODO what to do with PrimarySuffix case?
74              }
75  
76          } else if (decl != null && !occurrence.isThisOrSuper()) {
77              Map<VariableNameDeclaration, List<NameOccurrence>> variableNames = getVariableDeclarations();
78              List<NameOccurrence> nameOccurrences = variableNames.get(decl);
79              if (nameOccurrences == null) {
80                  // TODO may be a class name
81              } else {
82                  nameOccurrences.add(occurrence);
83                  Node n = occurrence.getLocation();
84                  if (n instanceof ASTName) {
85                      ((ASTName) n).setNameDeclaration(decl);
86                  } // TODO what to do with PrimarySuffix case?
87              }
88          }
89          return decl;
90      }
91  
92      public Map<VariableNameDeclaration, List<NameOccurrence>> getVariableDeclarations() {
93          return getDeclarations(VariableNameDeclaration.class);
94      }
95  
96      public Map<MethodNameDeclaration, List<NameOccurrence>> getMethodDeclarations() {
97          return getDeclarations(MethodNameDeclaration.class);
98      }
99  
100     public Map<ClassNameDeclaration, List<NameOccurrence>> getClassDeclarations() {
101         return getDeclarations(ClassNameDeclaration.class);
102     }
103 
104     public ClassScope getEnclosingClassScope() {
105         return this;
106     }
107 
108     public String getClassName() {
109         return this.className;
110     }
111 
112     protected NameDeclaration findVariableHere(PLSQLNameOccurrence occurrence) {
113         Map<VariableNameDeclaration, List<NameOccurrence>> variableDeclarations = getVariableDeclarations();
114         Map<MethodNameDeclaration, List<NameOccurrence>> methodDeclarations = getMethodDeclarations();
115         if (occurrence.isThisOrSuper() || occurrence.getImage().equals(className)) {
116             if (variableDeclarations.isEmpty() && methodDeclarations.isEmpty()) {
117                 // this could happen if you do this:
118                 // public class Foo {
119                 // private String x = super.toString();
120                 // }
121                 return null;
122             }
123             // return any name declaration, since all we really want is to get
124             // the scope
125             // for example, if there's a
126             // public class Foo {
127             // private static final int X = 2;
128             // private int y = Foo.X;
129             // }
130             // we'll look up Foo just to get a handle to the class scope
131             // and then we'll look up X.
132             if (!variableDeclarations.isEmpty()) {
133                 return variableDeclarations.keySet().iterator().next();
134             }
135             return methodDeclarations.keySet().iterator().next();
136         }
137 
138         if (occurrence.isMethodOrConstructorInvocation()) {
139             for (MethodNameDeclaration mnd : methodDeclarations.keySet()) {
140                 if (mnd.getImage().equals(occurrence.getImage())) {
141                     int args = occurrence.getArgumentCount();
142                     if (args == mnd.getParameterCount() || mnd.isVarargs() && args >= mnd.getParameterCount() - 1) {
143                         // FIXME if several methods have the same name
144                         // and parameter count, only one will get caught here
145                         // we need to make some attempt at type lookup and
146                         // discrimination
147                         // or, failing that, mark this as a usage of all those
148                         // methods
149                         return mnd;
150                     }
151                 }
152             }
153             return null;
154         }
155 
156         List<String> images = new ArrayList<String>();
157         images.add(occurrence.getImage());
158 
159         if (null == occurrence.getImage()) {
160             if (LOGGER.isLoggable(Level.FINEST)) {
161                 LOGGER.finest("occurrence==" + occurrence.toString() + "with Argumanet Count == "
162                         + occurrence.getArgumentCount() + " for className=" + className);
163             }
164         }
165 
166         if (occurrence.getImage().startsWith(className)) {
167             images.add(clipClassName(occurrence.getImage()));
168         }
169         ImageFinderFunction finder = new ImageFinderFunction(images);
170         Applier.apply(finder, getVariableDeclarations().keySet().iterator());
171         return finder.getDecl();
172     }
173 
174     public String toString() {
175         String res = "ClassScope (" + className + "): ";
176         Map<ClassNameDeclaration, List<NameOccurrence>> classNames = getClassDeclarations();
177         Map<MethodNameDeclaration, List<NameOccurrence>> methodNames = getMethodDeclarations();
178         Map<VariableNameDeclaration, List<NameOccurrence>> variableNames = getVariableDeclarations();
179         if (!classNames.isEmpty()) {
180             res += "(" + classNames.keySet() + ")";
181         }
182         if (!methodNames.isEmpty()) {
183             for (MethodNameDeclaration mnd : methodNames.keySet()) {
184                 res += mnd.toString();
185                 int usages = methodNames.get(mnd).size();
186                 res += "(begins at line " + mnd.getNode().getBeginLine() + ", " + usages + " usages)";
187                 res += ",";
188             }
189         }
190         if (!variableNames.isEmpty()) {
191             res += "(" + variableNames.keySet() + ")";
192         }
193         return res;
194     }
195 
196     private String clipClassName(String s) {
197         return s.substring(s.indexOf('.') + 1);
198     }
199 }