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}/../writingchecks.html" target="_top">Writing
34 * your own checks</a>
35 * @noinspection NoopMethodInAbstractClass
36 * @noinspectionreason NoopMethodInAbstractClass - we allow each check to
37 * define these methods, as needed. They should be overridden only
38 * by demand in subclasses
39 */
40 public abstract class AbstractCheck extends AbstractViolationReporter {
41
42 /**
43 * The check context.
44 *
45 * @noinspection ThreadLocalNotStaticFinal
46 * @noinspectionreason ThreadLocalNotStaticFinal - static context
47 * is problematic for multithreading
48 */
49 private final ThreadLocal<FileContext> context = ThreadLocal.withInitial(FileContext::new);
50
51 /** The tokens the check is interested in. */
52 private final Set<String> tokens = new HashSet<>();
53
54 /**
55 * The tab width for column reporting. Default is uninitialized as the value is inherited from
56 * the parent module.
57 */
58 private int tabWidth;
59
60 /**
61 * Creates a new {@code AbstractCheck} instance.
62 */
63 protected AbstractCheck() {
64 // no code by default
65 }
66
67 /**
68 * Returns the default token a check is interested in. Only used if the
69 * configuration for a check does not define the tokens.
70 *
71 * @return the default tokens
72 * @see TokenTypes
73 */
74 public abstract int[] getDefaultTokens();
75
76 /**
77 * The configurable token set.
78 * Used to protect Checks against malicious users who specify an
79 * unacceptable token set in the configuration file.
80 * The default implementation returns the check's default tokens.
81 *
82 * @return the token set this check is designed for.
83 * @see TokenTypes
84 */
85 public abstract int[] getAcceptableTokens();
86
87 /**
88 * The tokens that this check must be registered for.
89 *
90 * @return the token set this must be registered for.
91 * @see TokenTypes
92 */
93 public abstract int[] getRequiredTokens();
94
95 /**
96 * Whether comment nodes are required or not.
97 *
98 * @return false as a default value.
99 */
100 public boolean isCommentNodesRequired() {
101 return false;
102 }
103
104 /**
105 * Adds a set of tokens the check is interested in.
106 *
107 * @param strRep the string representation of the tokens interested in
108 * @noinspection WeakerAccess
109 * @noinspectionreason WeakerAccess - we avoid 'protected' when possible
110 */
111 public final void setTokens(String... strRep) {
112 Collections.addAll(tokens, strRep);
113 }
114
115 /**
116 * Returns the tokens registered for the check.
117 *
118 * @return the set of token names
119 */
120 public final Set<String> getTokenNames() {
121 return Collections.unmodifiableSet(tokens);
122 }
123
124 /**
125 * Returns the sorted set of {@link Violation}.
126 *
127 * @return the sorted set of {@link Violation}.
128 */
129 public SortedSet<Violation> getViolations() {
130 return new TreeSet<>(context.get().violations);
131 }
132
133 /**
134 * Clears the sorted set of {@link Violation} of the check.
135 */
136 public final void clearViolations() {
137 context.get().violations.clear();
138 }
139
140 /**
141 * Initialize the check. This is the time to verify that the check has
142 * everything required to perform its job.
143 */
144 public void init() {
145 // No code by default, should be overridden only by demand at subclasses
146 }
147
148 /**
149 * Destroy the check. It is being retired from service.
150 */
151 public void destroy() {
152 context.remove();
153 }
154
155 /**
156 * Called before the starting to process a tree. Ideal place to initialize
157 * information that is to be collected whilst processing a tree.
158 *
159 * @param rootAST the root of the tree
160 */
161 public void beginTree(DetailAST rootAST) {
162 // No code by default, should be overridden only by demand at subclasses
163 }
164
165 /**
166 * Called after finished processing a tree. Ideal place to report on
167 * information collected whilst processing a tree.
168 *
169 * @param rootAST the root of the tree
170 */
171 public void finishTree(DetailAST rootAST) {
172 // No code by default, should be overridden only by demand at subclasses
173 }
174
175 /**
176 * Called to process a token.
177 *
178 * @param ast the token to process
179 */
180 public void visitToken(DetailAST ast) {
181 // No code by default, should be overridden only by demand at subclasses
182 }
183
184 /**
185 * Called after all the child nodes have been process.
186 *
187 * @param ast the token leaving
188 */
189 public void leaveToken(DetailAST ast) {
190 // No code by default, should be overridden only by demand at subclasses
191 }
192
193 /**
194 * Set the file contents associated with the tree.
195 *
196 * @param contents the manager
197 */
198 public final void setFileContents(FileContents contents) {
199 context.get().fileContents = contents;
200 }
201
202 /**
203 * Returns the file contents associated with the tree.
204 *
205 * @return the file contents
206 * @deprecated
207 * Usage of this method is no longer accepted.
208 * Please use AST based methods instead.
209 * @noinspection WeakerAccess
210 * @noinspectionreason WeakerAccess - we avoid 'protected' when possible
211 */
212 @Deprecated(since = "9.3")
213 public final FileContents getFileContents() {
214 return context.get().fileContents;
215 }
216
217 /**
218 * Get tab width to report audit events with.
219 *
220 * @return the tab width to audit events with
221 */
222 protected final int getTabWidth() {
223 return tabWidth;
224 }
225
226 /**
227 * Set the tab width to report audit events with.
228 *
229 * @param tabWidth an {@code int} value
230 */
231 public final void setTabWidth(int tabWidth) {
232 this.tabWidth = tabWidth;
233 }
234
235 @Override
236 public final void log(int line, String key, Object... args) {
237 context.get().violations.add(
238 new Violation(
239 line,
240 getMessageBundle(),
241 key,
242 args,
243 getSeverityLevel(),
244 getId(),
245 getClass(),
246 getCustomMessages().get(key)));
247 }
248
249 /**
250 * Helper method to log a Violation.
251 *
252 * @param ast a node to get line id column numbers associated
253 * with the violation
254 * @param key key to locale violation format
255 * @param args arguments to format
256 */
257 public final void log(DetailAST ast, String key, Object... args) {
258 // CommonUtil.lengthExpandedTabs returns column number considering tabulation
259 // characters, it takes line from the file by line number, ast column number and tab
260 // width as arguments. Returned value is 0-based, but user must see column number starting
261 // from 1, that is why result of the method CommonUtil.lengthExpandedTabs
262 // is increased by one.
263
264 final int col = 1 + CommonUtil.lengthExpandedTabs(
265 getLines()[ast.getLineNo() - 1], ast.getColumnNo(), tabWidth);
266 context.get().violations.add(
267 new Violation(
268 ast.getLineNo(),
269 col,
270 ast.getColumnNo(),
271 ast.getType(),
272 getMessageBundle(),
273 key,
274 args,
275 getSeverityLevel(),
276 getId(),
277 getClass(),
278 getCustomMessages().get(key)));
279 }
280
281 @Override
282 public final void log(int lineNo, int colNo, String key,
283 Object... args) {
284 final int col = 1 + CommonUtil.lengthExpandedTabs(
285 getLines()[lineNo - 1], colNo, tabWidth);
286 context.get().violations.add(
287 new Violation(
288 lineNo,
289 col,
290 getMessageBundle(),
291 key,
292 args,
293 getSeverityLevel(),
294 getId(),
295 getClass(),
296 getCustomMessages().get(key)));
297 }
298
299 /**
300 * Returns the lines associated with the tree.
301 *
302 * @return the file contents
303 */
304 public final String[] getLines() {
305 return context.get().fileContents.getLines();
306 }
307
308 /**
309 * Returns the line associated with the tree.
310 *
311 * @param index index of the line
312 * @return the line from the file contents
313 */
314 public final String getLine(int index) {
315 return context.get().fileContents.getLine(index);
316 }
317
318 /**
319 * Returns full path to the file.
320 *
321 * @return full path to file.
322 */
323 public final String getFilePath() {
324 return context.get().fileContents.getFileName();
325 }
326
327 /**
328 * Returns code point representation of file text from given line number.
329 *
330 * @param index index of the line
331 * @return the array of Unicode code points
332 */
333 public final int[] getLineCodePoints(int index) {
334 return getLine(index).codePoints().toArray();
335 }
336
337 /**
338 * The actual context holder.
339 */
340 private static final class FileContext {
341 /** The sorted set for collecting violations. */
342 private final SortedSet<Violation> violations = new TreeSet<>();
343
344 /** The current file contents. */
345 private FileContents fileContents;
346
347 /**
348 * Creates a new {@code FileContext} instance.
349 */
350 private FileContext() {
351 // no code by default
352 }
353 }
354
355 }