View Javadoc

    1   /**
    2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
    3    */
    4   package net.sourceforge.pmd.lang.java.rule.javabeans;
    5
    6   import java.util.ArrayList;
    7   import java.util.Arrays;
    8   import java.util.List;
    9   import java.util.Map;
    10
    11  import net.sourceforge.pmd.lang.ast.Node;
    12  import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
    13  import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
    14  import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
    15  import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator;
    16  import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType;
    17  import net.sourceforge.pmd.lang.java.ast.ASTResultType;
    18  import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
    19  import net.sourceforge.pmd.lang.java.symboltable.MethodNameDeclaration;
    20  import net.sourceforge.pmd.lang.java.symboltable.NameOccurrence;
    21  import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
    22  import net.sourceforge.pmd.lang.rule.properties.StringProperty;
    23
    24  public class BeanMembersShouldSerializeRule extends AbstractJavaRule {
    25
    26      private String prefixProperty;
    27
    28      private static final StringProperty PREFIX_DESCRIPTOR = new StringProperty("prefix", "A variable prefix to skip, i.e., m_",
    29  	    "", 1.0f);
    30
    31      public BeanMembersShouldSerializeRule() {
    32  	definePropertyDescriptor(PREFIX_DESCRIPTOR);
    33      }
    34
    35      @Override
    36      public Object visit(ASTCompilationUnit node, Object data) {
    37  	prefixProperty = getProperty(PREFIX_DESCRIPTOR);
    38  	super.visit(node, data);
    39  	return data;
    40      }
    41
    42      private static String[] imagesOf(List<? extends Node> nodes) {
    43
    44  	String[] imageArray = new String[nodes.size()];
    45
    46  	for (int i = 0; i < nodes.size(); i++) {
    47  	    imageArray[i] = nodes.get(i).getImage();
    48  	}
    49  	return imageArray;
    50      }
    51
    52      @Override
    53      public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
    54  	if (node.isInterface()) {
    55  	    return data;
    56  	}
    57
    58  	Map<MethodNameDeclaration, List<NameOccurrence>> methods = node.getScope().getEnclosingClassScope()
    59  		.getMethodDeclarations();
    60  	List<ASTMethodDeclarator> getSetMethList = new ArrayList<ASTMethodDeclarator>(methods.size());
    61  	for (MethodNameDeclaration d : methods.keySet()) {
    62  	    ASTMethodDeclarator mnd = d.getMethodNameDeclaratorNode();
    63  	    if (isBeanAccessor(mnd)) {
    64  		getSetMethList.add(mnd);
    65  	    }
    66  	}
    67
    68  	String[] methNameArray = imagesOf(getSetMethList);
    69
    70  	Arrays.sort(methNameArray);
    71
    72  	Map<VariableNameDeclaration, List<NameOccurrence>> vars = node.getScope().getVariableDeclarations();
    73  	for (VariableNameDeclaration decl : vars.keySet()) {
    74  	    if (vars.get(decl).isEmpty() || decl.getAccessNodeParent().isTransient()
    75  		    || decl.getAccessNodeParent().isStatic()) {
    76  		continue;
    77  	    }
    78  	    String varName = trimIfPrefix(decl.getImage());
    79  	    varName = varName.substring(0, 1).toUpperCase() + varName.substring(1, varName.length());
    80  	    boolean hasGetMethod = Arrays.binarySearch(methNameArray, "get" + varName) >= 0
    81  		    || Arrays.binarySearch(methNameArray, "is" + varName) >= 0;
    82  	    boolean hasSetMethod = Arrays.binarySearch(methNameArray, "set" + varName) >= 0;
    83  	    if (!hasGetMethod || !hasSetMethod) {
    84  		addViolation(data, decl.getNode(), decl.getImage());
    85  	    }
    86  	}
    87  	return super.visit(node, data);
    88      }
    89
    90      private String trimIfPrefix(String img) {
    91  	if (prefixProperty != null && img.startsWith(prefixProperty)) {
    92  	    return img.substring(prefixProperty.length());
    93  	}
    94  	return img;
    95      }
    96
    97      private boolean isBeanAccessor(ASTMethodDeclarator meth) {
    98
    99  	String methodName = meth.getImage();
    100
    101 	if (methodName.startsWith("get") || methodName.startsWith("set")) {
    102 	    return true;
    103 	}
    104 	if (methodName.startsWith("is")) {
    105 	    ASTResultType ret = ((ASTMethodDeclaration) meth.jjtGetParent()).getResultType();
    106 	    List<ASTPrimitiveType> primitives = ret.findDescendantsOfType(ASTPrimitiveType.class);
    107 	    if (!primitives.isEmpty() && primitives.get(0).isBoolean()) {
    108 		return true;
    109 	    }
    110 	}
    111 	return false;
    112     }
    113 }