View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 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.TokenUtil;
27  
28  /**
29   * <div>
30   * Checks that array initialization contains a trailing comma.
31   * </div>
32   *
33   * <pre>
34   * int[] a = new int[]
35   * {
36   *   1,
37   *   2,
38   *   3,
39   * };
40   * </pre>
41   *
42   * <p>
43   * By default, the check demands a comma at the end if neither left nor right curly braces
44   * are on the same line as the last element of the array.
45   * </p>
46   * <pre>
47   * return new int[] { 0 };
48   * return new int[] { 0
49   *   };
50   * return new int[] {
51   *   0 };
52   * </pre>
53   *
54   * <p>
55   * Rationale: Putting this comma in makes it easier to change the
56   * order of the elements or add new elements on the end. Main benefit of a trailing
57   * comma is that when you add new entry to an array, no surrounding lines are changed.
58   * </p>
59   * <pre>
60   * {
61   *   100000000000000000000,
62   *   200000000000000000000, // OK
63   * }
64   *
65   * {
66   *   100000000000000000000,
67   *   200000000000000000000,
68   *   300000000000000000000,  // Just this line added, no other changes
69   * }
70   * </pre>
71   *
72   * <p>
73   * If closing brace is on the same line as trailing comma, this benefit is gone
74   * (as the check does not demand a certain location of curly braces the following
75   * two cases will not produce a violation):
76   * </p>
77   * <pre>
78   * {100000000000000000000,
79   *  200000000000000000000,} // Trailing comma not needed, line needs to be modified anyway
80   *
81   * {100000000000000000000,
82   *  200000000000000000000, // Modified line
83   *  300000000000000000000,} // Added line
84   * </pre>
85   *
86   * <p>
87   * If opening brace is on the same line as trailing comma there's also (more arguable) problem:
88   * </p>
89   * <pre>
90   * {100000000000000000000, // Line cannot be just duplicated to slightly modify entry
91   * }
92   *
93   * {100000000000000000000,
94   *  100000000000000000001, // More work needed to duplicate
95   * }
96   * </pre>
97   * <ul>
98   * <li>
99   * Property {@code alwaysDemandTrailingComma} - Control whether to always check for a trailing
100  * comma, even when an array is inline.
101  * Type is {@code boolean}.
102  * Default value is {@code false}.
103  * </li>
104  * </ul>
105  *
106  * <p>
107  * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
108  * </p>
109  *
110  * <p>
111  * Violation Message Keys:
112  * </p>
113  * <ul>
114  * <li>
115  * {@code array.trailing.comma}
116  * </li>
117  * </ul>
118  *
119  * @since 3.2
120  */
121 @StatelessCheck
122 public class ArrayTrailingCommaCheck extends AbstractCheck {
123 
124     /**
125      * A key is pointing to the warning message text in "messages.properties"
126      * file.
127      */
128     public static final String MSG_KEY = "array.trailing.comma";
129 
130     /**
131      * Control whether to always check for a trailing comma, even when an array is inline.
132      */
133     private boolean alwaysDemandTrailingComma;
134 
135     /**
136      * Setter to control whether to always check for a trailing comma, even when an array is inline.
137      *
138      * @param alwaysDemandTrailingComma whether to always check for a trailing comma.
139      * @since 8.33
140      */
141     public void setAlwaysDemandTrailingComma(boolean alwaysDemandTrailingComma) {
142         this.alwaysDemandTrailingComma = alwaysDemandTrailingComma;
143     }
144 
145     @Override
146     public int[] getDefaultTokens() {
147         return getRequiredTokens();
148     }
149 
150     @Override
151     public int[] getAcceptableTokens() {
152         return getRequiredTokens();
153     }
154 
155     @Override
156     public int[] getRequiredTokens() {
157         return new int[] {TokenTypes.ARRAY_INIT};
158     }
159 
160     @Override
161     public void visitToken(DetailAST arrayInit) {
162         final DetailAST rcurly = arrayInit.findFirstToken(TokenTypes.RCURLY);
163         final DetailAST previousSibling = rcurly.getPreviousSibling();
164 
165         if (arrayInit.getChildCount() != 1
166                 && (alwaysDemandTrailingComma
167                     || !TokenUtil.areOnSameLine(rcurly, previousSibling)
168                         && !TokenUtil.areOnSameLine(arrayInit, previousSibling))
169                 && previousSibling.getType() != TokenTypes.COMMA) {
170             log(previousSibling, MSG_KEY);
171         }
172     }
173 
174 }