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.design;
5   
6   import java.util.HashMap;
7   import java.util.List;
8   import java.util.Map;
9   
10  import net.sourceforge.pmd.lang.java.ast.ASTAssignmentOperator;
11  import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
12  import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
13  import net.sourceforge.pmd.lang.java.ast.ASTIfStatement;
14  import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
15  import net.sourceforge.pmd.lang.java.ast.ASTName;
16  import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral;
17  import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
18  import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
19  import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
20  import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression;
21  import net.sourceforge.pmd.lang.java.ast.ASTSynchronizedStatement;
22  import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
23  import net.sourceforge.pmd.lang.rule.properties.BooleanProperty;
24  
25  public class NonThreadSafeSingletonRule extends AbstractJavaRule {
26  
27      private Map<String, ASTFieldDeclaration> fieldDecls = new HashMap<>();
28  
29      private boolean checkNonStaticMethods = true;
30      private boolean checkNonStaticFields = true;
31  
32      private static final BooleanProperty CHECK_NON_STATIC_METHODS_DESCRIPTOR = new BooleanProperty(
33              "checkNonStaticMethods",
34              "Check for non-static methods.  Do not set this to false and checkNonStaticFields to true.", true, 1.0f);
35      private static final BooleanProperty CHECK_NON_STATIC_FIELDS_DESCRIPTOR = new BooleanProperty(
36              "checkNonStaticFields",
37              "Check for non-static fields.  Do not set this to true and checkNonStaticMethods to false.", false, 2.0f);
38  
39      public NonThreadSafeSingletonRule() {
40          definePropertyDescriptor(CHECK_NON_STATIC_METHODS_DESCRIPTOR);
41          definePropertyDescriptor(CHECK_NON_STATIC_FIELDS_DESCRIPTOR);
42      }
43  
44      @Override
45      public Object visit(ASTCompilationUnit node, Object data) {
46          fieldDecls.clear();
47          checkNonStaticMethods = getProperty(CHECK_NON_STATIC_METHODS_DESCRIPTOR);
48          checkNonStaticFields = getProperty(CHECK_NON_STATIC_FIELDS_DESCRIPTOR);
49          return super.visit(node, data);
50      }
51  
52      @Override
53      public Object visit(ASTFieldDeclaration node, Object data) {
54          if (checkNonStaticFields || node.isStatic()) {
55              fieldDecls.put(node.getVariableName(), node);
56          }
57          return super.visit(node, data);
58      }
59  
60      @Override
61      public Object visit(ASTMethodDeclaration node, Object data) {
62  
63          if (checkNonStaticMethods && !node.isStatic() || node.isSynchronized()) {
64              return super.visit(node, data);
65          }
66  
67          List<ASTIfStatement> ifStatements = node.findDescendantsOfType(ASTIfStatement.class);
68          for (ASTIfStatement ifStatement : ifStatements) {
69              if (ifStatement.getFirstParentOfType(ASTSynchronizedStatement.class) == null) {
70                  if (!ifStatement.hasDescendantOfType(ASTNullLiteral.class)) {
71                      continue;
72                  }
73                  ASTName n = ifStatement.getFirstDescendantOfType(ASTName.class);
74                  if (n == null || !fieldDecls.containsKey(n.getImage())) {
75                      continue;
76                  }
77                  List<ASTAssignmentOperator> assigmnents = ifStatement
78                          .findDescendantsOfType(ASTAssignmentOperator.class);
79                  boolean violation = false;
80                  for (int ix = 0; ix < assigmnents.size(); ix++) {
81                      ASTAssignmentOperator oper = assigmnents.get(ix);
82                      if (!(oper.jjtGetParent() instanceof ASTStatementExpression)) {
83                          continue;
84                      }
85                      ASTStatementExpression expr = (ASTStatementExpression) oper.jjtGetParent();
86                      if (expr.jjtGetChild(0) instanceof ASTPrimaryExpression
87                              && ((ASTPrimaryExpression) expr.jjtGetChild(0)).jjtGetNumChildren() == 1
88                              && ((ASTPrimaryExpression) expr.jjtGetChild(0)).jjtGetChild(0) instanceof ASTPrimaryPrefix) {
89                          ASTPrimaryPrefix pp = (ASTPrimaryPrefix) ((ASTPrimaryExpression) expr.jjtGetChild(0))
90                                  .jjtGetChild(0);
91                          String name = null;
92                          if (pp.usesThisModifier()) {
93                              ASTPrimarySuffix priSuf = expr.getFirstDescendantOfType(ASTPrimarySuffix.class);
94                              name = priSuf.getImage();
95                          } else {
96                              ASTName astName = (ASTName) pp.jjtGetChild(0);
97                              name = astName.getImage();
98                          }
99                          if (fieldDecls.containsKey(name)) {
100                             violation = true;
101                         }
102                     }
103                 }
104                 if (violation) {
105                     addViolation(data, ifStatement);
106                 }
107             }
108         }
109         return super.visit(node, data);
110     }
111 }