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
86
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()
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
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
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 }