001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.coding;
021
022import com.puppycrawl.tools.checkstyle.StatelessCheck;
023import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
024import com.puppycrawl.tools.checkstyle.api.DetailAST;
025import com.puppycrawl.tools.checkstyle.api.TokenTypes;
026
027/**
028 * <p>
029 * Checks that array initialization contains a trailing comma.
030 * </p>
031 * <pre>
032 * int[] a = new int[]
033 * {
034 *   1,
035 *   2,
036 *   3,
037 * };
038 * </pre>
039 * <p>
040 * The check demands a comma at the end if neither left nor right curly braces
041 * are on the same line as the last element of the array.
042 * </p>
043 * <pre>
044 * return new int[] { 0 };
045 * return new int[] { 0
046 *   };
047 * return new int[] {
048 *   0 };
049 * </pre>
050 * <p>
051 * Rationale: Putting this comma in makes it easier to change the
052 * order of the elements or add new elements on the end. Main benefit of a trailing
053 * comma is that when you add new entry to an array, no surrounding lines are changed.
054 * </p>
055 * <pre>
056 * {
057 *   100000000000000000000,
058 *   200000000000000000000, // OK
059 * }
060 *
061 * {
062 *   100000000000000000000,
063 *   200000000000000000000,
064 *   300000000000000000000,  // Just this line added, no other changes
065 * }
066 * </pre>
067 * <p>
068 * If closing brace is on the same line as training comma, this benefit is gone
069 * (as the check does not demand a certain location of curly braces the following
070 * two cases will not produce a violation):
071 * </p>
072 * <pre>
073 * {100000000000000000000,
074 *  200000000000000000000,} // Trailing comma not needed, line needs to be modified anyway
075 *
076 * {100000000000000000000,
077 *  200000000000000000000, // Modified line
078 *  300000000000000000000,} // Added line
079 * </pre>
080 * <p>
081 * If opening brace is on the same line as training comma there's also (more arguable) problem:
082 * </p>
083 * <pre>
084 * {100000000000000000000, // Line cannot be just duplicated to slightly modify entry
085 * }
086 *
087 * {100000000000000000000,
088 *  100000000000000000001, // More work needed to duplicate
089 * }
090 * </pre>
091 * <p>
092 * To configure the check:
093 * </p>
094 * <pre>
095 * &lt;module name=&quot;ArrayTrailingComma&quot;/&gt;
096 * </pre>
097 * <p>
098 * Which results in the following violations:
099 * </p>
100 * <pre>
101 * int[] numbers = {1, 2, 3};        //no violation
102 * boolean[] bools = {
103 * true,
104 * true,
105 * false
106 * };        //violation
107 *
108 * String[][] text = {{},{},};        //no violation
109 *
110 * double[][] decimals = {
111 * {0.5, 2.3, 1.1,},        //no violation
112 * {1.7, 1.9, 0.6},
113 * {0.8, 7.4, 6.5}
114 * };        // violation as previous line misses a comma
115 *
116 * char[] chars = {'a', 'b', 'c'
117 *   };        / /no violation
118 *
119 * String[] letters = {
120 *   "a", "b", "c"};        // no violation
121 *
122 * int[] a1 = new int[]{
123 *   1,
124 *   2
125 *   ,
126 * };        // no violation
127 *
128 * int[] a2 = new int[]{
129 *   1,
130 *   2
131 *   ,};        // no violation
132 * </pre>
133 *
134 * @since 3.2
135 */
136@StatelessCheck
137public class ArrayTrailingCommaCheck extends AbstractCheck {
138
139    /**
140     * A key is pointing to the warning message text in "messages.properties"
141     * file.
142     */
143    public static final String MSG_KEY = "array.trailing.comma";
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                && rcurly.getLineNo() != previousSibling.getLineNo()
167                && arrayInit.getLineNo() != previousSibling.getLineNo()
168                && previousSibling.getType() != TokenTypes.COMMA) {
169            log(rcurly.getLineNo(), MSG_KEY);
170        }
171    }
172
173}