View Javadoc

    1   package net.sourceforge.pmd.lang.java.rule.optimizations;
    2
    3   import net.sourceforge.pmd.lang.ast.Node;
    4   import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral;
    5   import net.sourceforge.pmd.lang.java.ast.ASTCastExpression;
    6   import net.sourceforge.pmd.lang.java.ast.ASTExpression;
    7   import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
    8   import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
    9   import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral;
    10  import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
    11  import net.sourceforge.pmd.lang.java.ast.ASTReferenceType;
    12  import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
    13  import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
    14  import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
    15
    16  /**
    17   * Detects redundant field initializers, i.e. the field initializer expressions the JVM would assign by default.
    18   *
    19   * @author lucian.ciufudean@gmail.com
    20   * @since Apr 10, 2009
    21   */
    22  public class RedundantFieldInitializerRule extends AbstractJavaRule {
    23
    24      public RedundantFieldInitializerRule() {
    25  	addRuleChainVisit(ASTFieldDeclaration.class);
    26      }
    27
    28      public Object visit(ASTFieldDeclaration fieldDeclaration, Object data) {
    29  	// Finals can only be initialized once.
    30  	if (fieldDeclaration.isFinal()) {
    31  	    return data;
    32  	}
    33
    34  	// Look for a match to the following XPath:
    35  	//    VariableDeclarator/VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/Literal
    36  	for (ASTVariableDeclarator variableDeclarator : fieldDeclaration
    37  		.findChildrenOfType(ASTVariableDeclarator.class)) {
    38  	    if (variableDeclarator.jjtGetNumChildren() > 1) {
    39  		final Node variableInitializer = variableDeclarator.jjtGetChild(1);
    40  		if (variableInitializer.jjtGetChild(0) instanceof ASTExpression) {
    41  		    final Node expression = variableInitializer.jjtGetChild(0);
    42  		    final Node primaryExpression;
    43  		    if (expression.jjtGetNumChildren() == 1) {
    44  			if (expression.jjtGetChild(0) instanceof ASTPrimaryExpression) {
    45  			    primaryExpression = expression.jjtGetChild(0);
    46  			} else if (expression.jjtGetChild(0) instanceof ASTCastExpression
    47  				&& expression.jjtGetChild(0).jjtGetChild(1) instanceof ASTPrimaryExpression) {
    48  			    primaryExpression = expression.jjtGetChild(0).jjtGetChild(1);
    49  			} else {
    50  			    continue;
    51  			}
    52  		    } else {
    53  			continue;
    54  		    }
    55  		    final Node primaryPrefix = primaryExpression.jjtGetChild(0);
    56  		    if (primaryPrefix.jjtGetNumChildren() == 1 && primaryPrefix.jjtGetChild(0) instanceof ASTLiteral) {
    57  			final ASTLiteral literal = (ASTLiteral) primaryPrefix.jjtGetChild(0);
    58  			if (isRef(fieldDeclaration, variableDeclarator)) {
    59  			    // Reference type
    60  			    if (literal.jjtGetNumChildren() == 1 && literal.jjtGetChild(0) instanceof ASTNullLiteral) {
    61  				addViolation(data, variableDeclarator);
    62  			    }
    63  			} else {
    64  			    // Primitive type
    65  			    if (literal.jjtGetNumChildren() == 1 && literal.jjtGetChild(0) instanceof ASTBooleanLiteral) {
    66  				// boolean type
    67  				ASTBooleanLiteral booleanLiteral = (ASTBooleanLiteral) literal.jjtGetChild(0);
    68  				if (!booleanLiteral.isTrue()) {
    69  				    addViolation(data, variableDeclarator);
    70  				}
    71  			    } else if (literal.jjtGetNumChildren() == 0) {
    72  				// numeric type
    73  				// Note: Not catching NumberFormatException, as it shouldn't be happening on valid source code.
    74  				double value = -1;
    75  				if (literal.isIntLiteral()) {
    76  				    String s = literal.getImage();
    77  				    if (s.endsWith("l") || s.endsWith("L")) {
    78  					s = s.substring(0, s.length() - 1);
    79  				    }
    80  				    value = Long.decode(s).doubleValue();
    81  				} else if (literal.isFloatLiteral()) {
    82  				    value = Double.parseDouble(literal.getImage());
    83  				} else if (literal.isCharLiteral()) {
    84  				    value = literal.getImage().charAt(1);
    85  				}
    86
    87  				if (value == 0) {
    88  				    addViolation(data, variableDeclarator);
    89  				}
    90  			    }
    91  			}
    92  		    }
    93  		}
    94  	    }
    95  	}
    96
    97  	return data;
    98      }
    99
    100     /**
    101      * Checks if a FieldDeclaration is a reference type (includes arrays). The reference information is in the
    102      * FieldDeclaration for this example: <pre>int[] ia1</pre> and in the VariableDeclarator for this example:
    103      * <pre>int ia2[];</pre>.
    104      *
    105      * @param fieldDeclaration the field to check.
    106      * @param variableDeclarator the variable declarator to check.
    107      * @return <code>true</code> if the field is a reference. <code>false</code> otherwise.
    108      */
    109     private boolean isRef(ASTFieldDeclaration fieldDeclaration, ASTVariableDeclarator variableDeclarator) {
    110 	Node type = fieldDeclaration.jjtGetChild(0).jjtGetChild(0);
    111 	if (type instanceof ASTReferenceType) {
    112 	    // Reference type, array or otherwise
    113 	    return true;
    114 	} else {
    115 	    // Primitive array?
    116 	    return ((ASTVariableDeclaratorId) variableDeclarator.jjtGetChild(0)).isArray();
    117 	}
    118     }
    119
    120     private void addViolation(Object data, ASTVariableDeclarator variableDeclarator) {
    121 	super.addViolation(data, variableDeclarator, variableDeclarator.jjtGetChild(0).getImage());
    122     }
    123 }