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 }