001: /**
002: * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003: */package net.sourceforge.pmd.symboltable;
004:
005: import net.sourceforge.pmd.ast.ASTAssignmentOperator;
006: import net.sourceforge.pmd.ast.ASTExpression;
007: import net.sourceforge.pmd.ast.ASTName;
008: import net.sourceforge.pmd.ast.ASTPostfixExpression;
009: import net.sourceforge.pmd.ast.ASTPreDecrementExpression;
010: import net.sourceforge.pmd.ast.ASTPreIncrementExpression;
011: import net.sourceforge.pmd.ast.ASTPrimaryExpression;
012: import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
013: import net.sourceforge.pmd.ast.ASTStatementExpression;
014: import net.sourceforge.pmd.ast.Node;
015: import net.sourceforge.pmd.ast.SimpleNode;
016:
017: public class NameOccurrence {
018:
019: private SimpleNode location;
020: private String image;
021: private NameOccurrence qualifiedName;
022:
023: private boolean isMethodOrConstructorInvocation;
024: private int argumentCount;
025:
026: private final static String THIS = "this";
027: private final static String SUPER = "super";
028:
029: private final static String THIS_DOT = "this.";
030: private final static String SUPER_DOT = "super.";
031:
032: public NameOccurrence(SimpleNode location, String image) {
033: this .location = location;
034: this .image = image;
035: }
036:
037: public void setIsMethodOrConstructorInvocation() {
038: isMethodOrConstructorInvocation = true;
039: }
040:
041: public void setArgumentCount(int count) {
042: argumentCount = count;
043: }
044:
045: public int getArgumentCount() {
046: return argumentCount;
047: }
048:
049: public boolean isMethodOrConstructorInvocation() {
050: return isMethodOrConstructorInvocation;
051: }
052:
053: public void setNameWhichThisQualifies(NameOccurrence qualifiedName) {
054: this .qualifiedName = qualifiedName;
055: }
056:
057: public NameOccurrence getNameForWhichThisIsAQualifier() {
058: return qualifiedName;
059: }
060:
061: public boolean isPartOfQualifiedName() {
062: return qualifiedName != null;
063: }
064:
065: public SimpleNode getLocation() {
066: return location;
067: }
068:
069: public boolean isOnRightHandSide() {
070: SimpleNode node = (SimpleNode) location.jjtGetParent()
071: .jjtGetParent().jjtGetParent();
072: return node instanceof ASTExpression
073: && node.jjtGetNumChildren() == 3;
074: }
075:
076: public boolean isOnLeftHandSide() {
077: // I detest this method with every atom of my being
078: SimpleNode primaryExpression;
079: if (location.jjtGetParent() instanceof ASTPrimaryExpression) {
080: primaryExpression = (SimpleNode) location.jjtGetParent()
081: .jjtGetParent();
082: } else if (location.jjtGetParent().jjtGetParent() instanceof ASTPrimaryExpression) {
083: primaryExpression = (SimpleNode) location.jjtGetParent()
084: .jjtGetParent().jjtGetParent();
085: } else {
086: throw new RuntimeException(
087: "Found a NameOccurrence that didn't have an ASTPrimary Expression as parent or grandparent. Parent = "
088: + location.jjtGetParent()
089: + " and grandparent = "
090: + location.jjtGetParent().jjtGetParent());
091: }
092:
093: if (isStandAlonePostfix(primaryExpression)) {
094: return true;
095: }
096:
097: if (primaryExpression.jjtGetNumChildren() <= 1) {
098: return false;
099: }
100:
101: if (!(primaryExpression.jjtGetChild(1) instanceof ASTAssignmentOperator)) {
102: return false;
103: }
104:
105: if (isPartOfQualifiedName() /* or is an array type */) {
106: return false;
107: }
108:
109: if (isCompoundAssignment(primaryExpression)) {
110: return false;
111: }
112:
113: return true;
114: }
115:
116: private boolean isCompoundAssignment(SimpleNode primaryExpression) {
117: return ((ASTAssignmentOperator) (primaryExpression
118: .jjtGetChild(1))).isCompound();
119: }
120:
121: private boolean isStandAlonePostfix(SimpleNode primaryExpression) {
122: if (!(primaryExpression instanceof ASTPostfixExpression)
123: || !(primaryExpression.jjtGetParent() instanceof ASTStatementExpression)) {
124: return false;
125: }
126:
127: ASTPrimaryPrefix pf = (ASTPrimaryPrefix) ((ASTPrimaryExpression) primaryExpression
128: .jjtGetChild(0)).jjtGetChild(0);
129: if (pf.usesThisModifier()) {
130: return true;
131: }
132:
133: return thirdChildHasDottedName(primaryExpression);
134: }
135:
136: private boolean thirdChildHasDottedName(SimpleNode primaryExpression) {
137: Node thirdChild = primaryExpression.jjtGetChild(0).jjtGetChild(
138: 0).jjtGetChild(0);
139: return thirdChild instanceof ASTName
140: && ((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
158: || node instanceof ASTPreIncrementExpression
159: || node instanceof ASTPostfixExpression) {
160: return true;
161: }
162:
163: if (node instanceof ASTStatementExpression) {
164: ASTStatementExpression exp = (ASTStatementExpression) node;
165: if (exp.jjtGetNumChildren() >= 2
166: && exp.jjtGetChild(1) instanceof ASTAssignmentOperator) {
167: ASTAssignmentOperator op = (ASTAssignmentOperator) exp
168: .jjtGetChild(1);
169: if (op.isCompound()) {
170: return true;
171: }
172: }
173: }
174:
175: // deal with extra parenthesis: "(i)++"
176: if (p instanceof ASTPrimaryPrefix
177: && p.jjtGetNumChildren() == 1
178: && gp instanceof ASTPrimaryExpression
179: && gp.jjtGetNumChildren() == 1
180: && node instanceof ASTExpression
181: && node.jjtGetNumChildren() == 1
182: && node.jjtGetParent() instanceof ASTPrimaryPrefix
183: && node.jjtGetParent().jjtGetNumChildren() == 1) {
184: l = node;
185: continue;
186: }
187:
188: // catch this.i++ or ++this.i
189: if (gp instanceof ASTPreDecrementExpression
190: || gp instanceof ASTPreIncrementExpression
191: || gp instanceof ASTPostfixExpression) {
192: return true;
193: }
194:
195: return false;
196: }
197: }
198:
199: /**
200: * Simply return true is the image is equal to keyword 'this' or 'super'.
201: *
202: * @return return true if image equal to 'this' or 'super'.
203: */
204: public boolean isThisOrSuper() {
205: return image.equals(THIS) || image.equals(SUPER);
206: }
207:
208: /**
209: * Simply return if the image start with keyword 'this' or 'super'.
210: *
211: * @return true, if keyword is used, false otherwise.
212: */
213: public boolean useThisOrSuper() {
214: Node node = location.jjtGetParent();
215: if (node instanceof ASTPrimaryExpression) {
216: ASTPrimaryExpression primaryExpression = (ASTPrimaryExpression) node;
217: ASTPrimaryPrefix prefix = primaryExpression
218: .getFirstChildOfType(ASTPrimaryPrefix.class);
219: if (prefix != null)
220: return (prefix.usesSuperModifier() || prefix
221: .usesThisModifier());
222: }
223: return image.startsWith(THIS_DOT)
224: || image.startsWith(SUPER_DOT);
225: }
226:
227: @Override
228: public boolean equals(Object o) {
229: NameOccurrence n = (NameOccurrence) o;
230: return n.getImage().equals(getImage());
231: }
232:
233: @Override
234: public int hashCode() {
235: return getImage().hashCode();
236: }
237:
238: public String getImage() {
239: return image;
240: }
241:
242: @Override
243: public String toString() {
244: return getImage()
245: + ":"
246: + location.getBeginLine()
247: + ":"
248: + location.getClass()
249: + (this .isMethodOrConstructorInvocation() ? "(method call)"
250: : "");
251: }
252: }
|