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; 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.CheckstyleException; 032import com.puppycrawl.tools.checkstyle.api.LocalizedMessage; 033import com.puppycrawl.tools.checkstyle.api.SeverityLevel; 034 035/** 036 * Simple plain logger for text output. 037 * This is maybe not very suitable for a text output into a file since it 038 * does not need all 'audit finished' and so on stuff, but it looks good on 039 * stdout anyway. If there is really a problem this is what XMLLogger is for. 040 * It gives structure. 041 * 042 * @author <a href="mailto:stephane.bailliez@wanadoo.fr">Stephane Bailliez</a> 043 * @see XMLLogger 044 * @noinspection ClassWithTooManyConstructors 045 */ 046public class DefaultLogger extends AutomaticBean implements AuditListener { 047 048 /** 049 * A key pointing to the add exception 050 * message in the "messages.properties" file. 051 */ 052 public static final String ADD_EXCEPTION_MESSAGE = "DefaultLogger.addException"; 053 /** 054 * A key pointing to the started audit 055 * message in the "messages.properties" file. 056 */ 057 public static final String AUDIT_STARTED_MESSAGE = "DefaultLogger.auditStarted"; 058 /** 059 * A key pointing to the finished audit 060 * message in the "messages.properties" file. 061 */ 062 public static final String AUDIT_FINISHED_MESSAGE = "DefaultLogger.auditFinished"; 063 064 /** Where to write info messages. **/ 065 private final PrintWriter infoWriter; 066 /** Close info stream after use. */ 067 private final boolean closeInfo; 068 069 /** Where to write error messages. **/ 070 private final PrintWriter errorWriter; 071 /** Close error stream after use. */ 072 private final boolean closeError; 073 074 /** Formatter for the log message. */ 075 private final AuditEventFormatter formatter; 076 077 /** 078 * Creates a new {@code DefaultLogger} instance. 079 * @param outputStream where to log infos and errors 080 * @param closeStreamsAfterUse if oS should be closed in auditFinished() 081 * @deprecated in order to fulfill demands of BooleanParameter IDEA check. 082 * @noinspection BooleanParameter 083 */ 084 @Deprecated 085 public DefaultLogger(OutputStream outputStream, boolean closeStreamsAfterUse) { 086 // no need to close oS twice 087 this(outputStream, closeStreamsAfterUse, outputStream, false); 088 } 089 090 /** 091 * Creates a new {@code DefaultLogger} instance. 092 * @param infoStream the {@code OutputStream} for info messages. 093 * @param closeInfoAfterUse auditFinished should close infoStream. 094 * @param errorStream the {@code OutputStream} for error messages. 095 * @param closeErrorAfterUse auditFinished should close errorStream 096 * @deprecated in order to fulfill demands of BooleanParameter IDEA check. 097 * @noinspection BooleanParameter 098 */ 099 @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 271}