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 }