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(0, 1).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 = ((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 }
|