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.Logger;
10  
11  import net.sourceforge.pmd.lang.ast.Node;
12  import net.sourceforge.pmd.lang.plsql.ast.ASTName;
13  import net.sourceforge.pmd.lang.plsql.ast.AbstractPLSQLNode;
14  import net.sourceforge.pmd.lang.symboltable.AbstractScope;
15  import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
16  import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
17  
18  public class ClassScope extends AbstractScope {
19     private final static Logger LOGGER = Logger.getLogger(ClassScope.class.getName()); 
20  
21      // FIXME - this breaks given sufficiently nested code
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 = AbstractPLSQLNode.getCanonicalImage(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          //this.className = getParent().getEnclosingClassScope().getClassName() + "$" + String.valueOf(anonymousInnerClassCounter);
42          int v = anonymousInnerClassCounter.get().intValue();
43          this.className = "Anonymous$" + v;
44          anonymousInnerClassCounter.set(v + 1);
45      }
46  
47      @Override
48      public void addDeclaration(NameDeclaration declaration) {
49          if (declaration instanceof VariableNameDeclaration && getDeclarations().keySet().contains(declaration)) {
50              throw new RuntimeException(declaration + " is already in the symbol table");
51          }
52          super.addDeclaration(declaration);
53      }
54  
55      @Override
56      public NameDeclaration addNameOccurrence(NameOccurrence occ) {
57          PLSQLNameOccurrence occurrence = (PLSQLNameOccurrence)occ;
58          NameDeclaration decl = findVariableHere(occurrence);
59          Map<MethodNameDeclaration, List<NameOccurrence>> methodNames = getMethodDeclarations();
60          if (decl != null && occurrence.isMethodOrConstructorInvocation()) {
61              List<NameOccurrence> nameOccurrences = methodNames.get(decl);
62              if (nameOccurrences == null) {
63                  // TODO may be a class name: Foo.this.super();
64              } else {
65                  nameOccurrences.add(occurrence);
66                  Node n = occurrence.getLocation();
67                  if (n instanceof ASTName) {
68                      ((ASTName) n).setNameDeclaration(decl);
69                  } // TODO what to do with PrimarySuffix case?
70              }
71  
72          } else if (decl != null && !occurrence.isThisOrSuper()) {
73              Map<VariableNameDeclaration, List<NameOccurrence>> variableNames = getVariableDeclarations();
74              List<NameOccurrence> nameOccurrences = variableNames.get(decl);
75              if (nameOccurrences == null) {
76                  // TODO may be a class name
77              } else {
78                  nameOccurrences.add(occurrence);
79                  Node n = occurrence.getLocation();
80                  if (n instanceof ASTName) {
81                      ((ASTName) n).setNameDeclaration(decl);
82                  } // TODO what to do with PrimarySuffix case?
83              }
84          }
85          return decl;
86      }
87  
88      public Map<VariableNameDeclaration, List<NameOccurrence>> getVariableDeclarations() {
89          return getDeclarations(VariableNameDeclaration.class);
90      }
91  
92      public Map<MethodNameDeclaration, List<NameOccurrence>> getMethodDeclarations() {
93          return getDeclarations(MethodNameDeclaration.class);
94      }
95  
96      public Map<ClassNameDeclaration, List<NameOccurrence>> getClassDeclarations() {
97          return getDeclarations(ClassNameDeclaration.class);
98      }
99  
100     public ClassScope getEnclosingClassScope() {
101         return this;
102     }
103 
104     public String getClassName() {
105         return this.className;
106     }
107 
108     protected NameDeclaration findVariableHere(PLSQLNameOccurrence occurrence) {
109         Map<VariableNameDeclaration, List<NameOccurrence>> variableDeclarations = getVariableDeclarations();
110         Map<MethodNameDeclaration, List<NameOccurrence>> methodDeclarations = getMethodDeclarations();
111         if (occurrence.isThisOrSuper() || occurrence.getImage().equals(className)) {
112             if (variableDeclarations.isEmpty() && methodDeclarations.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 (!variableDeclarations.isEmpty()) {
128                 return variableDeclarations.keySet().iterator().next();
129             }
130             return methodDeclarations.keySet().iterator().next();
131         }
132 
133         if (occurrence.isMethodOrConstructorInvocation()) {
134             for (MethodNameDeclaration mnd: methodDeclarations.keySet()) {
135                 if (mnd.getImage().equals(occurrence.getImage())) {
136                     int args = occurrence.getArgumentCount();
137                     if (args == mnd.getParameterCount() || (mnd.isVarargs() && args >= mnd.getParameterCount() - 1)) {
138                         // FIXME if several methods have the same name
139                         // and parameter count, only one will get caught here
140                         // we need to make some attempt at type lookup and discrimination
141                         // or, failing that, mark this as a usage of all those methods
142                         return mnd;
143                     }
144                 }
145             }
146             return null;
147         }
148 
149         List<String> images = new ArrayList<String>();
150         images.add(occurrence.getImage());
151 
152 	if (null==occurrence.getImage()) {
153 		LOGGER.finest("occurrence==" +occurrence.toString() 
154 			+ "with Argumanet Count == "+occurrence.getArgumentCount()
155 			+ " for className="+className
156 			) ;
157 	}
158 
159 	
160         if (occurrence.getImage().startsWith(className)) {
161             images.add(clipClassName(occurrence.getImage()));
162         }
163         ImageFinderFunction finder = new ImageFinderFunction(images);
164         Applier.apply(finder, getVariableDeclarations().keySet().iterator());
165         return finder.getDecl();
166     }
167 
168     public String toString() {
169         String res = "ClassScope (" + className + "): ";
170         Map<ClassNameDeclaration, List<NameOccurrence>> classNames = getClassDeclarations();
171         Map<MethodNameDeclaration, List<NameOccurrence>> methodNames = getMethodDeclarations();
172         Map<VariableNameDeclaration, List<NameOccurrence>> variableNames = getVariableDeclarations();
173         if (!classNames.isEmpty()) {
174             res += "(" + classNames.keySet() + ")";
175         }
176         if (!methodNames.isEmpty()) {
177             for (MethodNameDeclaration mnd: methodNames.keySet()) {
178                 res += mnd.toString();
179                 int usages = methodNames.get(mnd).size();
180                 res += "(begins at line " + mnd.getNode().getBeginLine() + ", " + usages + " usages)";
181                 res += ",";
182             }
183         }
184         if (!variableNames.isEmpty()) {
185             res += "(" + variableNames.keySet() + ")";
186         }
187         return res;
188     }
189 
190     private String clipClassName(String s) {
191         return s.substring(s.indexOf('.') + 1);
192     }
193 }