001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2018 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.io.File;
023import java.util.Arrays;
024import java.util.SortedSet;
025import java.util.TreeSet;
026
027import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
028
029/**
030 * Provides common functionality for many FileSetChecks.
031 *
032 * @noinspection NoopMethodInAbstractClass
033 */
034public abstract class AbstractFileSetCheck
035    extends AbstractViolationReporter
036    implements FileSetCheck {
037
038    /**
039     * Collects the error messages.
040     */
041    private static final ThreadLocal<SortedSet<LocalizedMessage>> MESSAGE_COLLECTOR =
042            ThreadLocal.withInitial(TreeSet::new);
043
044    /** The dispatcher errors are fired to. */
045    private MessageDispatcher messageDispatcher;
046
047    /** The file extensions that are accepted by this filter. */
048    private String[] fileExtensions = CommonUtils.EMPTY_STRING_ARRAY;
049
050    /**
051     * Called to process a file that matches the specified file extensions.
052     * @param file the file to be processed
053     * @param fileText the contents of the file.
054     * @throws CheckstyleException if error condition within Checkstyle occurs.
055     */
056    protected abstract void processFiltered(File file, FileText fileText)
057            throws CheckstyleException;
058
059    @Override
060    public void init() {
061        // No code by default, should be overridden only by demand at subclasses
062    }
063
064    @Override
065    public void destroy() {
066        // No code by default, should be overridden only by demand at subclasses
067    }
068
069    @Override
070    public void beginProcessing(String charset) {
071        // No code by default, should be overridden only by demand at subclasses
072    }
073
074    @Override
075    public final SortedSet<LocalizedMessage> process(File file, FileText fileText)
076            throws CheckstyleException {
077        final SortedSet<LocalizedMessage> messages = MESSAGE_COLLECTOR.get();
078        messages.clear();
079        // Process only what interested in
080        if (CommonUtils.matchesFileExtension(file, fileExtensions)) {
081            processFiltered(file, fileText);
082        }
083        final SortedSet<LocalizedMessage> result = new TreeSet<>(messages);
084        messages.clear();
085        return result;
086    }
087
088    @Override
089    public void finishProcessing() {
090        // No code by default, should be overridden only by demand at subclasses
091    }
092
093    @Override
094    public final void setMessageDispatcher(MessageDispatcher messageDispatcher) {
095        this.messageDispatcher = messageDispatcher;
096    }
097
098    /**
099     * A message dispatcher is used to fire violation messages to
100     * interested audit listeners.
101     *
102     * @return the current MessageDispatcher.
103     */
104    protected final MessageDispatcher getMessageDispatcher() {
105        return messageDispatcher;
106    }
107
108    /**
109     * Makes copy of file extensions and returns them.
110     * @return file extensions that identify the files that pass the
111     *     filter of this FileSetCheck.
112     */
113    public String[] getFileExtensions() {
114        return Arrays.copyOf(fileExtensions, fileExtensions.length);
115    }
116
117    /**
118     * Sets the file extensions that identify the files that pass the
119     * filter of this FileSetCheck.
120     * @param extensions the set of file extensions. A missing
121     *         initial '.' character of an extension is automatically added.
122     * @throws IllegalArgumentException is argument is null
123     */
124    public final void setFileExtensions(String... extensions) {
125        if (extensions == null) {
126            throw new IllegalArgumentException("Extensions array can not be null");
127        }
128
129        fileExtensions = new String[extensions.length];
130        for (int i = 0; i < extensions.length; i++) {
131            final String extension = extensions[i];
132            if (CommonUtils.startsWithChar(extension, '.')) {
133                fileExtensions[i] = extension;
134            }
135            else {
136                fileExtensions[i] = "." + extension;
137            }
138        }
139    }
140
141    /**
142     * Adds the sorted set of {@link LocalizedMessage} to the message collector.
143     * @param messages the sorted set of {@link LocalizedMessage}.
144     */
145    protected static void addMessages(SortedSet<LocalizedMessage> messages) {
146        MESSAGE_COLLECTOR.get().addAll(messages);
147    }
148
149    @Override
150    public final void log(int line, String key, Object... args) {
151        log(line, 0, key, args);
152    }
153
154    @Override
155    public final void log(int lineNo, int colNo, String key,
156            Object... args) {
157        MESSAGE_COLLECTOR.get().add(
158                new LocalizedMessage(lineNo,
159                        colNo,
160                        getMessageBundle(),
161                        key,
162                        args,
163                        getSeverityLevel(),
164                        getId(),
165                        getClass(),
166                        getCustomMessages().get(key)));
167    }
168
169    /**
170     * Notify all listeners about the errors in a file.
171     * Calls {@code MessageDispatcher.fireErrors()} with
172     * all logged errors and than clears errors' list.
173     * @param fileName the audited file
174     */
175    protected final void fireErrors(String fileName) {
176        final SortedSet<LocalizedMessage> errors = new TreeSet<>(MESSAGE_COLLECTOR.get());
177        MESSAGE_COLLECTOR.get().clear();
178        messageDispatcher.fireErrors(fileName, errors);
179    }
180
181}