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