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