View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 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  
27  /**
28   * <p>
29   * Checks if array initialization contains optional trailing comma.
30   * </p>
31   * <p>
32   * Rationale: Putting this comma in make is easier to change the
33   * order of the elements or add new elements on the end. Main benefit of a trailing
34   * comma is that when you add new entry to an array, no surrounding lines are changed.
35   * </p>
36   * <p>
37   * The check demands a comma at the end if neither left nor right curly braces
38   * are on the same line as the last element of the array.
39   * </p>
40   * <pre>
41   * return new int[] { 0 };
42   * return new int[] { 0
43   *     };
44   * return new int[] {
45   *     0 };
46   * </pre>
47   * <pre>
48   * {
49   *     100000000000000000000,
50   *     200000000000000000000, // OK
51   * }
52   *
53   * {
54   *     100000000000000000000,
55   *     200000000000000000000,
56   *     300000000000000000000,  // Just this line added, no other changes
57   * }
58   * </pre>
59   * <p>
60   * If closing brace is on the same line as training comma, this benefit is gone
61   * (as the Check does not demand a certain location of curly braces the following
62   * two cases will not produce a violation):
63   * </p>
64   * <pre>
65   * {100000000000000000000,
66   *     200000000000000000000,} // Trailing comma not needed, line needs to be modified anyway
67   *
68   * {100000000000000000000,
69   *     200000000000000000000, // Modified line
70   *     300000000000000000000,} // Added line
71   * </pre>
72   * <p>
73   * If opening brace is on the same line as training comma there's also (more arguable) problem:
74   * </p>
75   * <pre>
76   * {100000000000000000000, // Line cannot be just duplicated to slightly modify entry
77   * }
78   *
79   * {100000000000000000000,
80   *     100000000000000000001, // More work needed to duplicate
81   * }
82   * </pre>
83   * <p>
84   * An example of how to configure the check is:
85   * </p>
86   * <pre>
87   * &lt;module name="ArrayTrailingComma"/&gt;
88   * </pre>
89   * @author o_sukhodolsky
90   */
91  @StatelessCheck
92  public class ArrayTrailingCommaCheck extends AbstractCheck {
93  
94      /**
95       * A key is pointing to the warning message text in "messages.properties"
96       * file.
97       */
98      public static final String MSG_KEY = "array.trailing.comma";
99  
100     @Override
101     public int[] getDefaultTokens() {
102         return getRequiredTokens();
103     }
104 
105     @Override
106     public int[] getAcceptableTokens() {
107         return getRequiredTokens();
108     }
109 
110     @Override
111     public int[] getRequiredTokens() {
112         return new int[] {TokenTypes.ARRAY_INIT};
113     }
114 
115     @Override
116     public void visitToken(DetailAST arrayInit) {
117         final DetailAST rcurly = arrayInit.findFirstToken(TokenTypes.RCURLY);
118         final DetailAST previousSibling = rcurly.getPreviousSibling();
119 
120         if (arrayInit.getLineNo() != rcurly.getLineNo()
121                 && arrayInit.getChildCount() != 1
122                 && rcurly.getLineNo() != previousSibling.getLineNo()
123                 && arrayInit.getLineNo() != previousSibling.getLineNo()
124                 && previousSibling.getType() != TokenTypes.COMMA) {
125             log(rcurly.getLineNo(), MSG_KEY);
126         }
127     }
128 }