View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2018 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.coding;
21  
22  import com.puppycrawl.tools.checkstyle.StatelessCheck;
23  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
24  import com.puppycrawl.tools.checkstyle.api.DetailAST;
25  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26  import com.puppycrawl.tools.checkstyle.utils.CheckUtils;
27  
28  /**
29   * <p>
30   * Checks that each variable declaration is in its own statement
31   * and on its own line.
32   * </p>
33   * <p>
34   * Rationale: <a
35   * href="http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-141270.html">
36   * the SUN Code conventions chapter 6.1</a> recommends that
37   * declarations should be one per line.
38   * </p>
39   * <p>
40   * An example of how to configure the check is:
41   * </p>
42   * <pre>
43   * &lt;module name="MultipleVariableDeclarations"/&gt;
44   * </pre>
45   * @author o_sukhodolsky
46   */
47  @StatelessCheck
48  public class MultipleVariableDeclarationsCheck extends AbstractCheck {
49  
50      /**
51       * A key is pointing to the warning message text in "messages.properties"
52       * file.
53       */
54      public static final String MSG_MULTIPLE = "multiple.variable.declarations";
55  
56      /**
57       * A key is pointing to the warning message text in "messages.properties"
58       * file.
59       */
60      public static final String MSG_MULTIPLE_COMMA = "multiple.variable.declarations.comma";
61  
62      @Override
63      public int[] getAcceptableTokens() {
64          return getRequiredTokens();
65      }
66  
67      @Override
68      public int[] getDefaultTokens() {
69          return getRequiredTokens();
70      }
71  
72      @Override
73      public int[] getRequiredTokens() {
74          return new int[] {TokenTypes.VARIABLE_DEF};
75      }
76  
77      @Override
78      public void visitToken(DetailAST ast) {
79          DetailAST nextNode = ast.getNextSibling();
80  
81          if (nextNode != null) {
82              final boolean isCommaSeparated = nextNode.getType() == TokenTypes.COMMA;
83  
84              if (isCommaSeparated
85                  || nextNode.getType() == TokenTypes.SEMI) {
86                  nextNode = nextNode.getNextSibling();
87              }
88  
89              if (nextNode != null
90                      && nextNode.getType() == TokenTypes.VARIABLE_DEF) {
91                  final DetailAST firstNode = CheckUtils.getFirstNode(ast);
92                  if (isCommaSeparated) {
93                      // Check if the multiple variable declarations are in a
94                      // for loop initializer. If they are, then no warning
95                      // should be displayed. Declaring multiple variables in
96                      // a for loop initializer is a good way to minimize
97                      // variable scope. Refer Feature Request Id - 2895985
98                      // for more details
99                      if (ast.getParent().getType() != TokenTypes.FOR_INIT) {
100                         log(firstNode, MSG_MULTIPLE_COMMA);
101                     }
102                 }
103                 else {
104                     final DetailAST lastNode = getLastNode(ast);
105                     final DetailAST firstNextNode = CheckUtils.getFirstNode(nextNode);
106 
107                     if (firstNextNode.getLineNo() == lastNode.getLineNo()) {
108                         log(firstNode, MSG_MULTIPLE);
109                     }
110                 }
111             }
112         }
113     }
114 
115     /**
116      * Finds sub-node for given node maximum (line, column) pair.
117      * @param node the root of tree for search.
118      * @return sub-node with maximum (line, column) pair.
119      */
120     private static DetailAST getLastNode(final DetailAST node) {
121         DetailAST currentNode = node;
122         DetailAST child = node.getFirstChild();
123         while (child != null) {
124             final DetailAST newNode = getLastNode(child);
125             if (newNode.getLineNo() > currentNode.getLineNo()
126                 || newNode.getLineNo() == currentNode.getLineNo()
127                     && newNode.getColumnNo() > currentNode.getColumnNo()) {
128                 currentNode = newNode;
129             }
130             child = child.getNextSibling();
131         }
132 
133         return currentNode;
134     }
135 
136 }