001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2026 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.api; 021 022import java.util.Collections; 023import java.util.HashSet; 024import java.util.Set; 025import java.util.SortedSet; 026import java.util.TreeSet; 027 028import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 029 030/** 031 * The base class for checks. 032 * 033 * @see <a href="{@docRoot}/../writingchecks.html" target="_top">Writing 034 * your own checks</a> 035 * @noinspection NoopMethodInAbstractClass 036 * @noinspectionreason NoopMethodInAbstractClass - we allow each check to 037 * define these methods, as needed. They should be overridden only 038 * by demand in subclasses 039 */ 040public abstract class AbstractCheck extends AbstractViolationReporter { 041 042 /** 043 * The check context. 044 * 045 * @noinspection ThreadLocalNotStaticFinal 046 * @noinspectionreason ThreadLocalNotStaticFinal - static context 047 * is problematic for multithreading 048 */ 049 private final ThreadLocal<FileContext> context = ThreadLocal.withInitial(FileContext::new); 050 051 /** The tokens the check is interested in. */ 052 private final Set<String> tokens = new HashSet<>(); 053 054 /** 055 * The tab width for column reporting. Default is uninitialized as the value is inherited from 056 * the parent module. 057 */ 058 private int tabWidth; 059 060 /** 061 * Creates a new {@code AbstractCheck} instance. 062 */ 063 protected AbstractCheck() { 064 // no code by default 065 } 066 067 /** 068 * Returns the default token a check is interested in. Only used if the 069 * configuration for a check does not define the tokens. 070 * 071 * @return the default tokens 072 * @see TokenTypes 073 */ 074 public abstract int[] getDefaultTokens(); 075 076 /** 077 * The configurable token set. 078 * Used to protect Checks against malicious users who specify an 079 * unacceptable token set in the configuration file. 080 * The default implementation returns the check's default tokens. 081 * 082 * @return the token set this check is designed for. 083 * @see TokenTypes 084 */ 085 public abstract int[] getAcceptableTokens(); 086 087 /** 088 * The tokens that this check must be registered for. 089 * 090 * @return the token set this must be registered for. 091 * @see TokenTypes 092 */ 093 public abstract int[] getRequiredTokens(); 094 095 /** 096 * Whether comment nodes are required or not. 097 * 098 * @return false as a default value. 099 */ 100 public boolean isCommentNodesRequired() { 101 return false; 102 } 103 104 /** 105 * Adds a set of tokens the check is interested in. 106 * 107 * @param strRep the string representation of the tokens interested in 108 * @noinspection WeakerAccess 109 * @noinspectionreason WeakerAccess - we avoid 'protected' when possible 110 */ 111 public final void setTokens(String... strRep) { 112 Collections.addAll(tokens, strRep); 113 } 114 115 /** 116 * Returns the tokens registered for the check. 117 * 118 * @return the set of token names 119 */ 120 public final Set<String> getTokenNames() { 121 return Collections.unmodifiableSet(tokens); 122 } 123 124 /** 125 * Returns the sorted set of {@link Violation}. 126 * 127 * @return the sorted set of {@link Violation}. 128 */ 129 public SortedSet<Violation> getViolations() { 130 return new TreeSet<>(context.get().violations); 131 } 132 133 /** 134 * Clears the sorted set of {@link Violation} of the check. 135 */ 136 public final void clearViolations() { 137 context.get().violations.clear(); 138 } 139 140 /** 141 * Initialize the check. This is the time to verify that the check has 142 * everything required to perform its job. 143 */ 144 public void init() { 145 // No code by default, should be overridden only by demand at subclasses 146 } 147 148 /** 149 * Destroy the check. It is being retired from service. 150 */ 151 public void destroy() { 152 context.remove(); 153 } 154 155 /** 156 * Called before the starting to process a tree. Ideal place to initialize 157 * information that is to be collected whilst processing a tree. 158 * 159 * @param rootAST the root of the tree 160 */ 161 public void beginTree(DetailAST rootAST) { 162 // No code by default, should be overridden only by demand at subclasses 163 } 164 165 /** 166 * Called after finished processing a tree. Ideal place to report on 167 * information collected whilst processing a tree. 168 * 169 * @param rootAST the root of the tree 170 */ 171 public void finishTree(DetailAST rootAST) { 172 // No code by default, should be overridden only by demand at subclasses 173 } 174 175 /** 176 * Called to process a token. 177 * 178 * @param ast the token to process 179 */ 180 public void visitToken(DetailAST ast) { 181 // No code by default, should be overridden only by demand at subclasses 182 } 183 184 /** 185 * Called after all the child nodes have been process. 186 * 187 * @param ast the token leaving 188 */ 189 public void leaveToken(DetailAST ast) { 190 // No code by default, should be overridden only by demand at subclasses 191 } 192 193 /** 194 * Set the file contents associated with the tree. 195 * 196 * @param contents the manager 197 */ 198 public final void setFileContents(FileContents contents) { 199 context.get().fileContents = contents; 200 } 201 202 /** 203 * Returns the file contents associated with the tree. 204 * 205 * @return the file contents 206 * @deprecated 207 * Usage of this method is no longer accepted. 208 * Please use AST based methods instead. 209 * @noinspection WeakerAccess 210 * @noinspectionreason WeakerAccess - we avoid 'protected' when possible 211 */ 212 @Deprecated(since = "9.3") 213 public final FileContents getFileContents() { 214 return context.get().fileContents; 215 } 216 217 /** 218 * Get tab width to report audit events with. 219 * 220 * @return the tab width to audit events with 221 */ 222 protected final int getTabWidth() { 223 return tabWidth; 224 } 225 226 /** 227 * Set the tab width to report audit events with. 228 * 229 * @param tabWidth an {@code int} value 230 */ 231 public final void setTabWidth(int tabWidth) { 232 this.tabWidth = tabWidth; 233 } 234 235 @Override 236 public final void log(int line, String key, Object... args) { 237 context.get().violations.add( 238 new Violation( 239 line, 240 getMessageBundle(), 241 key, 242 args, 243 getSeverityLevel(), 244 getId(), 245 getClass(), 246 getCustomMessages().get(key))); 247 } 248 249 /** 250 * Helper method to log a Violation. 251 * 252 * @param ast a node to get line id column numbers associated 253 * with the violation 254 * @param key key to locale violation format 255 * @param args arguments to format 256 */ 257 public final void log(DetailAST ast, String key, Object... args) { 258 // CommonUtil.lengthExpandedTabs returns column number considering tabulation 259 // characters, it takes line from the file by line number, ast column number and tab 260 // width as arguments. Returned value is 0-based, but user must see column number starting 261 // from 1, that is why result of the method CommonUtil.lengthExpandedTabs 262 // is increased by one. 263 264 final int col = 1 + CommonUtil.lengthExpandedTabs( 265 getLines()[ast.getLineNo() - 1], ast.getColumnNo(), tabWidth); 266 context.get().violations.add( 267 new Violation( 268 ast.getLineNo(), 269 col, 270 ast.getColumnNo(), 271 ast.getType(), 272 getMessageBundle(), 273 key, 274 args, 275 getSeverityLevel(), 276 getId(), 277 getClass(), 278 getCustomMessages().get(key))); 279 } 280 281 @Override 282 public final void log(int lineNo, int colNo, String key, 283 Object... args) { 284 final int col = 1 + CommonUtil.lengthExpandedTabs( 285 getLines()[lineNo - 1], colNo, tabWidth); 286 context.get().violations.add( 287 new Violation( 288 lineNo, 289 col, 290 getMessageBundle(), 291 key, 292 args, 293 getSeverityLevel(), 294 getId(), 295 getClass(), 296 getCustomMessages().get(key))); 297 } 298 299 /** 300 * Returns the lines associated with the tree. 301 * 302 * @return the file contents 303 */ 304 public final String[] getLines() { 305 return context.get().fileContents.getLines(); 306 } 307 308 /** 309 * Returns the line associated with the tree. 310 * 311 * @param index index of the line 312 * @return the line from the file contents 313 */ 314 public final String getLine(int index) { 315 return context.get().fileContents.getLine(index); 316 } 317 318 /** 319 * Returns full path to the file. 320 * 321 * @return full path to file. 322 */ 323 public final String getFilePath() { 324 return context.get().fileContents.getFileName(); 325 } 326 327 /** 328 * Returns code point representation of file text from given line number. 329 * 330 * @param index index of the line 331 * @return the array of Unicode code points 332 */ 333 public final int[] getLineCodePoints(int index) { 334 return getLine(index).codePoints().toArray(); 335 } 336 337 /** 338 * The actual context holder. 339 */ 340 private static final class FileContext { 341 /** The sorted set for collecting violations. */ 342 private final SortedSet<Violation> violations = new TreeSet<>(); 343 344 /** The current file contents. */ 345 private FileContents fileContents; 346 347 /** 348 * Creates a new {@code FileContext} instance. 349 */ 350 private FileContext() { 351 // no code by default 352 } 353 } 354 355}