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