RedundantFieldInitializerRule.java
001 package net.sourceforge.pmd.lang.java.rule.optimizations;
002 
003 import net.sourceforge.pmd.lang.ast.Node;
004 import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral;
005 import net.sourceforge.pmd.lang.java.ast.ASTCastExpression;
006 import net.sourceforge.pmd.lang.java.ast.ASTExpression;
007 import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
008 import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
009 import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral;
010 import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
011 import net.sourceforge.pmd.lang.java.ast.ASTReferenceType;
012 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
013 import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
014 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
015 
016 /**
017  * Detects redundant field initializers, i.e. the field initializer expressions the JVM would assign by default.
018  *
019  @author lucian.ciufudean@gmail.com
020  @since Apr 10, 2009
021  */
022 public class RedundantFieldInitializerRule extends AbstractJavaRule {
023 
024     public RedundantFieldInitializerRule() {
025   addRuleChainVisit(ASTFieldDeclaration.class);
026     }
027 
028     public Object visit(ASTFieldDeclaration fieldDeclaration, Object data) {
029   // Finals can only be initialized once.
030   if (fieldDeclaration.isFinal()) {
031       return data;
032   }
033 
034   // Look for a match to the following XPath:
035   //    VariableDeclarator/VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/Literal
036   for (ASTVariableDeclarator variableDeclarator : fieldDeclaration
037     .findChildrenOfType(ASTVariableDeclarator.class)) {
038       if (variableDeclarator.jjtGetNumChildren() 1) {
039     final Node variableInitializer = variableDeclarator.jjtGetChild(1);
040     if (variableInitializer.jjtGetChild(0instanceof ASTExpression) {
041         final Node expression = variableInitializer.jjtGetChild(0);
042         final Node primaryExpression;
043         if (expression.jjtGetNumChildren() == 1) {
044       if (expression.jjtGetChild(0instanceof ASTPrimaryExpression) {
045           primaryExpression = expression.jjtGetChild(0);
046       else if (expression.jjtGetChild(0instanceof ASTCastExpression
047         && expression.jjtGetChild(0).jjtGetChild(1instanceof ASTPrimaryExpression) {
048           primaryExpression = expression.jjtGetChild(0).jjtGetChild(1);
049       else {
050           continue;
051       }
052         else {
053       continue;
054         }
055         final Node primaryPrefix = primaryExpression.jjtGetChild(0);
056         if (primaryPrefix.jjtGetNumChildren() == && primaryPrefix.jjtGetChild(0instanceof ASTLiteral) {
057       final ASTLiteral literal = (ASTLiteralprimaryPrefix.jjtGetChild(0);
058       if (isRef(fieldDeclaration, variableDeclarator)) {
059           // Reference type
060           if (literal.jjtGetNumChildren() == && literal.jjtGetChild(0instanceof ASTNullLiteral) {
061         addViolation(data, variableDeclarator);
062           }
063       else {
064           // Primitive type
065           if (literal.jjtGetNumChildren() == && literal.jjtGetChild(0instanceof ASTBooleanLiteral) {
066         // boolean type
067         ASTBooleanLiteral booleanLiteral = (ASTBooleanLiteralliteral.jjtGetChild(0);
068         if (!booleanLiteral.isTrue()) {
069             addViolation(data, variableDeclarator);
070         }
071           else if (literal.jjtGetNumChildren() == 0) {
072         // numeric type
073         // Note: Not catching NumberFormatException, as it shouldn't be happening on valid source code.
074         double value = -1;
075         if (literal.isIntLiteral()) {
076             String s = literal.getImage();
077             if (s.endsWith("l"|| s.endsWith("L")) {
078           s = s.substring(0, s.length() 1);
079             }
080             value = Long.decode(s).doubleValue();
081         else if (literal.isFloatLiteral()) {
082             value = Double.parseDouble(literal.getImage());
083         else if (literal.isCharLiteral()) {
084             value = literal.getImage().charAt(1);
085         }
086 
087         if (value == 0) {
088             addViolation(data, variableDeclarator);
089         }
090           }
091       }
092         }
093     }
094       }
095   }
096 
097   return data;
098     }
099 
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 ((ASTVariableDeclaratorIdvariableDeclarator.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 }