001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2022 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.whitespace; 021 022import java.util.Locale; 023 024import com.puppycrawl.tools.checkstyle.StatelessCheck; 025import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 026import com.puppycrawl.tools.checkstyle.api.DetailAST; 027import com.puppycrawl.tools.checkstyle.api.TokenTypes; 028 029/** 030 * <p> 031 * Checks the padding of an empty for iterator; that is whether a white 032 * space is required at an empty for iterator, or such white space is 033 * forbidden. No check occurs if there is a line wrap at the iterator, as in 034 * </p> 035 * <pre> 036 * for (Iterator foo = very.long.line.iterator(); 037 * foo.hasNext(); 038 * ) 039 * </pre> 040 * <ul> 041 * <li> 042 * Property {@code option} - Specify policy on how to pad an empty for iterator. 043 * Type is {@code com.puppycrawl.tools.checkstyle.checks.whitespace.PadOption}. 044 * Default value is {@code nospace}. 045 * </li> 046 * </ul> 047 * <p> 048 * To configure the check: 049 * </p> 050 * <pre> 051 * <module name="EmptyForIteratorPad"/> 052 * </pre> 053 * <p> 054 * Example: 055 * </p> 056 * <pre> 057 * for (Iterator it = map.entrySet().iterator(); it.hasNext();); // ok 058 * for (Iterator it = map.entrySet().iterator(); it.hasNext(); ); // violation since whitespace 059 * //after semicolon 060 * 061 * for (Iterator foo = very.long.line.iterator(); 062 * foo.hasNext(); 063 * ); // ok 064 * </pre> 065 * <p> 066 * To configure the check to require white space at an empty for iterator: 067 * </p> 068 * <pre> 069 * <module name="EmptyForIteratorPad"> 070 * <property name="option" value="space"/> 071 * </module> 072 * </pre> 073 * <p> 074 * Example: 075 * </p> 076 * <pre> 077 * for (Iterator it = map.entrySet().iterator(); it.hasNext();); // violation as there is no 078 * // whitespace after semicolon 079 * 080 * for (Iterator it = map.entrySet().iterator(); it.hasNext(); ); // ok 081 * 082 * for (Iterator foo = very.long.line.iterator(); 083 * foo.hasNext(); 084 * ); // violation as there is no whitespace after semicolon 085 * </pre> 086 * <p> 087 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 088 * </p> 089 * <p> 090 * Violation Message Keys: 091 * </p> 092 * <ul> 093 * <li> 094 * {@code ws.followed} 095 * </li> 096 * <li> 097 * {@code ws.notFollowed} 098 * </li> 099 * </ul> 100 * 101 * @since 3.0 102 */ 103@StatelessCheck 104public class EmptyForIteratorPadCheck 105 extends AbstractCheck { 106 107 /** 108 * A key is pointing to the warning message text in "messages.properties" 109 * file. 110 */ 111 public static final String MSG_WS_FOLLOWED = "ws.followed"; 112 113 /** 114 * A key is pointing to the warning message text in "messages.properties" 115 * file. 116 */ 117 public static final String MSG_WS_NOT_FOLLOWED = "ws.notFollowed"; 118 119 /** Semicolon literal. */ 120 private static final String SEMICOLON = ";"; 121 122 /** Specify policy on how to pad an empty for iterator. */ 123 private PadOption option = PadOption.NOSPACE; 124 125 /** 126 * Setter to specify policy on how to pad an empty for iterator. 127 * 128 * @param optionStr string to decode option from 129 * @throws IllegalArgumentException if unable to decode 130 */ 131 public void setOption(String optionStr) { 132 option = PadOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH)); 133 } 134 135 @Override 136 public int[] getDefaultTokens() { 137 return getRequiredTokens(); 138 } 139 140 @Override 141 public int[] getAcceptableTokens() { 142 return getRequiredTokens(); 143 } 144 145 @Override 146 public int[] getRequiredTokens() { 147 return new int[] {TokenTypes.FOR_ITERATOR}; 148 } 149 150 @Override 151 public void visitToken(DetailAST ast) { 152 if (!ast.hasChildren()) { 153 // empty for iterator. test pad after semi. 154 final DetailAST semi = ast.getPreviousSibling(); 155 final String line = getLines()[semi.getLineNo() - 1]; 156 final int after = semi.getColumnNo() + 1; 157 // don't check if at end of line 158 if (after < line.length()) { 159 if (option == PadOption.NOSPACE 160 && Character.isWhitespace(line.charAt(after))) { 161 log(ast, MSG_WS_FOLLOWED, SEMICOLON); 162 } 163 else if (option == PadOption.SPACE 164 && !Character.isWhitespace(line.charAt(after))) { 165 log(ast, MSG_WS_NOT_FOLLOWED, SEMICOLON); 166 } 167 } 168 } 169 } 170 171}