001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2017 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.api.AbstractCheck;
023import com.puppycrawl.tools.checkstyle.api.DetailAST;
024import com.puppycrawl.tools.checkstyle.api.TokenTypes;
025
026/**
027 * <p>
028 * Checks if array initialization contains optional trailing comma.
029 * </p>
030 * <p>
031 * Rationale: Putting this comma in make is easier to change the
032 * order of the elements or add new elements on the end. Main benefit of a trailing
033 * comma is that when you add new entry to an array, no surrounding lines are changed.
034 * </p>
035 * <p>
036 * The check demands a comma at the end if neither left nor right curly braces
037 * are on the same line as the last element of the array.
038 * </p>
039 * <pre>
040 * return new int[] { 0 };
041 * return new int[] { 0
042 *     };
043 * return new int[] {
044 *     0 };
045 * </pre>
046 * <pre>
047 * {
048 *     100000000000000000000,
049 *     200000000000000000000, // OK
050 * }
051 *
052 * {
053 *     100000000000000000000,
054 *     200000000000000000000,
055 *     300000000000000000000,  // Just this line added, no other changes
056 * }
057 * </pre>
058 * <p>
059 * If closing brace is on the same line as training comma, this benefit is gone
060 * (as the Check does not demand a certain location of curly braces the following
061 * two cases will not produce a violation):
062 * </p>
063 * <pre>
064 * {100000000000000000000,
065 *     200000000000000000000,} // Trailing comma not needed, line needs to be modified anyway
066 *
067 * {100000000000000000000,
068 *     200000000000000000000, // Modified line
069 *     300000000000000000000,} // Added line
070 * </pre>
071 * <p>
072 * If opening brace is on the same line as training comma there's also (more arguable) problem:
073 * </p>
074 * <pre>
075 * {100000000000000000000, // Line cannot be just duplicated to slightly modify entry
076 * }
077 *
078 * {100000000000000000000,
079 *     100000000000000000001, // More work needed to duplicate
080 * }
081 * </pre>
082 * <p>
083 * An example of how to configure the check is:
084 * </p>
085 * <pre>
086 * &lt;module name="ArrayTrailingComma"/&gt;
087 * </pre>
088 * @author o_sukhodolsky
089 */
090public class ArrayTrailingCommaCheck extends AbstractCheck {
091
092    /**
093     * A key is pointing to the warning message text in "messages.properties"
094     * file.
095     */
096    public static final String MSG_KEY = "array.trailing.comma";
097
098    @Override
099    public int[] getDefaultTokens() {
100        return getAcceptableTokens();
101    }
102
103    @Override
104    public int[] getAcceptableTokens() {
105        return new int[] {TokenTypes.ARRAY_INIT};
106    }
107
108    @Override
109    public int[] getRequiredTokens() {
110        return getAcceptableTokens();
111    }
112
113    @Override
114    public void visitToken(DetailAST arrayInit) {
115        final DetailAST rcurly = arrayInit.findFirstToken(TokenTypes.RCURLY);
116        final DetailAST previousSibling = rcurly.getPreviousSibling();
117
118        if (arrayInit.getLineNo() != rcurly.getLineNo()
119                && arrayInit.getChildCount() != 1
120                && rcurly.getLineNo() != previousSibling.getLineNo()
121                && arrayInit.getLineNo() != previousSibling.getLineNo()
122                && previousSibling.getType() != TokenTypes.COMMA) {
123            log(rcurly.getLineNo(), MSG_KEY);
124        }
125    }
126}