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