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.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  	    // Note that a Setter method is not applicable to a final variable...
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 }