001 /**
002 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003 */
004 package net.sourceforge.pmd.lang.java.symboltable;
005
006 import net.sourceforge.pmd.lang.ast.Node;
007 import net.sourceforge.pmd.lang.java.ast.ASTAssignmentOperator;
008 import net.sourceforge.pmd.lang.java.ast.ASTExpression;
009 import net.sourceforge.pmd.lang.java.ast.ASTName;
010 import net.sourceforge.pmd.lang.java.ast.ASTPostfixExpression;
011 import net.sourceforge.pmd.lang.java.ast.ASTPreDecrementExpression;
012 import net.sourceforge.pmd.lang.java.ast.ASTPreIncrementExpression;
013 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
014 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
015 import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression;
016 import net.sourceforge.pmd.lang.java.ast.JavaNode;
017
018 public class NameOccurrence {
019
020 private JavaNode location;
021 private String image;
022 private NameOccurrence qualifiedName;
023
024 private boolean isMethodOrConstructorInvocation;
025 private int argumentCount;
026
027 private final static String THIS = "this";
028 private final static String SUPER = "super";
029
030 private final static String THIS_DOT = "this.";
031 private final static String SUPER_DOT = "super.";
032
033 public NameOccurrence(JavaNode location, String image) {
034 this.location = location;
035 this.image = image;
036 }
037
038 public void setIsMethodOrConstructorInvocation() {
039 isMethodOrConstructorInvocation = true;
040 }
041
042 public void setArgumentCount(int count) {
043 argumentCount = count;
044 }
045
046 public int getArgumentCount() {
047 return argumentCount;
048 }
049
050 public boolean isMethodOrConstructorInvocation() {
051 return isMethodOrConstructorInvocation;
052 }
053
054 public void setNameWhichThisQualifies(NameOccurrence qualifiedName) {
055 this.qualifiedName = qualifiedName;
056 }
057
058 public NameOccurrence getNameForWhichThisIsAQualifier() {
059 return qualifiedName;
060 }
061
062 public boolean isPartOfQualifiedName() {
063 return qualifiedName != null;
064 }
065
066 public JavaNode getLocation() {
067 return location;
068 }
069
070 public boolean isOnRightHandSide() {
071 Node node = location.jjtGetParent().jjtGetParent().jjtGetParent();
072 return node instanceof ASTExpression && node.jjtGetNumChildren() == 3;
073 }
074
075
076 public boolean isOnLeftHandSide() {
077 // I detest this method with every atom of my being
078 Node primaryExpression;
079 if (location.jjtGetParent() instanceof ASTPrimaryExpression) {
080 primaryExpression = location.jjtGetParent().jjtGetParent();
081 } else if (location.jjtGetParent().jjtGetParent() instanceof ASTPrimaryExpression) {
082 primaryExpression = location.jjtGetParent().jjtGetParent().jjtGetParent();
083 } else {
084 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());
085 }
086
087 if (isStandAlonePostfix(primaryExpression)) {
088 return true;
089 }
090
091 if (primaryExpression.jjtGetNumChildren() <= 1) {
092 return false;
093 }
094
095 if (!(primaryExpression.jjtGetChild(1) instanceof ASTAssignmentOperator)) {
096 return false;
097 }
098
099 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 (node instanceof ASTStatementExpression) {
152 ASTStatementExpression exp = (ASTStatementExpression) node;
153 if (exp.jjtGetNumChildren() >= 2 && exp.jjtGetChild(1) instanceof ASTAssignmentOperator) {
154 ASTAssignmentOperator op = (ASTAssignmentOperator) exp.jjtGetChild(1);
155 if (op.isCompound()) {
156 return true;
157 }
158 }
159 }
160
161 // deal with extra parenthesis: "(i)++"
162 if (p instanceof ASTPrimaryPrefix && p.jjtGetNumChildren() == 1 &&
163 gp instanceof ASTPrimaryExpression && gp.jjtGetNumChildren() == 1&&
164 node instanceof ASTExpression && node.jjtGetNumChildren() == 1 &&
165 node.jjtGetParent() instanceof ASTPrimaryPrefix && node.jjtGetParent().jjtGetNumChildren() == 1) {
166 l = node;
167 continue;
168 }
169
170 // catch this.i++ or ++this.i
171 return gp instanceof ASTPreDecrementExpression || gp instanceof ASTPreIncrementExpression || gp instanceof ASTPostfixExpression;
172 }
173 }
174
175 /**
176 * Simply return true is the image is equal to keyword 'this' or 'super'.
177 *
178 * @return return true if image equal to 'this' or 'super'.
179 */
180 public boolean isThisOrSuper() {
181 return image.equals(THIS) || image.equals(SUPER);
182 }
183
184 /**
185 * Simply return if the image start with keyword 'this' or 'super'.
186 *
187 * @return true, if keyword is used, false otherwise.
188 */
189 public boolean useThisOrSuper() {
190 Node node = location.jjtGetParent();
191 if ( node instanceof ASTPrimaryExpression ) {
192 ASTPrimaryExpression primaryExpression = (ASTPrimaryExpression)node;
193 ASTPrimaryPrefix prefix = (ASTPrimaryPrefix) primaryExpression.jjtGetChild(0);
194 if ( prefix != null ) {
195 return prefix.usesSuperModifier() || prefix.usesThisModifier();
196 }
197 }
198 return image.startsWith(THIS_DOT) || image.startsWith(SUPER_DOT);
199 }
200
201 @Override
202 public boolean equals(Object o) {
203 NameOccurrence n = (NameOccurrence) o;
204 return n.getImage().equals(getImage());
205 }
206
207 @Override
208 public int hashCode() {
209 return getImage().hashCode();
210 }
211
212 public String getImage() {
213 return image;
214 }
215
216 @Override
217 public String toString() {
218 return getImage() + ":" + location.getBeginLine() + ":" + location.getClass() + (this.isMethodOrConstructorInvocation() ? "(method call)" : "");
219 }
220 }
|