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 net.sourceforge.pmd.lang.ast.Node;
7   import net.sourceforge.pmd.lang.java.ast.ASTAnnotation;
8   import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration;
9   import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration;
10  import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
11  import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
12  import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
13  import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
14  import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
15  import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
16  import net.sourceforge.pmd.lang.rule.properties.BooleanProperty;
17  
18  /**
19   * Detects fields that are declared after methods, constructors, etc.
20   * It was a XPath rule, but the Java version is much faster.
21   * The XPath rule for reference:
22   * <pre>
23  //ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration
24  [not(.//ClassOrInterfaceBodyDeclaration) or $ignoreAnonymousClassDeclarations = 'false']
25  [../preceding-sibling::ClassOrInterfaceBodyDeclaration
26      [  count(ClassOrInterfaceDeclaration) > 0
27      or count(ConstructorDeclaration) > 0
28      or count(MethodDeclaration) > 0
29      or count(AnnotationTypeDeclaration) > 0
30      or ($ignoreEnumDeclarations = 'false' and count(EnumDeclaration) > 0)
31      ]
32  ]
33   * </pre>
34   */
35  public class FieldDeclarationsShouldBeAtStartOfClassRule extends AbstractJavaRule {
36  
37      private BooleanProperty ignoreEnumDeclarations = new BooleanProperty("ignoreEnumDeclarations", "Ignore Enum Declarations that precede fields.", true, 1.0f);
38      private BooleanProperty ignoreAnonymousClassDeclarations = new BooleanProperty("ignoreAnonymousClassDeclarations", "Ignore Field Declarations, that are initialized with anonymous class declarations", true, 2.0f);
39  
40      /**
41       * Initializes the rule {@link FieldDeclarationsShouldBeAtStartOfClassRule}.
42       */
43      public FieldDeclarationsShouldBeAtStartOfClassRule() {
44          definePropertyDescriptor(ignoreEnumDeclarations);
45          definePropertyDescriptor(ignoreAnonymousClassDeclarations);
46      }
47  
48      @Override
49      public Object visit(ASTFieldDeclaration node, Object data) {
50          Node parent = node.jjtGetParent().jjtGetParent();
51          for (int i = 0; i < parent.jjtGetNumChildren(); i++) {
52              Node child = parent.jjtGetChild(i);
53              if (child.jjtGetNumChildren() > 0) {
54                  child = skipAnnotations(child);
55              }
56              if (child.equals(node)) {
57                  break;
58              }
59              if (child instanceof ASTFieldDeclaration) {
60                  continue;
61              }
62              if (node.hasDescendantOfType(ASTClassOrInterfaceBodyDeclaration.class) && getProperty(ignoreAnonymousClassDeclarations).booleanValue()) {
63                  continue;
64              }
65              if (child instanceof ASTClassOrInterfaceDeclaration
66                      || child instanceof ASTMethodDeclaration
67                      || child instanceof ASTConstructorDeclaration
68                      || child instanceof ASTAnnotationTypeDeclaration) {
69                  addViolation(data, node);
70                  break;
71              }
72              if (child instanceof ASTEnumDeclaration && !getProperty(ignoreEnumDeclarations).booleanValue()) {
73                  addViolation(data, node);
74                  break;
75              }
76          }
77          return data;
78      }
79  
80      /**
81       * Ignore all annotations, until anything, that is not an annotation and
82       * return this node
83       * @param child the node from where to start the search
84       * @return the first child or the first child after annotations
85       */
86      private Node skipAnnotations(Node child) {
87          Node nextChild = child.jjtGetChild(0);
88          for (int j = 0; j < child.jjtGetNumChildren(); j++) {
89              if (!(child.jjtGetChild(j) instanceof ASTAnnotation)) {
90                  nextChild = child.jjtGetChild(j);
91                  break;
92              }
93          }
94          return nextChild;
95      }
96  }