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