View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle;
21  
22  import java.io.OutputStream;
23  import java.io.OutputStreamWriter;
24  import java.io.PrintWriter;
25  import java.io.Writer;
26  import java.nio.charset.StandardCharsets;
27  
28  import com.puppycrawl.tools.checkstyle.api.AuditEvent;
29  import com.puppycrawl.tools.checkstyle.api.AuditListener;
30  import com.puppycrawl.tools.checkstyle.api.AutomaticBean;
31  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
32  import com.puppycrawl.tools.checkstyle.api.LocalizedMessage;
33  import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
34  
35  /**
36   * Simple plain logger for text output.
37   * This is maybe not very suitable for a text output into a file since it
38   * does not need all 'audit finished' and so on stuff, but it looks good on
39   * stdout anyway. If there is really a problem this is what XMLLogger is for.
40   * It gives structure.
41   *
42   * @author <a href="mailto:stephane.bailliez@wanadoo.fr">Stephane Bailliez</a>
43   * @see XMLLogger
44   * @noinspection ClassWithTooManyConstructors
45   */
46  public class DefaultLogger extends AutomaticBean implements AuditListener {
47  
48      /**
49       * A key pointing to the add exception
50       * message in the "messages.properties" file.
51       */
52      public static final String ADD_EXCEPTION_MESSAGE = "DefaultLogger.addException";
53      /**
54       * A key pointing to the started audit
55       * message in the "messages.properties" file.
56       */
57      public static final String AUDIT_STARTED_MESSAGE = "DefaultLogger.auditStarted";
58      /**
59       * A key pointing to the finished audit
60       * message in the "messages.properties" file.
61       */
62      public static final String AUDIT_FINISHED_MESSAGE = "DefaultLogger.auditFinished";
63  
64      /** Where to write info messages. **/
65      private final PrintWriter infoWriter;
66      /** Close info stream after use. */
67      private final boolean closeInfo;
68  
69      /** Where to write error messages. **/
70      private final PrintWriter errorWriter;
71      /** Close error stream after use. */
72      private final boolean closeError;
73  
74      /** Formatter for the log message. */
75      private final AuditEventFormatter formatter;
76  
77      /**
78       * Creates a new {@code DefaultLogger} instance.
79       * @param outputStream where to log infos and errors
80       * @param closeStreamsAfterUse if oS should be closed in auditFinished()
81       * @deprecated in order to fulfill demands of BooleanParameter IDEA check.
82       * @noinspection BooleanParameter
83       */
84      @Deprecated
85      public DefaultLogger(OutputStream outputStream, boolean closeStreamsAfterUse) {
86          // no need to close oS twice
87          this(outputStream, closeStreamsAfterUse, outputStream, false);
88      }
89  
90      /**
91       * Creates a new {@code DefaultLogger} instance.
92       * @param infoStream the {@code OutputStream} for info messages.
93       * @param closeInfoAfterUse auditFinished should close infoStream.
94       * @param errorStream the {@code OutputStream} for error messages.
95       * @param closeErrorAfterUse auditFinished should close errorStream
96       * @deprecated in order to fulfill demands of BooleanParameter IDEA check.
97       * @noinspection BooleanParameter
98       */
99      @Deprecated
100     public DefaultLogger(OutputStream infoStream,
101                          boolean closeInfoAfterUse,
102                          OutputStream errorStream,
103                          boolean closeErrorAfterUse) {
104         this(infoStream, closeInfoAfterUse, errorStream, closeErrorAfterUse,
105             new AuditEventDefaultFormatter());
106     }
107 
108     /**
109      * Creates a new {@code DefaultLogger} instance.
110      *
111      * @param infoStream the {@code OutputStream} for info messages
112      * @param closeInfoAfterUse auditFinished should close infoStream
113      * @param errorStream the {@code OutputStream} for error messages
114      * @param closeErrorAfterUse auditFinished should close errorStream
115      * @param messageFormatter formatter for the log message.
116      * @deprecated in order to fulfill demands of BooleanParameter IDEA check.
117      * @noinspection BooleanParameter, WeakerAccess
118      */
119     @Deprecated
120     public DefaultLogger(OutputStream infoStream,
121                          boolean closeInfoAfterUse,
122                          OutputStream errorStream,
123                          boolean closeErrorAfterUse,
124                          AuditEventFormatter messageFormatter) {
125         closeInfo = closeInfoAfterUse;
126         closeError = closeErrorAfterUse;
127         final Writer infoStreamWriter = new OutputStreamWriter(infoStream, StandardCharsets.UTF_8);
128         infoWriter = new PrintWriter(infoStreamWriter);
129 
130         if (infoStream == errorStream) {
131             errorWriter = infoWriter;
132         }
133         else {
134             final Writer errorStreamWriter = new OutputStreamWriter(errorStream,
135                     StandardCharsets.UTF_8);
136             errorWriter = new PrintWriter(errorStreamWriter);
137         }
138         formatter = messageFormatter;
139     }
140 
141     /**
142      * Creates a new {@code DefaultLogger} instance.
143      * @param outputStream where to log infos and errors
144      * @param outputStreamOptions if {@code CLOSE} that should be closed in auditFinished()
145      */
146     public DefaultLogger(OutputStream outputStream, OutputStreamOptions outputStreamOptions) {
147         // no need to close oS twice
148         this(outputStream, outputStreamOptions, outputStream, OutputStreamOptions.NONE);
149     }
150 
151     /**
152      * Creates a new {@code DefaultLogger} instance.
153      * @param infoStream the {@code OutputStream} for info messages.
154      * @param infoStreamOptions if {@code CLOSE} info should be closed in auditFinished()
155      * @param errorStream the {@code OutputStream} for error messages.
156      * @param errorStreamOptions if {@code CLOSE} error should be closed in auditFinished()
157      */
158     public DefaultLogger(OutputStream infoStream,
159                          OutputStreamOptions infoStreamOptions,
160                          OutputStream errorStream,
161                          OutputStreamOptions errorStreamOptions) {
162         this(infoStream, infoStreamOptions, errorStream, errorStreamOptions,
163                 new AuditEventDefaultFormatter());
164     }
165 
166     /**
167      * Creates a new {@code DefaultLogger} instance.
168      *
169      * @param infoStream the {@code OutputStream} for info messages
170      * @param infoStreamOptions if {@code CLOSE} info should be closed in auditFinished()
171      * @param errorStream the {@code OutputStream} for error messages
172      * @param errorStreamOptions if {@code CLOSE} error should be closed in auditFinished()
173      * @param messageFormatter formatter for the log message.
174      * @noinspection WeakerAccess
175      */
176     public DefaultLogger(OutputStream infoStream,
177                          OutputStreamOptions infoStreamOptions,
178                          OutputStream errorStream,
179                          OutputStreamOptions errorStreamOptions,
180                          AuditEventFormatter messageFormatter) {
181         closeInfo = infoStreamOptions == OutputStreamOptions.CLOSE;
182         closeError = errorStreamOptions == OutputStreamOptions.CLOSE;
183         final Writer infoStreamWriter = new OutputStreamWriter(infoStream, StandardCharsets.UTF_8);
184         infoWriter = new PrintWriter(infoStreamWriter);
185 
186         if (infoStream == errorStream) {
187             errorWriter = infoWriter;
188         }
189         else {
190             final Writer errorStreamWriter = new OutputStreamWriter(errorStream,
191                     StandardCharsets.UTF_8);
192             errorWriter = new PrintWriter(errorStreamWriter);
193         }
194         formatter = messageFormatter;
195     }
196 
197     @Override
198     protected void finishLocalSetup() throws CheckstyleException {
199         // No code by default
200     }
201 
202     /**
203      * Print an Emacs compliant line on the error stream.
204      * If the column number is non zero, then also display it.
205      * @see AuditListener
206      **/
207     @Override
208     public void addError(AuditEvent event) {
209         final SeverityLevel severityLevel = event.getSeverityLevel();
210         if (severityLevel != SeverityLevel.IGNORE) {
211             final String errorMessage = formatter.format(event);
212             errorWriter.println(errorMessage);
213         }
214     }
215 
216     @Override
217     public void addException(AuditEvent event, Throwable throwable) {
218         synchronized (errorWriter) {
219             final LocalizedMessage addExceptionMessage = new LocalizedMessage(0,
220                 Definitions.CHECKSTYLE_BUNDLE, ADD_EXCEPTION_MESSAGE,
221                 new String[] {event.getFileName()}, null,
222                 LocalizedMessage.class, null);
223             errorWriter.println(addExceptionMessage.getMessage());
224             throwable.printStackTrace(errorWriter);
225         }
226     }
227 
228     @Override
229     public void auditStarted(AuditEvent event) {
230         final LocalizedMessage auditStartMessage = new LocalizedMessage(0,
231             Definitions.CHECKSTYLE_BUNDLE, AUDIT_STARTED_MESSAGE, null, null,
232             LocalizedMessage.class, null);
233         infoWriter.println(auditStartMessage.getMessage());
234         infoWriter.flush();
235     }
236 
237     @Override
238     public void auditFinished(AuditEvent event) {
239         final LocalizedMessage auditFinishMessage = new LocalizedMessage(0,
240             Definitions.CHECKSTYLE_BUNDLE, AUDIT_FINISHED_MESSAGE, null, null,
241             LocalizedMessage.class, null);
242         infoWriter.println(auditFinishMessage.getMessage());
243         closeStreams();
244     }
245 
246     @Override
247     public void fileStarted(AuditEvent event) {
248         // No need to implement this method in this class
249     }
250 
251     @Override
252     public void fileFinished(AuditEvent event) {
253         infoWriter.flush();
254     }
255 
256     /**
257      * Flushes the output streams and closes them if needed.
258      */
259     private void closeStreams() {
260         infoWriter.flush();
261         if (closeInfo) {
262             infoWriter.close();
263         }
264 
265         errorWriter.flush();
266         if (closeError) {
267             errorWriter.close();
268         }
269     }
270 }