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