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.LocalizedMessage; 032import com.puppycrawl.tools.checkstyle.api.SeverityLevel; 033 034/** 035 * Simple plain logger for text output. 036 * This is maybe not very suitable for a text output into a file since it 037 * does not need all 'audit finished' and so on stuff, but it looks good on 038 * stdout anyway. If there is really a problem this is what XMLLogger is for. 039 * It gives structure. 040 * 041 * @see XMLLogger 042 * @noinspection ClassWithTooManyConstructors 043 */ 044public class DefaultLogger extends AutomaticBean implements AuditListener { 045 046 /** 047 * A key pointing to the add exception 048 * message in the "messages.properties" file. 049 */ 050 public static final String ADD_EXCEPTION_MESSAGE = "DefaultLogger.addException"; 051 /** 052 * A key pointing to the started audit 053 * message in the "messages.properties" file. 054 */ 055 public static final String AUDIT_STARTED_MESSAGE = "DefaultLogger.auditStarted"; 056 /** 057 * A key pointing to the finished audit 058 * message in the "messages.properties" file. 059 */ 060 public static final String AUDIT_FINISHED_MESSAGE = "DefaultLogger.auditFinished"; 061 062 /** Where to write info messages. **/ 063 private final PrintWriter infoWriter; 064 /** Close info stream after use. */ 065 private final boolean closeInfo; 066 067 /** Where to write error messages. **/ 068 private final PrintWriter errorWriter; 069 /** Close error stream after use. */ 070 private final boolean closeError; 071 072 /** Formatter for the log message. */ 073 private final AuditEventFormatter formatter; 074 075 /** 076 * Creates a new {@code DefaultLogger} instance. 077 * @param outputStream where to log infos and errors 078 * @param closeStreamsAfterUse if oS should be closed in auditFinished() 079 * @deprecated in order to fulfill demands of BooleanParameter IDEA check. 080 * @noinspection BooleanParameter 081 */ 082 @Deprecated 083 public DefaultLogger(OutputStream outputStream, boolean closeStreamsAfterUse) { 084 // no need to close oS twice 085 this(outputStream, closeStreamsAfterUse, outputStream, false); 086 } 087 088 /** 089 * Creates a new {@code DefaultLogger} instance. 090 * @param infoStream the {@code OutputStream} for info messages. 091 * @param closeInfoAfterUse auditFinished should close infoStream. 092 * @param errorStream the {@code OutputStream} for error messages. 093 * @param closeErrorAfterUse auditFinished should close errorStream 094 * @deprecated in order to fulfill demands of BooleanParameter IDEA check. 095 * @noinspection BooleanParameter 096 */ 097 @Deprecated 098 public DefaultLogger(OutputStream infoStream, 099 boolean closeInfoAfterUse, 100 OutputStream errorStream, 101 boolean closeErrorAfterUse) { 102 this(infoStream, closeInfoAfterUse, errorStream, closeErrorAfterUse, 103 new AuditEventDefaultFormatter()); 104 } 105 106 /** 107 * Creates a new {@code DefaultLogger} instance. 108 * 109 * @param infoStream the {@code OutputStream} for info messages 110 * @param closeInfoAfterUse auditFinished should close infoStream 111 * @param errorStream the {@code OutputStream} for error messages 112 * @param closeErrorAfterUse auditFinished should close errorStream 113 * @param messageFormatter formatter for the log message. 114 * @deprecated in order to fulfill demands of BooleanParameter IDEA check. 115 * @noinspection BooleanParameter, WeakerAccess 116 */ 117 @Deprecated 118 public DefaultLogger(OutputStream infoStream, 119 boolean closeInfoAfterUse, 120 OutputStream errorStream, 121 boolean closeErrorAfterUse, 122 AuditEventFormatter messageFormatter) { 123 closeInfo = closeInfoAfterUse; 124 closeError = closeErrorAfterUse; 125 final Writer infoStreamWriter = new OutputStreamWriter(infoStream, StandardCharsets.UTF_8); 126 infoWriter = new PrintWriter(infoStreamWriter); 127 128 if (infoStream == errorStream) { 129 errorWriter = infoWriter; 130 } 131 else { 132 final Writer errorStreamWriter = new OutputStreamWriter(errorStream, 133 StandardCharsets.UTF_8); 134 errorWriter = new PrintWriter(errorStreamWriter); 135 } 136 formatter = messageFormatter; 137 } 138 139 /** 140 * Creates a new {@code DefaultLogger} instance. 141 * @param outputStream where to log infos and errors 142 * @param outputStreamOptions if {@code CLOSE} that should be closed in auditFinished() 143 */ 144 public DefaultLogger(OutputStream outputStream, OutputStreamOptions outputStreamOptions) { 145 // no need to close oS twice 146 this(outputStream, outputStreamOptions, outputStream, OutputStreamOptions.NONE); 147 } 148 149 /** 150 * Creates a new {@code DefaultLogger} instance. 151 * @param infoStream the {@code OutputStream} for info messages. 152 * @param infoStreamOptions if {@code CLOSE} info should be closed in auditFinished() 153 * @param errorStream the {@code OutputStream} for error messages. 154 * @param errorStreamOptions if {@code CLOSE} error should be closed in auditFinished() 155 */ 156 public DefaultLogger(OutputStream infoStream, 157 OutputStreamOptions infoStreamOptions, 158 OutputStream errorStream, 159 OutputStreamOptions errorStreamOptions) { 160 this(infoStream, infoStreamOptions, errorStream, errorStreamOptions, 161 new AuditEventDefaultFormatter()); 162 } 163 164 /** 165 * Creates a new {@code DefaultLogger} instance. 166 * 167 * @param infoStream the {@code OutputStream} for info messages 168 * @param infoStreamOptions if {@code CLOSE} info should be closed in auditFinished() 169 * @param errorStream the {@code OutputStream} for error messages 170 * @param errorStreamOptions if {@code CLOSE} error should be closed in auditFinished() 171 * @param messageFormatter formatter for the log message. 172 * @noinspection WeakerAccess 173 */ 174 public DefaultLogger(OutputStream infoStream, 175 OutputStreamOptions infoStreamOptions, 176 OutputStream errorStream, 177 OutputStreamOptions errorStreamOptions, 178 AuditEventFormatter messageFormatter) { 179 closeInfo = infoStreamOptions == OutputStreamOptions.CLOSE; 180 closeError = errorStreamOptions == OutputStreamOptions.CLOSE; 181 final Writer infoStreamWriter = new OutputStreamWriter(infoStream, StandardCharsets.UTF_8); 182 infoWriter = new PrintWriter(infoStreamWriter); 183 184 if (infoStream == errorStream) { 185 errorWriter = infoWriter; 186 } 187 else { 188 final Writer errorStreamWriter = new OutputStreamWriter(errorStream, 189 StandardCharsets.UTF_8); 190 errorWriter = new PrintWriter(errorStreamWriter); 191 } 192 formatter = messageFormatter; 193 } 194 195 @Override 196 protected void finishLocalSetup() { 197 // No code by default 198 } 199 200 /** 201 * Print an Emacs compliant line on the error stream. 202 * If the column number is non zero, then also display it. 203 * @see AuditListener 204 **/ 205 @Override 206 public void addError(AuditEvent event) { 207 final SeverityLevel severityLevel = event.getSeverityLevel(); 208 if (severityLevel != SeverityLevel.IGNORE) { 209 final String errorMessage = formatter.format(event); 210 errorWriter.println(errorMessage); 211 } 212 } 213 214 @Override 215 public void addException(AuditEvent event, Throwable throwable) { 216 synchronized (errorWriter) { 217 final LocalizedMessage addExceptionMessage = new LocalizedMessage(0, 218 Definitions.CHECKSTYLE_BUNDLE, ADD_EXCEPTION_MESSAGE, 219 new String[] {event.getFileName()}, null, 220 LocalizedMessage.class, null); 221 errorWriter.println(addExceptionMessage.getMessage()); 222 throwable.printStackTrace(errorWriter); 223 } 224 } 225 226 @Override 227 public void auditStarted(AuditEvent event) { 228 final LocalizedMessage auditStartMessage = new LocalizedMessage(0, 229 Definitions.CHECKSTYLE_BUNDLE, AUDIT_STARTED_MESSAGE, null, null, 230 LocalizedMessage.class, null); 231 infoWriter.println(auditStartMessage.getMessage()); 232 infoWriter.flush(); 233 } 234 235 @Override 236 public void auditFinished(AuditEvent event) { 237 final LocalizedMessage auditFinishMessage = new LocalizedMessage(0, 238 Definitions.CHECKSTYLE_BUNDLE, AUDIT_FINISHED_MESSAGE, null, null, 239 LocalizedMessage.class, null); 240 infoWriter.println(auditFinishMessage.getMessage()); 241 closeStreams(); 242 } 243 244 @Override 245 public void fileStarted(AuditEvent event) { 246 // No need to implement this method in this class 247 } 248 249 @Override 250 public void fileFinished(AuditEvent event) { 251 infoWriter.flush(); 252 } 253 254 /** 255 * Flushes the output streams and closes them if needed. 256 */ 257 private void closeStreams() { 258 infoWriter.flush(); 259 if (closeInfo) { 260 infoWriter.close(); 261 } 262 263 errorWriter.flush(); 264 if (closeError) { 265 errorWriter.close(); 266 } 267 } 268 269}