1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2025 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 * <div class="wrapper"><pre class="prettyprint"><code class="language-java">
34 * int[] a = new int[]
35 * {
36 * 1,
37 * 2,
38 * 3,
39 * };
40 * </code></pre></div>
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 * <div class="wrapper"><pre class="prettyprint"><code class="language-java">
47 * return new int[] { 0 };
48 * return new int[] { 0
49 * };
50 * return new int[] {
51 * 0 };
52 * </code></pre></div>
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 * <div class="wrapper"><pre class="prettyprint"><code class="language-java">
60 * {
61 * 100000000000000000000,
62 * 200000000000000000000, // OK
63 * }
64 *
65 * {
66 * 100000000000000000000,
67 * 200000000000000000000,
68 * 300000000000000000000, // Just this line added, no other changes
69 * }
70 * </code></pre></div>
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 * <div class="wrapper"><pre class="prettyprint"><code class="language-java">
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 * </code></pre></div>
85 *
86 * <p>
87 * If opening brace is on the same line as trailing comma there's also (more arguable) problem:
88 * </p>
89 * <div class="wrapper"><pre class="prettyprint"><code class="language-java">
90 * {100000000000000000000, // Line cannot be just duplicated to slightly modify entry
91 * }
92 *
93 * {100000000000000000000,
94 * 100000000000000000001, // More work needed to duplicate
95 * }
96 * </code></pre></div>
97 *
98 * @since 3.2
99 */
100 @StatelessCheck
101 public class ArrayTrailingCommaCheck extends AbstractCheck {
102
103 /**
104 * A key is pointing to the warning message text in "messages.properties"
105 * file.
106 */
107 public static final String MSG_KEY = "array.trailing.comma";
108
109 /**
110 * Control whether to always check for a trailing comma, even when an array is inline.
111 */
112 private boolean alwaysDemandTrailingComma;
113
114 /**
115 * Setter to control whether to always check for a trailing comma, even when an array is inline.
116 *
117 * @param alwaysDemandTrailingComma whether to always check for a trailing comma.
118 * @since 8.33
119 */
120 public void setAlwaysDemandTrailingComma(boolean alwaysDemandTrailingComma) {
121 this.alwaysDemandTrailingComma = alwaysDemandTrailingComma;
122 }
123
124 @Override
125 public int[] getDefaultTokens() {
126 return getRequiredTokens();
127 }
128
129 @Override
130 public int[] getAcceptableTokens() {
131 return getRequiredTokens();
132 }
133
134 @Override
135 public int[] getRequiredTokens() {
136 return new int[] {TokenTypes.ARRAY_INIT};
137 }
138
139 @Override
140 public void visitToken(DetailAST arrayInit) {
141 final DetailAST rcurly = arrayInit.findFirstToken(TokenTypes.RCURLY);
142 final DetailAST previousSibling = rcurly.getPreviousSibling();
143
144 if (arrayInit.getChildCount() != 1
145 && (alwaysDemandTrailingComma
146 || !TokenUtil.areOnSameLine(rcurly, previousSibling)
147 && !TokenUtil.areOnSameLine(arrayInit, previousSibling))
148 && previousSibling.getType() != TokenTypes.COMMA) {
149 log(previousSibling, MSG_KEY);
150 }
151 }
152
153 }