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 }