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