001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2015 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 044 extends AutomaticBean 045 implements AuditListener { 046 /** Cushion for avoiding StringBuffer.expandCapacity */ 047 private static final int BUFFER_CUSHION = 12; 048 049 /** Where to write info messages. **/ 050 private final PrintWriter infoWriter; 051 /** Close info stream after use. */ 052 private final boolean closeInfo; 053 054 /** Where to write error messages. **/ 055 private final PrintWriter errorWriter; 056 /** Close error stream after use. */ 057 private final boolean closeError; 058 059 /** Print severity level. */ 060 private boolean printSeverity = true; 061 062 /** 063 * Creates a new {@code DefaultLogger} instance. 064 * @param outputStream where to log infos and errors 065 * @param closeStreamsAfterUse if oS should be closed in auditFinished() 066 */ 067 public DefaultLogger(OutputStream outputStream, boolean closeStreamsAfterUse) { 068 // no need to close oS twice 069 this(outputStream, closeStreamsAfterUse, outputStream, false); 070 } 071 072 /** 073 * Creates a new <code>DefaultLogger</code> instance. 074 * @param infoStream the {@code OutputStream} for info messages. 075 * @param closeInfoAfterUse auditFinished should close infoStream. 076 * @param errorStream the {@code OutputStream} for error messages. 077 * @param closeErrorAfterUse auditFinished should close errorStream 078 * @param printSeverity if severity level should be printed. 079 */ 080 public DefaultLogger(OutputStream infoStream, 081 boolean closeInfoAfterUse, 082 OutputStream errorStream, 083 boolean closeErrorAfterUse, 084 boolean printSeverity) { 085 this(infoStream, closeInfoAfterUse, errorStream, closeErrorAfterUse); 086 this.printSeverity = printSeverity; 087 } 088 089 /** 090 * Creates a new {@code DefaultLogger} instance. 091 * 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 */ 097 public DefaultLogger(OutputStream infoStream, 098 boolean closeInfoAfterUse, 099 OutputStream errorStream, 100 boolean closeErrorAfterUse) { 101 closeInfo = closeInfoAfterUse; 102 closeError = closeErrorAfterUse; 103 final Writer infoStreamWriter = new OutputStreamWriter(infoStream, StandardCharsets.UTF_8); 104 final Writer errorStreamWriter = new OutputStreamWriter(errorStream, 105 StandardCharsets.UTF_8); 106 infoWriter = new PrintWriter(infoStreamWriter); 107 108 if (infoStream == errorStream) { 109 errorWriter = infoWriter; 110 } 111 else { 112 errorWriter = new PrintWriter(errorStreamWriter); 113 } 114 } 115 116 /** 117 * Print an Emacs compliant line on the error stream. 118 * If the column number is non zero, then also display it. 119 * @see AuditListener 120 **/ 121 @Override 122 public void addError(AuditEvent event) { 123 final SeverityLevel severityLevel = event.getSeverityLevel(); 124 if (severityLevel != SeverityLevel.IGNORE) { 125 126 final String fileName = event.getFileName(); 127 final String message = event.getMessage(); 128 129 // avoid StringBuffer.expandCapacity 130 final int bufLen = fileName.length() + message.length() 131 + BUFFER_CUSHION; 132 final StringBuilder sb = new StringBuilder(bufLen); 133 134 sb.append(fileName).append(':').append(event.getLine()); 135 if (event.getColumn() > 0) { 136 sb.append(':').append(event.getColumn()); 137 } 138 final String errorMessageSeparator = ": "; 139 if (printSeverity) { 140 sb.append(errorMessageSeparator).append(severityLevel.getName()); 141 } 142 sb.append(errorMessageSeparator).append(message); 143 errorWriter.println(sb); 144 } 145 } 146 147 @Override 148 public void addException(AuditEvent event, Throwable throwable) { 149 synchronized (errorWriter) { 150 errorWriter.println("Error auditing " + event.getFileName()); 151 throwable.printStackTrace(errorWriter); 152 } 153 } 154 155 @Override 156 public void auditStarted(AuditEvent event) { 157 infoWriter.println("Starting audit..."); 158 infoWriter.flush(); 159 } 160 161 @Override 162 public void fileFinished(AuditEvent event) { 163 infoWriter.flush(); 164 } 165 166 @Override 167 public void fileStarted(AuditEvent event) { 168 // No need to implement this method in this class 169 } 170 171 @Override 172 public void auditFinished(AuditEvent event) { 173 infoWriter.println("Audit done."); 174 closeStreams(); 175 } 176 177 /** 178 * Flushes the output streams and closes them if needed. 179 */ 180 private void closeStreams() { 181 infoWriter.flush(); 182 if (closeInfo) { 183 infoWriter.close(); 184 } 185 186 errorWriter.flush(); 187 if (closeError) { 188 errorWriter.close(); 189 } 190 } 191}