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