View Javadoc

    1   /**
    2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
    3    */
    4   package net.sourceforge.pmd.lang.java.symboltable;
    5
    6   import net.sourceforge.pmd.lang.ast.Node;
    7   import net.sourceforge.pmd.lang.java.ast.ASTAssignmentOperator;
    8   import net.sourceforge.pmd.lang.java.ast.ASTExpression;
    9   import net.sourceforge.pmd.lang.java.ast.ASTName;
    10  import net.sourceforge.pmd.lang.java.ast.ASTPostfixExpression;
    11  import net.sourceforge.pmd.lang.java.ast.ASTPreDecrementExpression;
    12  import net.sourceforge.pmd.lang.java.ast.ASTPreIncrementExpression;
    13  import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
    14  import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
    15  import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression;
    16  import net.sourceforge.pmd.lang.java.ast.JavaNode;
    17
    18  public class NameOccurrence {
    19
    20      private JavaNode 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(JavaNode 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 JavaNode getLocation() {
    67          return location;
    68      }
    69
    70      public boolean isOnRightHandSide() {
    71  	Node node = location.jjtGetParent().jjtGetParent().jjtGetParent();
    72          return node instanceof ASTExpression && node.jjtGetNumChildren() == 3;
    73      }
    74
    75
    76      public boolean isOnLeftHandSide() {
    77          // I detest this method with every atom of my being
    78  	Node primaryExpression;
    79          if (location.jjtGetParent() instanceof ASTPrimaryExpression) {
    80              primaryExpression = location.jjtGetParent().jjtGetParent();
    81          } else if (location.jjtGetParent().jjtGetParent() instanceof ASTPrimaryExpression) {
    82              primaryExpression = location.jjtGetParent().jjtGetParent().jjtGetParent();
    83          } else {
    84              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());
    85          }
    86
    87          if (isStandAlonePostfix(primaryExpression)) {
    88              return true;
    89          }
    90
    91          if (primaryExpression.jjtGetNumChildren() <= 1) {
    92              return false;
    93          }
    94
    95          if (!(primaryExpression.jjtGetChild(1) instanceof ASTAssignmentOperator)) {
    96              return false;
    97          }
    98
    99          if (isPartOfQualifiedName() /* or is an array type */) {
    100             return false;
    101         }
    102
    103         if (isCompoundAssignment(primaryExpression)) {
    104             return false;
    105         }
    106
    107         return true;
    108     }
    109
    110     private boolean isCompoundAssignment(Node primaryExpression) {
    111         return ((ASTAssignmentOperator) primaryExpression.jjtGetChild(1)).isCompound();
    112     }
    113
    114     private boolean isStandAlonePostfix(Node primaryExpression) {
    115         if (!(primaryExpression instanceof ASTPostfixExpression) || !(primaryExpression.jjtGetParent() instanceof ASTStatementExpression)) {
    116             return false;
    117         }
    118
    119         ASTPrimaryPrefix pf = (ASTPrimaryPrefix) ((ASTPrimaryExpression) primaryExpression.jjtGetChild(0)).jjtGetChild(0);
    120         if (pf.usesThisModifier()) {
    121             return true;
    122         }
    123
    124         return thirdChildHasDottedName(primaryExpression);
    125     }
    126
    127     private boolean thirdChildHasDottedName(Node primaryExpression) {
    128         Node thirdChild = primaryExpression.jjtGetChild(0).jjtGetChild(0).jjtGetChild(0);
    129         return thirdChild instanceof ASTName && ((ASTName) thirdChild).getImage().indexOf('.') == -1;
    130     }
    131
    132     /**
    133      * Assert it the occurrence is a self assignment such as:
    134      * <code>
    135      * 		i += 3;
    136      * </code>
    137      *
    138      * @return true, if the occurrence is self-assignment, false, otherwise.
    139      */
    140     @SuppressWarnings("PMD.AvoidBranchingStatementAsLastInLoop")
    141     public boolean isSelfAssignment() {
    142         Node l = location;
    143         while (true) {
    144             Node p = l.jjtGetParent();
    145             Node gp = p.jjtGetParent();
    146             Node node = gp.jjtGetParent();
    147             if (node instanceof ASTPreDecrementExpression || node instanceof ASTPreIncrementExpression || node instanceof ASTPostfixExpression) {
    148                 return true;
    149             }
    150
    151             if (hasAssignmentOperator(gp)) {
    152                 return isCompoundAssignment(gp);
    153             }
    154
    155             if (hasAssignmentOperator(node)) {
    156                 return isCompoundAssignment(node);
    157             }
    158
    159             // deal with extra parenthesis: "(i)++"
    160             if (p instanceof ASTPrimaryPrefix && p.jjtGetNumChildren() == 1 &&
    161                     gp instanceof ASTPrimaryExpression && gp.jjtGetNumChildren() == 1&&
    162                     node instanceof ASTExpression && node.jjtGetNumChildren() == 1 &&
    163                     node.jjtGetParent() instanceof ASTPrimaryPrefix && node.jjtGetParent().jjtGetNumChildren() == 1) {
    164                 l = node;
    165                 continue;
    166             }
    167
    168             // catch this.i++ or ++this.i
    169             return gp instanceof ASTPreDecrementExpression || gp instanceof ASTPreIncrementExpression || gp instanceof ASTPostfixExpression;
    170         }
    171     }
    172
    173     private boolean hasAssignmentOperator(Node node) {
    174         if (node instanceof ASTStatementExpression || node instanceof ASTExpression) {
    175             if (node.jjtGetNumChildren() >= 2 && node.jjtGetChild(1) instanceof ASTAssignmentOperator) {
    176                 return true;
    177             }
    178         }
    179         return false;
    180     }
    181
    182     /**
    183      * Simply return true is the image is equal to keyword 'this' or 'super'.
    184      *
    185      * @return return true if image equal to 'this' or 'super'.
    186      */
    187     public boolean isThisOrSuper() {
    188         return image.equals(THIS) || image.equals(SUPER);
    189     }
    190
    191     /**
    192      * Simply return if the image start with keyword 'this' or 'super'.
    193      *
    194      * @return true, if keyword is used, false otherwise.
    195      */
    196     public boolean useThisOrSuper() {
    197 		Node node = location.jjtGetParent();
    198 		if ( node instanceof ASTPrimaryExpression ) {
    199 			ASTPrimaryExpression primaryExpression = (ASTPrimaryExpression)node;
    200 			ASTPrimaryPrefix prefix = (ASTPrimaryPrefix) primaryExpression.jjtGetChild(0);
    201 			if ( prefix != null ) {
    202 			    return prefix.usesSuperModifier() || prefix.usesThisModifier();
    203 			}
    204 		}
    205     	return image.startsWith(THIS_DOT) || image.startsWith(SUPER_DOT);
    206     }
    207
    208     @Override
    209     public boolean equals(Object o) {
    210     	if (o instanceof NameOccurrence) {
    211     		NameOccurrence n = (NameOccurrence) o;
    212     		return n.getImage().equals(getImage());
    213     		}
    214     	return false;
    215     }
    216
    217     @Override
    218     public int hashCode() {
    219         return getImage().hashCode();
    220     }
    221
    222     public String getImage() {
    223         return image;
    224     }
    225
    226     @Override
    227     public String toString() {
    228         return getImage() + ":" + location.getBeginLine() + ":" + location.getClass() + (this.isMethodOrConstructorInvocation() ? "(method call)" : "");
    229     }
    230 }