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.ast.AccessNode;
19 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
20 import net.sourceforge.pmd.lang.java.symboltable.ClassScope;
21 import net.sourceforge.pmd.lang.java.symboltable.MethodNameDeclaration;
22 import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
23 import net.sourceforge.pmd.lang.rule.properties.StringProperty;
24 import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
25 import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
26
27 public class BeanMembersShouldSerializeRule extends AbstractJavaRule {
28
29 private String prefixProperty;
30
31 private static final StringProperty PREFIX_DESCRIPTOR = new StringProperty("prefix", "A variable prefix to skip, i.e., m_",
32 "", 1.0f);
33
34 public BeanMembersShouldSerializeRule() {
35 definePropertyDescriptor(PREFIX_DESCRIPTOR);
36 }
37
38 @Override
39 public Object visit(ASTCompilationUnit node, Object data) {
40 prefixProperty = getProperty(PREFIX_DESCRIPTOR);
41 super.visit(node, data);
42 return data;
43 }
44
45 private static String[] imagesOf(List<? extends Node> nodes) {
46
47 String[] imageArray = new String[nodes.size()];
48
49 for (int i = 0; i < nodes.size(); i++) {
50 imageArray[i] = nodes.get(i).getImage();
51 }
52 return imageArray;
53 }
54
55 @Override
56 public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
57 if (node.isInterface()) {
58 return data;
59 }
60
61 Map<MethodNameDeclaration, List<NameOccurrence>> methods = node.getScope().getEnclosingScope(ClassScope.class)
62 .getMethodDeclarations();
63 List<ASTMethodDeclarator> getSetMethList = new ArrayList<ASTMethodDeclarator>(methods.size());
64 for (MethodNameDeclaration d : methods.keySet()) {
65 ASTMethodDeclarator mnd = d.getMethodNameDeclaratorNode();
66 if (isBeanAccessor(mnd)) {
67 getSetMethList.add(mnd);
68 }
69 }
70
71 String[] methNameArray = imagesOf(getSetMethList);
72
73 Arrays.sort(methNameArray);
74
75 Map<NameDeclaration, List<NameOccurrence>> vars = node.getScope().getDeclarations();
76 for (NameDeclaration decl : vars.keySet()) {
77 if (!(decl instanceof VariableNameDeclaration)) {
78 continue;
79 }
80 AccessNode accessNodeParent = ((VariableNameDeclaration)decl).getAccessNodeParent();
81 if (vars.get(decl).isEmpty() || accessNodeParent.isTransient()
82 || accessNodeParent.isStatic()) {
83 continue;
84 }
85 String varName = trimIfPrefix(decl.getImage());
86 varName = varName.substring(0, 1).toUpperCase() + varName.substring(1, varName.length());
87 boolean hasGetMethod = Arrays.binarySearch(methNameArray, "get" + varName) >= 0
88 || Arrays.binarySearch(methNameArray, "is" + varName) >= 0;
89 boolean hasSetMethod = Arrays.binarySearch(methNameArray, "set" + varName) >= 0;
90
91 if (!hasGetMethod || (!accessNodeParent.isFinal() && !hasSetMethod)) {
92 addViolation(data, decl.getNode(), decl.getImage());
93 }
94 }
95 return super.visit(node, data);
96 }
97
98 private String trimIfPrefix(String img) {
99 if (prefixProperty != null && img.startsWith(prefixProperty)) {
100 return img.substring(prefixProperty.length());
101 }
102 return img;
103 }
104
105 private boolean isBeanAccessor(ASTMethodDeclarator meth) {
106
107 String methodName = meth.getImage();
108
109 if (methodName.startsWith("get") || methodName.startsWith("set")) {
110 return true;
111 }
112 if (methodName.startsWith("is")) {
113 ASTResultType ret = ((ASTMethodDeclaration) meth.jjtGetParent()).getResultType();
114 List<ASTPrimitiveType> primitives = ret.findDescendantsOfType(ASTPrimitiveType.class);
115 if (!primitives.isEmpty() && primitives.get(0).isBoolean()) {
116 return true;
117 }
118 }
119 return false;
120 }
121 }