1
2
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 }