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.whitespace; 21 22 import java.io.File; 23 24 import com.puppycrawl.tools.checkstyle.StatelessCheck; 25 import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck; 26 import com.puppycrawl.tools.checkstyle.api.FileText; 27 28 /** 29 * <div> 30 * Checks that there are no tab characters ({@code '\t'}) in the source code. 31 * </div> 32 * 33 * <p> 34 * Rationale: 35 * </p> 36 * <ul> 37 * <li> 38 * Developers should not need to configure the tab width of their text editors in order 39 * to be able to read source code. 40 * </li> 41 * <li> 42 * From the Apache jakarta coding standards: In a distributed development environment, 43 * when the commit messages get sent to a mailing list, they are almost impossible to 44 * read if you use tabs. 45 * </li> 46 * </ul> 47 * 48 * <p> 49 * When the {@code FileTabCharacter} check is used with the default configuration, 50 * only the first instance of a tab character is reported. 51 * </p> 52 * 53 * <ul> 54 * <li> 55 * Property {@code eachLine} - Control whether to report on each line containing a tab, 56 * or just the first instance. 57 * Type is {@code boolean}. 58 * Default value is {@code false}. 59 * </li> 60 * <li> 61 * Property {@code fileExtensions} - Specify the file extensions of the files to process. 62 * Type is {@code java.lang.String[]}. 63 * Default value is {@code ""}. 64 * </li> 65 * </ul> 66 * 67 * <p> 68 * Parent is {@code com.puppycrawl.tools.checkstyle.Checker} 69 * </p> 70 * 71 * <p> 72 * Violation Message Keys: 73 * </p> 74 * <ul> 75 * <li> 76 * {@code containsTab} 77 * </li> 78 * <li> 79 * {@code file.containsTab} 80 * </li> 81 * </ul> 82 * 83 * @since 5.0 84 */ 85 @StatelessCheck 86 public class FileTabCharacterCheck extends AbstractFileSetCheck { 87 88 /** 89 * A key is pointing to the warning message text in "messages.properties" 90 * file. 91 */ 92 public static final String MSG_CONTAINS_TAB = "containsTab"; 93 94 /** 95 * A key is pointing to the warning message text in "messages.properties" 96 * file. 97 */ 98 public static final String MSG_FILE_CONTAINS_TAB = "file.containsTab"; 99 100 /** Control whether to report on each line containing a tab, or just the first instance. */ 101 private boolean eachLine; 102 103 @Override 104 protected void processFiltered(File file, FileText fileText) { 105 int lineNum = 0; 106 for (int index = 0; index < fileText.size(); index++) { 107 final String line = fileText.get(index); 108 lineNum++; 109 final int tabPosition = line.indexOf('\t'); 110 if (tabPosition != -1) { 111 if (eachLine) { 112 log(lineNum, tabPosition, MSG_CONTAINS_TAB); 113 } 114 else { 115 log(lineNum, tabPosition, MSG_FILE_CONTAINS_TAB); 116 break; 117 } 118 } 119 } 120 } 121 122 /** 123 * Setter to control whether to report on each line containing a tab, or just the first 124 * instance. 125 * 126 * @param eachLine Whether report on each line containing a tab. 127 * @since 5.0 128 */ 129 public void setEachLine(boolean eachLine) { 130 this.eachLine = eachLine; 131 } 132 133 }