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 }