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.ASTAssignmentOperator;
    7   import net.sourceforge.pmd.ast.ASTExpression;
    8   import net.sourceforge.pmd.ast.ASTName;
    9   import net.sourceforge.pmd.ast.ASTPostfixExpression;
    10  import net.sourceforge.pmd.ast.ASTPreDecrementExpression;
    11  import net.sourceforge.pmd.ast.ASTPreIncrementExpression;
    12  import net.sourceforge.pmd.ast.ASTPrimaryExpression;
    13  import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
    14  import net.sourceforge.pmd.ast.ASTStatementExpression;
    15  import net.sourceforge.pmd.ast.Node;
    16  import net.sourceforge.pmd.ast.SimpleNode;
    17
    18  public class NameOccurrence {
    19
    20      private SimpleNode location;
    21      private String image;
    22      private NameOccurrence qualifiedName;
    23
    24      private boolean isMethodOrConstructorInvocation;
    25      private int argumentCount;
    26
    27      private final static String THIS = "this";
    28      private final static String SUPER = "super";
    29
    30      private final static String THIS_DOT = "this.";
    31      private final static String SUPER_DOT = "super.";
    32
    33      public NameOccurrence(SimpleNode location, String image) {
    34          this.location = location;
    35          this.image = image;
    36      }
    37
    38      public void setIsMethodOrConstructorInvocation() {
    39          isMethodOrConstructorInvocation = true;
    40      }
    41
    42      public void setArgumentCount(int count) {
    43          argumentCount = count;
    44      }
    45
    46      public int getArgumentCount() {
    47          return argumentCount;
    48      }
    49
    50      public boolean isMethodOrConstructorInvocation() {
    51          return isMethodOrConstructorInvocation;
    52      }
    53
    54      public void setNameWhichThisQualifies(NameOccurrence qualifiedName) {
    55          this.qualifiedName = qualifiedName;
    56      }
    57
    58      public NameOccurrence getNameForWhichThisIsAQualifier() {
    59          return qualifiedName;
    60      }
    61
    62      public boolean isPartOfQualifiedName() {
    63          return qualifiedName != null;
    64      }
    65
    66      public SimpleNode getLocation() {
    67          return location;
    68      }
    69
    70      public boolean isOnRightHandSide() {
    71          SimpleNode node = (SimpleNode) location.jjtGetParent().jjtGetParent().jjtGetParent();
    72          return node instanceof ASTExpression && node.jjtGetNumChildren() == 3;
    73      }
    74
    75      /**
    76       * <p>A handy method to assert if the name is on the right hand side or the left hand side of
    77       * an expression. One basic example:
    78       *     <code>
    79       *         obj.getMethod(); // Name "getMethod()" returns false, "obj" returns true
    80       *     </code>
    81       * </p>
    82       * @return
    83       */
    84      public boolean isOnLeftHandSide() {
    85          // I detest this method with every atom of my being
    86          // FIXME: I didn't wrote the previous comment, but based on it i added a FIXME !
    87      	SimpleNode parentOfPrimaryExpression;
    88          if (location.jjtGetParent() instanceof ASTPrimaryExpression) {
    89              parentOfPrimaryExpression = (SimpleNode) location.jjtGetParent().jjtGetParent();
    90          } else if (location.jjtGetParent().jjtGetParent() instanceof ASTPrimaryExpression) {
    91              parentOfPrimaryExpression = (SimpleNode) location.jjtGetParent().jjtGetParent().jjtGetParent();
    92          } else {
    93              throw new RuntimeException("Found a NameOccurrence that didn't have an ASTPrimary Expression as parent or grandparent.  Parent = " + location.jjtGetParent() + " and grandparent = " + location.jjtGetParent().jjtGetParent());
    94          }
    95
    96          if (isStandAlonePostfix(parentOfPrimaryExpression)) {
    97              return true;
    98          }
    99          //
    100         if (parentOfPrimaryExpression.jjtGetNumChildren() <= 1) {
    101             return false;
    102         }
    103
    104         if (!(parentOfPrimaryExpression.jjtGetChild(1) instanceof ASTAssignmentOperator)) {
    105             return false;
    106         }
    107
    108
    109         if (isPartOfQualifiedName() /* or is an array type */) {
    110             return false;
    111         }
    112
    113         if (isCompoundAssignment(parentOfPrimaryExpression)) {
    114             return false;
    115         }
    116
    117         return true;
    118     }
    119
    120
    121     private boolean isCompoundAssignment(SimpleNode primaryExpression) {
    122         return ((ASTAssignmentOperator) (primaryExpression.jjtGetChild(1))).isCompound();
    123     }
    124
    125     private boolean isStandAlonePostfix(SimpleNode primaryExpression) {
    126         if (!(primaryExpression instanceof ASTPostfixExpression) || !(primaryExpression.jjtGetParent() instanceof ASTStatementExpression)) {
    127             return false;
    128         }
    129
    130         ASTPrimaryPrefix pf = (ASTPrimaryPrefix) ((ASTPrimaryExpression) primaryExpression.jjtGetChild(0)).jjtGetChild(0);
    131         if (pf.usesThisModifier()) {
    132             return true;
    133         }
    134
    135         return thirdChildHasDottedName(primaryExpression);
    136     }
    137
    138     private boolean thirdChildHasDottedName(SimpleNode primaryExpression) {
    139         Node thirdChild = primaryExpression.jjtGetChild(0).jjtGetChild(0).jjtGetChild(0);
    140         return thirdChild instanceof ASTName && ((ASTName) thirdChild).getImage().indexOf('.') == -1;
    141     }
    142
    143     /**
    144      * Assert it the occurrence is a self assignment such as:
    145      * <code>
    146      * 		i += 3;
    147      * </code>
    148      *
    149      * @return true, if the occurrence is self-assignment, false, otherwise.
    150      */
    151     public boolean isSelfAssignment() {
    152         Node l = location;
    153         while (true) {
    154             Node p = l.jjtGetParent();
    155             Node gp = p.jjtGetParent();
    156             Node node = gp.jjtGetParent();
    157             if (node instanceof ASTPreDecrementExpression || node instanceof ASTPreIncrementExpression || node instanceof ASTPostfixExpression) {
    158                 return true;
    159             }
    160
    161             if (node instanceof ASTStatementExpression) {
    162                 ASTStatementExpression exp = (ASTStatementExpression) node;
    163                 if (exp.jjtGetNumChildren() >= 2 && exp.jjtGetChild(1) instanceof ASTAssignmentOperator) {
    164                     ASTAssignmentOperator op = (ASTAssignmentOperator) exp.jjtGetChild(1);
    165                     if (op.isCompound()) {
    166                         return true;
    167                     }
    168                 }
    169             }
    170
    171             // deal with extra parenthesis: "(i)++"
    172             if (p instanceof ASTPrimaryPrefix && p.jjtGetNumChildren() == 1 &&
    173                     gp instanceof ASTPrimaryExpression && gp.jjtGetNumChildren() == 1&&
    174                     node instanceof ASTExpression && node.jjtGetNumChildren() == 1 &&
    175                     node.jjtGetParent() instanceof ASTPrimaryPrefix && node.jjtGetParent().jjtGetNumChildren() == 1) {
    176                 l = node;
    177                 continue;
    178             }
    179
    180             // catch this.i++ or ++this.i
    181             if (gp instanceof ASTPreDecrementExpression || gp instanceof ASTPreIncrementExpression || gp instanceof ASTPostfixExpression) {
    182                 return true;
    183             }
    184
    185             return false;
    186         }
    187     }
    188
    189     /**
    190      * Simply return true is the image is equal to keyword 'this' or 'super'.
    191      *
    192      * @return return true if image equal to 'this' or 'super'.
    193      */
    194     public boolean isThisOrSuper() {
    195         return image.equals(THIS) || image.equals(SUPER);
    196     }
    197
    198     /**
    199      * Simply return if the image start with keyword 'this' or 'super'.
    200      *
    201      * @return true, if keyword is used, false otherwise.
    202      */
    203     public boolean useThisOrSuper() {
    204 		Node node = location.jjtGetParent();
    205 		if ( node instanceof ASTPrimaryExpression ) {
    206 			ASTPrimaryExpression primaryExpression = (ASTPrimaryExpression)node;
    207 			ASTPrimaryPrefix prefix = primaryExpression.getFirstChildOfType(ASTPrimaryPrefix.class);
    208 			if ( prefix != null )
    209 				return (prefix.usesSuperModifier() || prefix.usesThisModifier());
    210 		}
    211     	return image.startsWith(THIS_DOT) || image.startsWith(SUPER_DOT);
    212     }
    213
    214     @Override
    215     public boolean equals(Object o) {
    216         NameOccurrence n = (NameOccurrence) o;
    217         return n.getImage().equals(getImage());
    218     }
    219
    220     @Override
    221     public int hashCode() {
    222         return getImage().hashCode();
    223     }
    224
    225     public String getImage() {
    226         return image;
    227     }
    228
    229     @Override
    230     public String toString() {
    231         return getImage() + ":" + location.getBeginLine() + ":" + location.getClass() + (this.isMethodOrConstructorInvocation() ? "(method call)" : "");
    232     }
    233 }