001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2024 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; 026import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 027 028/** 029 * <div> 030 * Checks that array initialization contains a trailing comma. 031 * </div> 032 * 033 * <pre> 034 * int[] a = new int[] 035 * { 036 * 1, 037 * 2, 038 * 3, 039 * }; 040 * </pre> 041 * 042 * <p> 043 * By default, the check demands a comma at the end if neither left nor right curly braces 044 * are on the same line as the last element of the array. 045 * </p> 046 * <pre> 047 * return new int[] { 0 }; 048 * return new int[] { 0 049 * }; 050 * return new int[] { 051 * 0 }; 052 * </pre> 053 * 054 * <p> 055 * Rationale: Putting this comma in makes it easier to change the 056 * order of the elements or add new elements on the end. Main benefit of a trailing 057 * comma is that when you add new entry to an array, no surrounding lines are changed. 058 * </p> 059 * <pre> 060 * { 061 * 100000000000000000000, 062 * 200000000000000000000, // OK 063 * } 064 * 065 * { 066 * 100000000000000000000, 067 * 200000000000000000000, 068 * 300000000000000000000, // Just this line added, no other changes 069 * } 070 * </pre> 071 * 072 * <p> 073 * If closing brace is on the same line as trailing comma, this benefit is gone 074 * (as the check does not demand a certain location of curly braces the following 075 * two cases will not produce a violation): 076 * </p> 077 * <pre> 078 * {100000000000000000000, 079 * 200000000000000000000,} // Trailing comma not needed, line needs to be modified anyway 080 * 081 * {100000000000000000000, 082 * 200000000000000000000, // Modified line 083 * 300000000000000000000,} // Added line 084 * </pre> 085 * 086 * <p> 087 * If opening brace is on the same line as trailing comma there's also (more arguable) problem: 088 * </p> 089 * <pre> 090 * {100000000000000000000, // Line cannot be just duplicated to slightly modify entry 091 * } 092 * 093 * {100000000000000000000, 094 * 100000000000000000001, // More work needed to duplicate 095 * } 096 * </pre> 097 * <ul> 098 * <li> 099 * Property {@code alwaysDemandTrailingComma} - Control whether to always check for a trailing 100 * comma, even when an array is inline. 101 * Type is {@code boolean}. 102 * Default value is {@code false}. 103 * </li> 104 * </ul> 105 * 106 * <p> 107 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 108 * </p> 109 * 110 * <p> 111 * Violation Message Keys: 112 * </p> 113 * <ul> 114 * <li> 115 * {@code array.trailing.comma} 116 * </li> 117 * </ul> 118 * 119 * @since 3.2 120 */ 121@StatelessCheck 122public class ArrayTrailingCommaCheck extends AbstractCheck { 123 124 /** 125 * A key is pointing to the warning message text in "messages.properties" 126 * file. 127 */ 128 public static final String MSG_KEY = "array.trailing.comma"; 129 130 /** 131 * Control whether to always check for a trailing comma, even when an array is inline. 132 */ 133 private boolean alwaysDemandTrailingComma; 134 135 /** 136 * Setter to control whether to always check for a trailing comma, even when an array is inline. 137 * 138 * @param alwaysDemandTrailingComma whether to always check for a trailing comma. 139 * @since 8.33 140 */ 141 public void setAlwaysDemandTrailingComma(boolean alwaysDemandTrailingComma) { 142 this.alwaysDemandTrailingComma = alwaysDemandTrailingComma; 143 } 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 && (alwaysDemandTrailingComma 167 || !TokenUtil.areOnSameLine(rcurly, previousSibling) 168 && !TokenUtil.areOnSameLine(arrayInit, previousSibling)) 169 && previousSibling.getType() != TokenTypes.COMMA) { 170 log(previousSibling, MSG_KEY); 171 } 172 } 173 174}