001: /**
002: * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003: */package net.sourceforge.pmd.rules;
004:
005: import java.util.ArrayList;
006: import java.util.Arrays;
007: import java.util.List;
008: import java.util.Map;
009:
010: import net.sourceforge.pmd.AbstractRule;
011: import net.sourceforge.pmd.PropertyDescriptor;
012: import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
013: import net.sourceforge.pmd.ast.ASTCompilationUnit;
014: import net.sourceforge.pmd.ast.ASTMethodDeclarator;
015: import net.sourceforge.pmd.ast.ASTPrimitiveType;
016: import net.sourceforge.pmd.ast.ASTResultType;
017: import net.sourceforge.pmd.ast.SimpleNode;
018: import net.sourceforge.pmd.properties.StringProperty;
019: import net.sourceforge.pmd.symboltable.MethodNameDeclaration;
020: import net.sourceforge.pmd.symboltable.NameOccurrence;
021: import net.sourceforge.pmd.symboltable.VariableNameDeclaration;
022:
023: public class BeanMembersShouldSerializeRule extends AbstractRule {
024:
025: private String prefixProperty;
026:
027: private static final PropertyDescriptor prefixDescriptor = new StringProperty(
028: "prefix", "Prefix somethingorother?", "", 1.0f);
029:
030: private static final Map<String, PropertyDescriptor> propertyDescriptorsByName = asFixedMap(prefixDescriptor);
031:
032: public Object visit(ASTCompilationUnit node, Object data) {
033: prefixProperty = getStringProperty(prefixDescriptor);
034: super .visit(node, data);
035: return data;
036: }
037:
038: private static String[] imagesOf(
039: List<? extends SimpleNode> simpleNodes) {
040:
041: String[] imageArray = new String[simpleNodes.size()];
042:
043: for (int i = 0; i < simpleNodes.size(); i++) {
044: imageArray[i] = simpleNodes.get(i).getImage();
045: }
046: return imageArray;
047: }
048:
049: public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
050: if (node.isInterface()) {
051: return data;
052: }
053:
054: Map<MethodNameDeclaration, List<NameOccurrence>> methods = node
055: .getScope().getEnclosingClassScope()
056: .getMethodDeclarations();
057: List<ASTMethodDeclarator> getSetMethList = new ArrayList<ASTMethodDeclarator>(
058: methods.size());
059: for (MethodNameDeclaration d : methods.keySet()) {
060: ASTMethodDeclarator mnd = d.getMethodNameDeclaratorNode();
061: if (isBeanAccessor(mnd)) {
062: getSetMethList.add(mnd);
063: }
064: }
065:
066: String[] methNameArray = imagesOf(getSetMethList);
067:
068: Arrays.sort(methNameArray);
069:
070: Map<VariableNameDeclaration, List<NameOccurrence>> vars = node
071: .getScope().getVariableDeclarations();
072: for (VariableNameDeclaration decl : vars.keySet()) {
073: if (vars.get(decl).isEmpty()
074: || decl.getAccessNodeParent().isTransient()
075: || decl.getAccessNodeParent().isStatic()) {
076: continue;
077: }
078: String varName = trimIfPrefix(decl.getImage());
079: varName = varName.substring(0, 1).toUpperCase()
080: + varName.substring(1, varName.length());
081: boolean hasGetMethod = Arrays.binarySearch(methNameArray,
082: "get" + varName) >= 0
083: || Arrays.binarySearch(methNameArray, "is"
084: + varName) >= 0;
085: boolean hasSetMethod = Arrays.binarySearch(methNameArray,
086: "set" + varName) >= 0;
087: if (!hasGetMethod || !hasSetMethod) {
088: addViolation(data, decl.getNode(), decl.getImage());
089: }
090: }
091: return super .visit(node, data);
092: }
093:
094: private String trimIfPrefix(String img) {
095: if (prefixProperty != null && img.startsWith(prefixProperty)) {
096: return img.substring(prefixProperty.length());
097: }
098: return img;
099: }
100:
101: private boolean isBeanAccessor(ASTMethodDeclarator meth) {
102:
103: String methodName = meth.getImage();
104:
105: if (methodName.startsWith("get")
106: || methodName.startsWith("set")) {
107: return true;
108: }
109: if (methodName.startsWith("is")) {
110: ASTResultType ret = (ASTResultType) meth.jjtGetParent()
111: .jjtGetChild(0);
112: List primitives = ret
113: .findChildrenOfType(ASTPrimitiveType.class);
114: if (!primitives.isEmpty()
115: && ((ASTPrimitiveType) primitives.get(0))
116: .isBoolean()) {
117: return true;
118: }
119: }
120: return false;
121: }
122:
123: /**
124: * @return Map
125: */
126: protected Map<String, PropertyDescriptor> propertiesByName() {
127: return propertyDescriptorsByName;
128: }
129: }
|