001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2025 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}