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