001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2016 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;
021
022import java.io.OutputStream;
023import java.io.OutputStreamWriter;
024import java.io.PrintWriter;
025import java.io.Writer;
026import java.nio.charset.StandardCharsets;
027
028import com.puppycrawl.tools.checkstyle.api.AuditEvent;
029import com.puppycrawl.tools.checkstyle.api.AuditListener;
030import com.puppycrawl.tools.checkstyle.api.AutomaticBean;
031import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
032
033/**
034 * Simple plain logger for text output.
035 * This is maybe not very suitable for a text output into a file since it
036 * does not need all 'audit finished' and so on stuff, but it looks good on
037 * stdout anyway. If there is really a problem this is what XMLLogger is for.
038 * It gives structure.
039 *
040 * @author <a href="mailto:stephane.bailliez@wanadoo.fr">Stephane Bailliez</a>
041 * @see XMLLogger
042 */
043public class DefaultLogger extends AutomaticBean implements AuditListener {
044
045    /** Where to write info messages. **/
046    private final PrintWriter infoWriter;
047    /** Close info stream after use. */
048    private final boolean closeInfo;
049
050    /** Where to write error messages. **/
051    private final PrintWriter errorWriter;
052    /** Close error stream after use. */
053    private final boolean closeError;
054
055    /** Formatter for the log message. */
056    private final AuditEventFormatter formatter;
057
058    /**
059     * Creates a new {@code DefaultLogger} instance.
060     * @param outputStream where to log infos and errors
061     * @param closeStreamsAfterUse if oS should be closed in auditFinished()
062     */
063    public DefaultLogger(OutputStream outputStream, boolean closeStreamsAfterUse) {
064        // no need to close oS twice
065        this(outputStream, closeStreamsAfterUse, outputStream, false);
066    }
067
068    /**
069     * Creates a new <code>DefaultLogger</code> instance.
070     * @param infoStream the {@code OutputStream} for info messages.
071     * @param closeInfoAfterUse auditFinished should close infoStream.
072     * @param errorStream the {@code OutputStream} for error messages.
073     * @param closeErrorAfterUse auditFinished should close errorStream
074     */
075    public DefaultLogger(OutputStream infoStream,
076                         boolean closeInfoAfterUse,
077                         OutputStream errorStream,
078                         boolean closeErrorAfterUse) {
079        this(infoStream, closeInfoAfterUse, errorStream, closeErrorAfterUse,
080            new AuditEventDefaultFormatter());
081    }
082
083    /**
084     * Creates a new {@code DefaultLogger} instance.
085     *
086     * @param infoStream the {@code OutputStream} for info messages
087     * @param closeInfoAfterUse auditFinished should close infoStream
088     * @param errorStream the {@code OutputStream} for error messages
089     * @param closeErrorAfterUse auditFinished should close errorStream
090     * @param messageFormatter formatter for the log message.
091     */
092    public DefaultLogger(OutputStream infoStream,
093                         boolean closeInfoAfterUse,
094                         OutputStream errorStream,
095                         boolean closeErrorAfterUse,
096                         AuditEventFormatter messageFormatter) {
097        closeInfo = closeInfoAfterUse;
098        closeError = closeErrorAfterUse;
099        final Writer infoStreamWriter = new OutputStreamWriter(infoStream, StandardCharsets.UTF_8);
100        infoWriter = new PrintWriter(infoStreamWriter);
101
102        if (infoStream == errorStream) {
103            errorWriter = infoWriter;
104        }
105        else {
106            final Writer errorStreamWriter = new OutputStreamWriter(errorStream,
107                    StandardCharsets.UTF_8);
108            errorWriter = new PrintWriter(errorStreamWriter);
109        }
110        formatter = messageFormatter;
111    }
112
113    /**
114     * Print an Emacs compliant line on the error stream.
115     * If the column number is non zero, then also display it.
116     * @see AuditListener
117     **/
118    @Override
119    public void addError(AuditEvent event) {
120        final SeverityLevel severityLevel = event.getSeverityLevel();
121        if (severityLevel != SeverityLevel.IGNORE) {
122            final String errorMessage = formatter.format(event);
123            errorWriter.println(errorMessage);
124        }
125    }
126
127    @Override
128    public void addException(AuditEvent event, Throwable throwable) {
129        synchronized (errorWriter) {
130            errorWriter.println("Error auditing " + event.getFileName());
131            throwable.printStackTrace(errorWriter);
132        }
133    }
134
135    @Override
136    public void auditStarted(AuditEvent event) {
137        infoWriter.println("Starting audit...");
138        infoWriter.flush();
139    }
140
141    @Override
142    public void auditFinished(AuditEvent event) {
143        infoWriter.println("Audit done.");
144        closeStreams();
145    }
146
147    @Override
148    public void fileStarted(AuditEvent event) {
149        // No need to implement this method in this class
150    }
151
152    @Override
153    public void fileFinished(AuditEvent event) {
154        infoWriter.flush();
155    }
156
157    /**
158     * Flushes the output streams and closes them if needed.
159     */
160    private void closeStreams() {
161        infoWriter.flush();
162        if (closeInfo) {
163            infoWriter.close();
164        }
165
166        errorWriter.flush();
167        if (closeError) {
168            errorWriter.close();
169        }
170    }
171}