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 }