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