1 /////////////////////////////////////////////////////////////////////////////////////////////// 2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules. 3 // Copyright (C) 2001-2025 the original author or authors. 4 // 5 // This library is free software; you can redistribute it and/or 6 // modify it under the terms of the GNU Lesser General Public 7 // License as published by the Free Software Foundation; either 8 // version 2.1 of the License, or (at your option) any later version. 9 // 10 // This library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 // Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public 16 // License along with this library; if not, write to the Free Software 17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 /////////////////////////////////////////////////////////////////////////////////////////////// 19 20 package com.puppycrawl.tools.checkstyle.checks.coding; 21 22 import com.puppycrawl.tools.checkstyle.StatelessCheck; 23 import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 24 import com.puppycrawl.tools.checkstyle.api.DetailAST; 25 import com.puppycrawl.tools.checkstyle.api.TokenTypes; 26 27 /** 28 * <div> 29 * Check that the {@code default} is after all the cases in a {@code switch} statement. 30 * </div> 31 * 32 * <p> 33 * Rationale: Java allows {@code default} anywhere within the 34 * {@code switch} statement. But it is more readable if it comes after the last {@code case}. 35 * </p> 36 * 37 * @since 3.4 38 */ 39 @StatelessCheck 40 public class DefaultComesLastCheck extends AbstractCheck { 41 42 /** 43 * A key is pointing to the warning message text in "messages.properties" 44 * file. 45 */ 46 public static final String MSG_KEY = "default.comes.last"; 47 48 /** 49 * A key is pointing to the warning message text in "messages.properties" 50 * file. 51 */ 52 public static final String MSG_KEY_SKIP_IF_LAST_AND_SHARED_WITH_CASE = 53 "default.comes.last.in.casegroup"; 54 55 /** Control whether to allow {@code default} along with {@code case} if they are not last. */ 56 private boolean skipIfLastAndSharedWithCase; 57 58 @Override 59 public int[] getAcceptableTokens() { 60 return getRequiredTokens(); 61 } 62 63 @Override 64 public int[] getDefaultTokens() { 65 return getRequiredTokens(); 66 } 67 68 @Override 69 public int[] getRequiredTokens() { 70 return new int[] { 71 TokenTypes.LITERAL_DEFAULT, 72 }; 73 } 74 75 /** 76 * Setter to control whether to allow {@code default} along with 77 * {@code case} if they are not last. 78 * 79 * @param newValue whether to ignore checking. 80 * @since 7.7 81 */ 82 public void setSkipIfLastAndSharedWithCase(boolean newValue) { 83 skipIfLastAndSharedWithCase = newValue; 84 } 85 86 @Override 87 public void visitToken(DetailAST ast) { 88 final DetailAST defaultGroupAST = ast.getParent(); 89 90 // Switch rules are not subject to fall through. 91 final boolean isSwitchRule = defaultGroupAST.getType() == TokenTypes.SWITCH_RULE; 92 93 if (skipIfLastAndSharedWithCase && !isSwitchRule) { 94 if (isNextSiblingOf(ast, TokenTypes.LITERAL_CASE)) { 95 log(ast, MSG_KEY_SKIP_IF_LAST_AND_SHARED_WITH_CASE); 96 } 97 else if (ast.getPreviousSibling() == null 98 && isNextSiblingOf(defaultGroupAST, 99 TokenTypes.CASE_GROUP)) { 100 log(ast, MSG_KEY); 101 } 102 } 103 else if (isNextSiblingOf(defaultGroupAST, 104 TokenTypes.CASE_GROUP) 105 || isNextSiblingOf(defaultGroupAST, 106 TokenTypes.SWITCH_RULE)) { 107 log(ast, MSG_KEY); 108 } 109 } 110 111 /** 112 * Return true only if passed tokenType in argument is found or returns false. 113 * 114 * @param ast root node. 115 * @param tokenType tokentype to be processed. 116 * @return true if desired token is found or else false. 117 */ 118 private static boolean isNextSiblingOf(DetailAST ast, int tokenType) { 119 return ast.getNextSibling() != null && ast.getNextSibling().getType() == tokenType; 120 } 121 122 }