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(since = "9.3")
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 full path to the file.
303     *
304     * @return full path to file.
305     */
306    public final String getFilePath() {
307        return context.get().fileContents.getFileName();
308    }
309
310    /**
311     * Returns code point representation of file text from given line number.
312     *
313     * @param index index of the line
314     * @return the array of Unicode code points
315     */
316    public final int[] getLineCodePoints(int index) {
317        return getLine(index).codePoints().toArray();
318    }
319
320    /**
321     * The actual context holder.
322     */
323    private static class FileContext {
324
325        /** The sorted set for collecting violations. */
326        private final SortedSet<Violation> violations = new TreeSet<>();
327
328        /** The current file contents. */
329        private FileContents fileContents;
330
331    }
332
333}