BeanMembersShouldSerializeRule.java
001 /**
002  * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003  */
004 package net.sourceforge.pmd.lang.java.rule.javabeans;
005 
006 import java.util.ArrayList;
007 import java.util.Arrays;
008 import java.util.List;
009 import java.util.Map;
010 
011 import net.sourceforge.pmd.lang.ast.Node;
012 import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
013 import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
014 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
015 import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator;
016 import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType;
017 import net.sourceforge.pmd.lang.java.ast.ASTResultType;
018 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
019 import net.sourceforge.pmd.lang.java.symboltable.MethodNameDeclaration;
020 import net.sourceforge.pmd.lang.java.symboltable.NameOccurrence;
021 import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
022 import net.sourceforge.pmd.lang.rule.properties.StringProperty;
023 
024 public class BeanMembersShouldSerializeRule extends AbstractJavaRule {
025 
026     private String prefixProperty;
027 
028     private static final StringProperty PREFIX_DESCRIPTOR = new StringProperty("prefix""A variable prefix to skip, i.e., m_",
029       ""1.0f);
030     
031     public BeanMembersShouldSerializeRule() {
032   definePropertyDescriptor(PREFIX_DESCRIPTOR);
033     }
034 
035     @Override
036     public Object visit(ASTCompilationUnit node, Object data) {
037   prefixProperty = getProperty(PREFIX_DESCRIPTOR);
038   super.visit(node, data);
039   return data;
040     }
041 
042     private static String[] imagesOf(List<? extends Node> nodes) {
043 
044   String[] imageArray = new String[nodes.size()];
045 
046   for (int i = 0; i < nodes.size(); i++) {
047       imageArray[i= nodes.get(i).getImage();
048   }
049   return imageArray;
050     }
051 
052     @Override
053     public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
054   if (node.isInterface()) {
055       return data;
056   }
057 
058   Map<MethodNameDeclaration, List<NameOccurrence>> methods = node.getScope().getEnclosingClassScope()
059     .getMethodDeclarations();
060   List<ASTMethodDeclarator> getSetMethList = new ArrayList<ASTMethodDeclarator>(methods.size());
061   for (MethodNameDeclaration d : methods.keySet()) {
062       ASTMethodDeclarator mnd = d.getMethodNameDeclaratorNode();
063       if (isBeanAccessor(mnd)) {
064     getSetMethList.add(mnd);
065       }
066   }
067 
068   String[] methNameArray = imagesOf(getSetMethList);
069 
070   Arrays.sort(methNameArray);
071 
072   Map<VariableNameDeclaration, List<NameOccurrence>> vars = node.getScope().getVariableDeclarations();
073   for (VariableNameDeclaration decl : vars.keySet()) {
074       if (vars.get(decl).isEmpty() || decl.getAccessNodeParent().isTransient()
075         || decl.getAccessNodeParent().isStatic()) {
076     continue;
077       }
078       String varName = trimIfPrefix(decl.getImage());
079       varName = varName.substring(01).toUpperCase() + varName.substring(1, varName.length());
080       boolean hasGetMethod = Arrays.binarySearch(methNameArray, "get" + varName>= 0
081         || Arrays.binarySearch(methNameArray, "is" + varName>= 0;
082       boolean hasSetMethod = Arrays.binarySearch(methNameArray, "set" + varName>= 0;
083       if (!hasGetMethod || !hasSetMethod) {
084     addViolation(data, decl.getNode(), decl.getImage());
085       }
086   }
087   return super.visit(node, data);
088     }
089 
090     private String trimIfPrefix(String img) {
091   if (prefixProperty != null && img.startsWith(prefixProperty)) {
092       return img.substring(prefixProperty.length());
093   }
094   return img;
095     }
096 
097     private boolean isBeanAccessor(ASTMethodDeclarator meth) {
098 
099   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 = ((ASTMethodDeclarationmeth.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 }