View Javadoc

    1   /*
    2    * SingularField.java
    3    *
    4    * Created on April 17, 2005, 9:49 PM
    5    */
    6   package net.sourceforge.pmd.rules.design;
    7
    8   import java.util.ArrayList;
    9   import java.util.List;
    10
    11  import net.sourceforge.pmd.AbstractRule;
    12  import net.sourceforge.pmd.PropertyDescriptor;
    13  import net.sourceforge.pmd.ast.ASTAssignmentOperator;
    14  import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
    15  import net.sourceforge.pmd.ast.ASTConstructorDeclaration;
    16  import net.sourceforge.pmd.ast.ASTFieldDeclaration;
    17  import net.sourceforge.pmd.ast.ASTIfStatement;
    18  import net.sourceforge.pmd.ast.ASTInitializer;
    19  import net.sourceforge.pmd.ast.ASTMethodDeclaration;
    20  import net.sourceforge.pmd.ast.ASTPrimaryExpression;
    21  import net.sourceforge.pmd.ast.ASTStatementExpression;
    22  import net.sourceforge.pmd.ast.ASTSynchronizedStatement;
    23  import net.sourceforge.pmd.ast.ASTVariableDeclaratorId;
    24  import net.sourceforge.pmd.ast.Node;
    25  import net.sourceforge.pmd.ast.SimpleNode;
    26  import net.sourceforge.pmd.properties.BooleanProperty;
    27  import net.sourceforge.pmd.symboltable.NameOccurrence;
    28
    29  /**
    30   * @author Eric Olander
    31   * @author Wouter Zelle
    32   */
    33  public class SingularField extends AbstractRule {
    34
    35  	/**
    36  	 * Restore old behaviour by setting both properties to true, which will result in many false positives
    37  	 */
    38      private static final PropertyDescriptor CHECK_INNER_CLASSES = new BooleanProperty(
    39  			"CheckInnerClasses", "Check inner classes", false, 1.0f);
    40      private static final PropertyDescriptor DISALLOW_NOT_ASSIGNMENT = new BooleanProperty(
    41  			"DisallowNotAssignment", "Disallow violations where the first usage is not an assignment", false, 1.0f);
    42
    43      public Object visit(ASTFieldDeclaration node, Object data) {
    44      	boolean checkInnerClasses = getBooleanProperty(CHECK_INNER_CLASSES);
    45      	boolean disallowNotAssignment = getBooleanProperty(DISALLOW_NOT_ASSIGNMENT);
    46
    47          if (node.isPrivate() && !node.isStatic()) {
    48              List<ASTVariableDeclaratorId> list = node.findChildrenOfType(ASTVariableDeclaratorId.class);
    49              ASTVariableDeclaratorId declaration = list.get(0);
    50              List<NameOccurrence> usages = declaration.getUsages();
    51              SimpleNode decl = null;
    52              boolean violation = true;
    53              for (int ix = 0; ix < usages.size(); ix++) {
    54                  NameOccurrence no = usages.get(ix);
    55                  SimpleNode location = no.getLocation();
    56
    57                  ASTPrimaryExpression primaryExpressionParent = location.getFirstParentOfType(ASTPrimaryExpression.class);
    58                  if (ix==0 && !disallowNotAssignment) {
    59                  	if (primaryExpressionParent.getFirstParentOfType(ASTIfStatement.class) != null) {
    60                  		//the first usage is in an if, so it may be skipped on
    61                  		//later calls to the method. So this might be legit code
    62                  		//that simply stores an object for later use.
    63                  		violation = false;
    64  	                	break;		//Optimization
    65                  	}
    66
    67                  	//Is the first usage in an assignment?
    68                  	Node potentialStatement = primaryExpressionParent.jjtGetParent();
    69  	                boolean assignmentToField = no.getImage().equals(location.getImage());	//Check the the assignment is not to a field inside the field object
    70  					if (!assignmentToField || !isInAssignment(potentialStatement)) {
    71  	                	violation = false;
    72  	                	break;		//Optimization
    73  	                } else {
    74  	                	if (usages.size() > ix + 1) {
    75  	                		SimpleNode secondUsageLocation = usages.get(ix + 1).getLocation();
    76
    77  	                		List<ASTStatementExpression> parentStatements = secondUsageLocation.getParentsOfType(ASTStatementExpression.class);
    78  	                		for (ASTStatementExpression statementExpression : parentStatements) {
    79  	                			if (statementExpression != null && statementExpression.equals(potentialStatement)) {
    80  		                			//The second usage is in the assignment of the first usage, which is allowed
    81  		                			violation = false;
    82  		    	                	break;		//Optimization
    83  		                		}
    84  							}
    85
    86  	                	}
    87  	                }
    88                  }
    89
    90                  if (!checkInnerClasses) {
    91  	                //Skip inner classes because the field can be used in the outer class and checking this is too difficult
    92  	                ASTClassOrInterfaceDeclaration clazz = location.getFirstParentOfType(ASTClassOrInterfaceDeclaration.class);
    93  	                if (clazz!= null && clazz.getFirstParentOfType(ASTClassOrInterfaceDeclaration.class) != null) {
    94  	                	violation = false;
    95  	                	break;			//Optimization
    96  	                }
    97                  }
    98
    99                  if (primaryExpressionParent.jjtGetParent() instanceof ASTSynchronizedStatement) {
    100                 	//This usage is directly in an expression of a synchronized block
    101                 	violation = false;
    102                 	break;			//Optimization
    103                 }
    104
    105                 SimpleNode method = location.getFirstParentOfType(ASTMethodDeclaration.class);
    106                 if (method == null) {
    107                     method = location.getFirstParentOfType(ASTConstructorDeclaration.class);
    108                     if (method == null) {
    109                     	method = location.getFirstParentOfType(ASTInitializer.class);
    110                     	if (method == null) {
    111                     		continue;
    112                     	}
    113                     }
    114                 }
    115
    116                 if (decl == null) {
    117                     decl = method;
    118                     continue;
    119                 } else if (decl != method) {
    120                     violation = false;
    121                     break;			//Optimization
    122                 }
    123
    124
    125             }
    126
    127             if (violation && !usages.isEmpty()) {
    128                 addViolation(data, node, new Object[] { declaration.getImage() });
    129             }
    130         }
    131         return data;
    132     }
    133
    134 	private boolean isInAssignment(Node potentialStatement) {
    135 		if (potentialStatement instanceof ASTStatementExpression) {
    136 			ASTStatementExpression statement = (ASTStatementExpression)potentialStatement;
    137 			List<ASTAssignmentOperator> assignments = new ArrayList<ASTAssignmentOperator>();
    138 			statement.findChildrenOfType(ASTAssignmentOperator.class, assignments, false);
    139 			if (assignments.isEmpty() || !"=".equals(assignments.get(0).getImage())) {
    140 				return false;
    141 			} else {
    142 				return true;
    143 			}
    144 		} else {
    145 			return false;
    146 		}
    147 	}
    148 }