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