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   * <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 }