001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files 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 * @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    /** The tab width for column reporting. */
055    private int tabWidth = CommonUtil.DEFAULT_TAB_WIDTH;
056
057    /**
058     * Returns the default token a check is interested in. Only used if the
059     * configuration for a check does not define the tokens.
060     *
061     * @return the default tokens
062     * @see TokenTypes
063     */
064    public abstract int[] getDefaultTokens();
065
066    /**
067     * The configurable token set.
068     * Used to protect Checks against malicious users who specify an
069     * unacceptable token set in the configuration file.
070     * The default implementation returns the check's default tokens.
071     *
072     * @return the token set this check is designed for.
073     * @see TokenTypes
074     */
075    public abstract int[] getAcceptableTokens();
076
077    /**
078     * The tokens that this check must be registered for.
079     *
080     * @return the token set this must be registered for.
081     * @see TokenTypes
082     */
083    public abstract int[] getRequiredTokens();
084
085    /**
086     * Whether comment nodes are required or not.
087     *
088     * @return false as a default value.
089     */
090    public boolean isCommentNodesRequired() {
091        return false;
092    }
093
094    /**
095     * Adds a set of tokens the check is interested in.
096     *
097     * @param strRep the string representation of the tokens interested in
098     * @noinspection WeakerAccess
099     * @noinspectionreason WeakerAccess - we avoid 'protected' when possible
100     */
101    public final void setTokens(String... strRep) {
102        Collections.addAll(tokens, strRep);
103    }
104
105    /**
106     * Returns the tokens registered for the check.
107     *
108     * @return the set of token names
109     */
110    public final Set<String> getTokenNames() {
111        return Collections.unmodifiableSet(tokens);
112    }
113
114    /**
115     * Returns the sorted set of {@link Violation}.
116     *
117     * @return the sorted set of {@link Violation}.
118     */
119    public SortedSet<Violation> getViolations() {
120        return new TreeSet<>(context.get().violations);
121    }
122
123    /**
124     * Clears the sorted set of {@link Violation} of the check.
125     */
126    public final void clearViolations() {
127        context.get().violations.clear();
128    }
129
130    /**
131     * Initialize the check. This is the time to verify that the check has
132     * everything required to perform its job.
133     */
134    public void init() {
135        // No code by default, should be overridden only by demand at subclasses
136    }
137
138    /**
139     * Destroy the check. It is being retired from service.
140     */
141    public void destroy() {
142        context.remove();
143    }
144
145    /**
146     * Called before the starting to process a tree. Ideal place to initialize
147     * information that is to be collected whilst processing a tree.
148     *
149     * @param rootAST the root of the tree
150     */
151    public void beginTree(DetailAST rootAST) {
152        // No code by default, should be overridden only by demand at subclasses
153    }
154
155    /**
156     * Called after finished processing a tree. Ideal place to report on
157     * information collected whilst processing a tree.
158     *
159     * @param rootAST the root of the tree
160     */
161    public void finishTree(DetailAST rootAST) {
162        // No code by default, should be overridden only by demand at subclasses
163    }
164
165    /**
166     * Called to process a token.
167     *
168     * @param ast the token to process
169     */
170    public void visitToken(DetailAST ast) {
171        // No code by default, should be overridden only by demand at subclasses
172    }
173
174    /**
175     * Called after all the child nodes have been process.
176     *
177     * @param ast the token leaving
178     */
179    public void leaveToken(DetailAST ast) {
180        // No code by default, should be overridden only by demand at subclasses
181    }
182
183    /**
184     * Set the file contents associated with the tree.
185     *
186     * @param contents the manager
187     */
188    public final void setFileContents(FileContents contents) {
189        context.get().fileContents = contents;
190    }
191
192    /**
193     * Returns the file contents associated with the tree.
194     *
195     * @return the file contents
196     * @deprecated
197     *      Usage of this method is no longer accepted.
198     *      Please use AST based methods instead.
199     * @noinspection WeakerAccess
200     * @noinspectionreason WeakerAccess - we avoid 'protected' when possible
201     */
202    @Deprecated(since = "9.3")
203    public final FileContents getFileContents() {
204        return context.get().fileContents;
205    }
206
207    /**
208     * Get tab width to report audit events with.
209     *
210     * @return the tab width to audit events with
211     */
212    protected final int getTabWidth() {
213        return tabWidth;
214    }
215
216    /**
217     * Set the tab width to report audit events with.
218     *
219     * @param tabWidth an {@code int} value
220     */
221    public final void setTabWidth(int tabWidth) {
222        this.tabWidth = tabWidth;
223    }
224
225    @Override
226    public final void log(int line, String key, Object... args) {
227        context.get().violations.add(
228            new Violation(
229                line,
230                getMessageBundle(),
231                key,
232                args,
233                getSeverityLevel(),
234                getId(),
235                getClass(),
236                getCustomMessages().get(key)));
237    }
238
239    @Override
240    public final void log(int lineNo, int colNo, String key,
241            Object... args) {
242        final int col = 1 + CommonUtil.lengthExpandedTabs(
243            getLines()[lineNo - 1], colNo, tabWidth);
244        context.get().violations.add(
245            new Violation(
246                lineNo,
247                col,
248                getMessageBundle(),
249                key,
250                args,
251                getSeverityLevel(),
252                getId(),
253                getClass(),
254                getCustomMessages().get(key)));
255    }
256
257    /**
258     * Helper method to log a Violation.
259     *
260     * @param ast a node to get line id column numbers associated
261     *             with the violation
262     * @param key key to locale violation format
263     * @param args arguments to format
264     */
265    public final void log(DetailAST ast, String key, Object... args) {
266        // CommonUtil.lengthExpandedTabs returns column number considering tabulation
267        // characters, it takes line from the file by line number, ast column number and tab
268        // width as arguments. Returned value is 0-based, but user must see column number starting
269        // from 1, that is why result of the method CommonUtil.lengthExpandedTabs
270        // is increased by one.
271
272        final int col = 1 + CommonUtil.lengthExpandedTabs(
273                getLines()[ast.getLineNo() - 1], ast.getColumnNo(), tabWidth);
274        context.get().violations.add(
275                new Violation(
276                        ast.getLineNo(),
277                        col,
278                        ast.getColumnNo(),
279                        ast.getType(),
280                        getMessageBundle(),
281                        key,
282                        args,
283                        getSeverityLevel(),
284                        getId(),
285                        getClass(),
286                        getCustomMessages().get(key)));
287    }
288
289    /**
290     * Returns the lines associated with the tree.
291     *
292     * @return the file contents
293     */
294    public final String[] getLines() {
295        return context.get().fileContents.getLines();
296    }
297
298    /**
299     * Returns the line associated with the tree.
300     *
301     * @param index index of the line
302     * @return the line from the file contents
303     */
304    public final String getLine(int index) {
305        return context.get().fileContents.getLine(index);
306    }
307
308    /**
309     * Returns full path to the file.
310     *
311     * @return full path to file.
312     */
313    public final String getFilePath() {
314        return context.get().fileContents.getFileName();
315    }
316
317    /**
318     * Returns code point representation of file text from given line number.
319     *
320     * @param index index of the line
321     * @return the array of Unicode code points
322     */
323    public final int[] getLineCodePoints(int index) {
324        return getLine(index).codePoints().toArray();
325    }
326
327    /**
328     * The actual context holder.
329     */
330    private static class FileContext {
331
332        /** The sorted set for collecting violations. */
333        private final SortedSet<Violation> violations = new TreeSet<>();
334
335        /** The current file contents. */
336        private FileContents fileContents;
337
338    }
339
340}