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