1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2024 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 * Returns the default token a check is interested in. Only used if the
62 * configuration for a check does not define the tokens.
63 *
64 * @return the default tokens
65 * @see TokenTypes
66 */
67 public abstract int[] getDefaultTokens();
68
69 /**
70 * The configurable token set.
71 * Used to protect Checks against malicious users who specify an
72 * unacceptable token set in the configuration file.
73 * The default implementation returns the check's default tokens.
74 *
75 * @return the token set this check is designed for.
76 * @see TokenTypes
77 */
78 public abstract int[] getAcceptableTokens();
79
80 /**
81 * The tokens that this check must be registered for.
82 *
83 * @return the token set this must be registered for.
84 * @see TokenTypes
85 */
86 public abstract int[] getRequiredTokens();
87
88 /**
89 * Whether comment nodes are required or not.
90 *
91 * @return false as a default value.
92 */
93 public boolean isCommentNodesRequired() {
94 return false;
95 }
96
97 /**
98 * Adds a set of tokens the check is interested in.
99 *
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 }