1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2026 the original author or authors.
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ///////////////////////////////////////////////////////////////////////////////////////////////
19
20 package com.puppycrawl.tools.checkstyle.api;
21
22 import java.util.Collections;
23 import java.util.HashSet;
24 import java.util.Set;
25 import java.util.SortedSet;
26 import java.util.TreeSet;
27
28 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
29
30 /**
31 * The base class for checks.
32 *
33 * @see <a href="{@docRoot}/../writing-checks.html" target="_top">Writing your own checks</a>
34 * @noinspection NoopMethodInAbstractClass
35 * @noinspectionreason NoopMethodInAbstractClass - we allow each check to
36 * define these methods, as needed. They should be overridden only by demand in subclasses
37 */
38 public abstract class AbstractCheck extends AbstractViolationReporter {
39
40 /**
41 * The check context.
42 *
43 * @noinspection ThreadLocalNotStaticFinal
44 * @noinspectionreason ThreadLocalNotStaticFinal - static context
45 * is problematic for multithreading
46 */
47 private final ThreadLocal<FileContext> context = ThreadLocal.withInitial(FileContext::new);
48
49 /** The tokens the check is interested in. */
50 private final Set<String> tokens = new HashSet<>();
51
52 /**
53 * The tab width for column reporting. Default is uninitialized as the value is inherited from
54 * the parent module.
55 */
56 private int tabWidth;
57
58 /**
59 * Creates a new {@code AbstractCheck} instance.
60 */
61 protected AbstractCheck() {
62 // no code by default
63 }
64
65 /**
66 * Returns the default token a check is interested in. Only used if the
67 * configuration for a check does not define the tokens.
68 *
69 * @return the default tokens
70 * @see TokenTypes
71 */
72 public abstract int[] getDefaultTokens();
73
74 /**
75 * The configurable token set.
76 * Used to protect Checks against malicious users who specify an
77 * unacceptable token set in the configuration file.
78 * The default implementation returns the check's default tokens.
79 *
80 * @return the token set this check is designed for.
81 * @see TokenTypes
82 */
83 public abstract int[] getAcceptableTokens();
84
85 /**
86 * The tokens that this check must be registered for.
87 *
88 * @return the token set this must be registered for.
89 * @see TokenTypes
90 */
91 public abstract int[] getRequiredTokens();
92
93 /**
94 * Whether comment nodes are required or not.
95 *
96 * @return false as a default value.
97 */
98 public boolean isCommentNodesRequired() {
99 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 }