001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files 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.io.File; 023 024import com.puppycrawl.tools.checkstyle.StatelessCheck; 025import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck; 026import com.puppycrawl.tools.checkstyle.api.FileText; 027 028/** 029 * <p> 030 * Checks that there are no tab characters ({@code '\t'}) in the source code. 031 * </p> 032 * <p> 033 * Rationale: 034 * </p> 035 * <ul> 036 * <li> 037 * Developers should not need to configure the tab width of their text editors in order 038 * to be able to read source code. 039 * </li> 040 * <li> 041 * From the Apache jakarta coding standards: In a distributed development environment, 042 * when the commit messages get sent to a mailing list, they are almost impossible to 043 * read if you use tabs. 044 * </li> 045 * </ul> 046 * <ul> 047 * <li> 048 * Property {@code eachLine} - Control whether to report on each line containing a tab, 049 * or just the first instance. 050 * Type is {@code boolean}. 051 * Default value is {@code false}. 052 * </li> 053 * <li> 054 * Property {@code fileExtensions} - Specify file type extension of files to process. 055 * Type is {@code java.lang.String[]}. 056 * Default value is {@code ""}. 057 * </li> 058 * </ul> 059 * <p> 060 * To configure the check to report only the first instance in each file: 061 * </p> 062 * <pre> 063 * <module name="FileTabCharacter"/> 064 * </pre> 065 * <p> 066 * Example - Test.java: 067 * </p> 068 * <pre> 069 * public class Test { 070 * int a; // violation, indented using tab 071 * 072 * public void foo (int arg) { // OK, indented using tab, only first occurrence in file reported 073 * a = arg; // OK, indented using spaces 074 * } // OK, indented using spaces 075 * } 076 * </pre> 077 * <p> 078 * To configure the check to report each instance in each file: 079 * </p> 080 * <pre> 081 * <module name="FileTabCharacter"> 082 * <property name="eachLine" value="true"/> 083 * </module> 084 * </pre> 085 * <p> 086 * Example - Test.java: 087 * </p> 088 * <pre> 089 * public class Test { 090 * int a; // violation, indented using tab 091 * 092 * public void foo (int arg) { // violation, indented using tab 093 * a = arg; // OK, indented using spaces 094 * } // OK, indented using spaces 095 * } 096 * </pre> 097 * <p> 098 * To configure the check to report instances on only certain file types: 099 * </p> 100 * <pre> 101 * <module name="FileTabCharacter"> 102 * <property name="fileExtensions" value="java, xml"/> 103 * </module> 104 * </pre> 105 * <p> 106 * Example - Test.java: 107 * </p> 108 * <pre> 109 * public class Test { 110 * int a; // violation, indented using tab 111 * 112 * public void foo (int arg) { // OK, indented using tab, only first occurrence in file reported 113 * a = arg; // OK, indented using spaces 114 * } // OK, indented using spaces 115 * } 116 * </pre> 117 * <p> 118 * Example - Test.xml: 119 * </p> 120 * <pre> 121 * <?xml version="1.0" encoding="UTF-8" ?> 122 * <UserAccount> 123 * <FirstName>John</FirstName> <!-- violation, indented using tab --> 124 * <LastName>Doe</LastName> <!-- only first occurrence in file reported --> 125 * </UserAccount> 126 * </pre> 127 * <p> 128 * Example - Test.html: 129 * </p> 130 * <pre> 131 * <head> 132 * <title>Page Title</title> <!-- no check performed, html file extension --> 133 * </head> <!-- not specified in check config --> 134 * <body> 135 * <p>This is a simple html document.</p> 136 * </body> 137 * </pre> 138 * <p> 139 * Parent is {@code com.puppycrawl.tools.checkstyle.Checker} 140 * </p> 141 * <p> 142 * Violation Message Keys: 143 * </p> 144 * <ul> 145 * <li> 146 * {@code containsTab} 147 * </li> 148 * <li> 149 * {@code file.containsTab} 150 * </li> 151 * </ul> 152 * 153 * @since 5.0 154 */ 155@StatelessCheck 156public class FileTabCharacterCheck extends AbstractFileSetCheck { 157 158 /** 159 * A key is pointing to the warning message text in "messages.properties" 160 * file. 161 */ 162 public static final String MSG_CONTAINS_TAB = "containsTab"; 163 164 /** 165 * A key is pointing to the warning message text in "messages.properties" 166 * file. 167 */ 168 public static final String MSG_FILE_CONTAINS_TAB = "file.containsTab"; 169 170 /** Control whether to report on each line containing a tab, or just the first instance. */ 171 private boolean eachLine; 172 173 @Override 174 protected void processFiltered(File file, FileText fileText) { 175 int lineNum = 0; 176 for (int index = 0; index < fileText.size(); index++) { 177 final String line = fileText.get(index); 178 lineNum++; 179 final int tabPosition = line.indexOf('\t'); 180 if (tabPosition != -1) { 181 if (eachLine) { 182 log(lineNum, tabPosition, MSG_CONTAINS_TAB); 183 } 184 else { 185 log(lineNum, tabPosition, MSG_FILE_CONTAINS_TAB); 186 break; 187 } 188 } 189 } 190 } 191 192 /** 193 * Setter to control whether to report on each line containing a tab, or just the first 194 * instance. 195 * 196 * @param eachLine Whether report on each line containing a tab. 197 */ 198 public void setEachLine(boolean eachLine) { 199 this.eachLine = eachLine; 200 } 201 202}