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 * <p> 030 * Checks that array initialization contains a trailing comma. 031 * </p> 032 * <pre> 033 * int[] a = new int[] 034 * { 035 * 1, 036 * 2, 037 * 3, 038 * }; 039 * </pre> 040 * <p> 041 * By default, the check demands a comma at the end if neither left nor right curly braces 042 * are on the same line as the last element of the array. 043 * </p> 044 * <pre> 045 * return new int[] { 0 }; 046 * return new int[] { 0 047 * }; 048 * return new int[] { 049 * 0 }; 050 * </pre> 051 * <p> 052 * Rationale: Putting this comma in makes it easier to change the 053 * order of the elements or add new elements on the end. Main benefit of a trailing 054 * comma is that when you add new entry to an array, no surrounding lines are changed. 055 * </p> 056 * <pre> 057 * { 058 * 100000000000000000000, 059 * 200000000000000000000, // OK 060 * } 061 * 062 * { 063 * 100000000000000000000, 064 * 200000000000000000000, 065 * 300000000000000000000, // Just this line added, no other changes 066 * } 067 * </pre> 068 * <p> 069 * If closing brace is on the same line as trailing comma, this benefit is gone 070 * (as the check does not demand a certain location of curly braces the following 071 * two cases will not produce a violation): 072 * </p> 073 * <pre> 074 * {100000000000000000000, 075 * 200000000000000000000,} // Trailing comma not needed, line needs to be modified anyway 076 * 077 * {100000000000000000000, 078 * 200000000000000000000, // Modified line 079 * 300000000000000000000,} // Added line 080 * </pre> 081 * <p> 082 * If opening brace is on the same line as trailing comma there's also (more arguable) problem: 083 * </p> 084 * <pre> 085 * {100000000000000000000, // Line cannot be just duplicated to slightly modify entry 086 * } 087 * 088 * {100000000000000000000, 089 * 100000000000000000001, // More work needed to duplicate 090 * } 091 * </pre> 092 * <ul> 093 * <li> 094 * Property {@code alwaysDemandTrailingComma} - Control whether to always check for a trailing 095 * comma, even when an array is inline. 096 * Type is {@code boolean}. 097 * Default value is {@code false}. 098 * </li> 099 * </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 115public 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}