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 }