/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.lang.java.symboltable; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.ast.ASTAssignmentOperator; import net.sourceforge.pmd.lang.java.ast.ASTExpression; import net.sourceforge.pmd.lang.java.ast.ASTMethodReference; import net.sourceforge.pmd.lang.java.ast.ASTName; import net.sourceforge.pmd.lang.java.ast.ASTPostfixExpression; import net.sourceforge.pmd.lang.java.ast.ASTPreDecrementExpression; import net.sourceforge.pmd.lang.java.ast.ASTPreIncrementExpression; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression; import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.symboltable.NameOccurrence; public class JavaNameOccurrence implements NameOccurrence { private JavaNode location; private String image; private NameOccurrence qualifiedName; private boolean isMethodOrConstructorInvocation; private int argumentCount; private static final String THIS = "this"; private static final String SUPER = "super"; private static final String THIS_DOT = "this."; private static final String SUPER_DOT = "super."; public JavaNameOccurrence(JavaNode location, String image) { this.location = location; this.image = image; } public void setIsMethodOrConstructorInvocation() { isMethodOrConstructorInvocation = true; } public void setArgumentCount(int count) { argumentCount = count; } public int getArgumentCount() { return argumentCount; } public boolean isMethodOrConstructorInvocation() { return isMethodOrConstructorInvocation; } public boolean isMethodReference() { return location instanceof ASTMethodReference; } public void setNameWhichThisQualifies(NameOccurrence qualifiedName) { this.qualifiedName = qualifiedName; } public NameOccurrence getNameForWhichThisIsAQualifier() { return qualifiedName; } public boolean isPartOfQualifiedName() { return qualifiedName != null; } @Override public JavaNode getLocation() { return location; } public boolean isOnRightHandSide() { Node node = location.jjtGetParent().jjtGetParent().jjtGetParent(); return node instanceof ASTExpression && node.jjtGetNumChildren() == 3; } public boolean isOnLeftHandSide() { // I detest this method with every atom of my being Node primaryExpression; if (location.jjtGetParent() instanceof ASTPrimaryExpression) { primaryExpression = location.jjtGetParent().jjtGetParent(); } else if (location.jjtGetParent().jjtGetParent() instanceof ASTPrimaryExpression) { primaryExpression = location.jjtGetParent().jjtGetParent().jjtGetParent(); } else { 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()); } if (isStandAlonePostfix(primaryExpression)) { return true; } if (primaryExpression.jjtGetNumChildren() <= 1) { return false; } if (!(primaryExpression.jjtGetChild(1) instanceof ASTAssignmentOperator)) { return false; } if (isPartOfQualifiedName() /* or is an array type */) { return false; } if (isCompoundAssignment(primaryExpression)) { return false; } return true; } private boolean isCompoundAssignment(Node primaryExpression) { return ((ASTAssignmentOperator) primaryExpression.jjtGetChild(1)).isCompound(); } private boolean isStandAlonePostfix(Node primaryExpression) { if (!(primaryExpression instanceof ASTPostfixExpression) || !(primaryExpression.jjtGetParent() instanceof ASTStatementExpression)) { return false; } ASTPrimaryPrefix pf = (ASTPrimaryPrefix) ((ASTPrimaryExpression) primaryExpression.jjtGetChild(0)) .jjtGetChild(0); if (pf.usesThisModifier()) { return true; } return thirdChildHasDottedName(primaryExpression); } private boolean thirdChildHasDottedName(Node primaryExpression) { Node thirdChild = primaryExpression.jjtGetChild(0).jjtGetChild(0).jjtGetChild(0); return thirdChild instanceof ASTName && ((ASTName) thirdChild).getImage().indexOf('.') == -1; } /** * Assert it the occurrence is a self assignment such as: * <code>i += 3;</code> * * @return true, if the occurrence is self-assignment, false, otherwise. */ @SuppressWarnings("PMD.AvoidBranchingStatementAsLastInLoop") public boolean isSelfAssignment() { Node l = location; while (true) { Node p = l.jjtGetParent(); Node gp = p.jjtGetParent(); Node node = gp.jjtGetParent(); if (node instanceof ASTPreDecrementExpression || node instanceof ASTPreIncrementExpression || node instanceof ASTPostfixExpression) { return true; } if (hasAssignmentOperator(gp)) { return isCompoundAssignment(gp); } if (hasAssignmentOperator(node)) { return isCompoundAssignment(node); } // deal with extra parenthesis: "(i)++" if (p instanceof ASTPrimaryPrefix && p.jjtGetNumChildren() == 1 && gp instanceof ASTPrimaryExpression && gp.jjtGetNumChildren() == 1 && node instanceof ASTExpression && node.jjtGetNumChildren() == 1 && node.jjtGetParent() instanceof ASTPrimaryPrefix && node.jjtGetParent().jjtGetNumChildren() == 1) { l = node; continue; } // catch this.i++ or ++this.i return gp instanceof ASTPreDecrementExpression || gp instanceof ASTPreIncrementExpression || gp instanceof ASTPostfixExpression; } } private boolean hasAssignmentOperator(Node node) { if (node instanceof ASTStatementExpression || node instanceof ASTExpression) { if (node.jjtGetNumChildren() >= 2 && node.jjtGetChild(1) instanceof ASTAssignmentOperator) { return true; } } return false; } /** * Simply return true is the image is equal to keyword 'this' or 'super'. * * @return return true if image equal to 'this' or 'super'. */ public boolean isThisOrSuper() { return THIS.equals(image) || SUPER.equals(image); } /** * Simply return if the image start with keyword 'this' or 'super'. * * @return true, if keyword is used, false otherwise. */ public boolean useThisOrSuper() { Node node = location.jjtGetParent(); if (node instanceof ASTPrimaryExpression) { ASTPrimaryExpression primaryExpression = (ASTPrimaryExpression) node; ASTPrimaryPrefix prefix = (ASTPrimaryPrefix) primaryExpression.jjtGetChild(0); if (prefix != null) { return prefix.usesSuperModifier() || prefix.usesThisModifier(); } } return image.startsWith(THIS_DOT) || image.startsWith(SUPER_DOT); } @Override public boolean equals(Object o) { if (o instanceof JavaNameOccurrence) { JavaNameOccurrence n = (JavaNameOccurrence) o; return n.getImage().equals(getImage()); } return false; } @Override public int hashCode() { return getImage().hashCode(); } @Override public String getImage() { return image; } @Override public String toString() { return getImage() + ":" + location.getBeginLine() + ":" + location.getClass() + (this.isMethodOrConstructorInvocation() ? "(method call)" : ""); } }