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