/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.ant; import java.io.BufferedWriter; import java.io.Console; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Properties; import org.apache.commons.io.IOUtils; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.types.Parameter; import net.sourceforge.pmd.Report; import net.sourceforge.pmd.renderers.Renderer; import net.sourceforge.pmd.renderers.RendererFactory; import net.sourceforge.pmd.util.StringUtil; public class Formatter { private File toFile; private String type; private boolean toConsole; private boolean showSuppressed; private final List<Parameter> parameters = new ArrayList<>(); private Writer writer; private Renderer renderer; public void setShowSuppressed(boolean value) { this.showSuppressed = value; } public void setType(String type) { this.type = type; } public void setToFile(File toFile) { this.toFile = toFile; } public void setToConsole(boolean toConsole) { this.toConsole = toConsole; } public void addConfiguredParam(Parameter parameter) { this.parameters.add(parameter); } public Renderer getRenderer() { return renderer; } public void start(String baseDir) { Properties properties = createProperties(); Charset charset; { String s = (String) properties.get("encoding"); if (null == s) { if (toConsole) { s = getConsoleEncoding(); if (null == s) { s = System.getProperty("file.encoding"); } } if (null == s) { charset = StandardCharsets.UTF_8; } else { charset = Charset.forName(s); } // Configures the encoding for the renderer. final Parameter parameter = new Parameter(); parameter.setName("encoding"); parameter.setValue(charset.name()); parameters.add(parameter); } else { charset = Charset.forName(s); } } try { if (toConsole) { writer = new BufferedWriter(new OutputStreamWriter(System.out, charset)); } if (toFile != null) { writer = getToFileWriter(baseDir, toFile, charset); } renderer = createRenderer(); renderer.setWriter(writer); renderer.start(); } catch (IOException ioe) { throw new BuildException(ioe.getMessage(), ioe); } } public void end(Report errorReport) { try { renderer.renderFileReport(errorReport); renderer.end(); if (toConsole) { writer.flush(); } else { writer.close(); } } catch (IOException ioe) { throw new BuildException(ioe.getMessage(), ioe); } } public boolean isNoOutputSupplied() { return toFile == null && !toConsole; } @Override public String toString() { return "file = " + toFile + "; renderer = " + type; } private static String[] validRendererCodes() { return RendererFactory.REPORT_FORMAT_TO_RENDERER.keySet() .toArray(new String[RendererFactory.REPORT_FORMAT_TO_RENDERER.size()]); } private static String unknownRendererMessage(String userSpecifiedType) { String[] typeCodes = validRendererCodes(); StringBuilder sb = new StringBuilder(100); sb.append("Formatter type must be one of: '").append(typeCodes[0]); for (int i = 1; i < typeCodes.length; i++) { sb.append("', '").append(typeCodes[i]); } sb.append("', or a class name; you specified: ").append(userSpecifiedType); return sb.toString(); } // FIXME - hm, what about this consoleRenderer thing... need a test for this Renderer createRenderer() { if (StringUtil.isEmpty(type)) { throw new BuildException(unknownRendererMessage("<unspecified>")); } Properties properties = createProperties(); Renderer renderer = RendererFactory.createRenderer(type, properties); renderer.setShowSuppressedViolations(showSuppressed); return renderer; } private Properties createProperties() { Properties properties = new Properties(); for (Parameter parameter : parameters) { properties.put(parameter.getName(), parameter.getValue()); } return properties; } private static Writer getToFileWriter(String baseDir, File toFile, Charset charset) throws IOException { final File file; if (toFile.isAbsolute()) { file = toFile; } else { file = new File(baseDir + System.getProperty("file.separator") + toFile.getPath()); } OutputStream output = null; Writer writer = null; boolean isOnError = true; try { output = new FileOutputStream(file); writer = new OutputStreamWriter(output, charset); writer = new BufferedWriter(writer); isOnError = false; } finally { if (isOnError) { IOUtils.closeQuietly(output); IOUtils.closeQuietly(writer); } } return writer; } private static String getConsoleEncoding() { Console console = System.console(); // in case of pipe or redirect, no interactive console. if (console != null) { try { Field f = Console.class.getDeclaredField("cs"); f.setAccessible(true); Object res = f.get(console); if (res instanceof Charset) { return ((Charset) res).name(); } } catch (NoSuchFieldException e) { // fall-through } catch (IllegalAccessException e) { // fall-through } return getNativeConsoleEncoding(); } return null; } private static String getNativeConsoleEncoding() { try { Method m = Console.class.getDeclaredMethod("encoding"); m.setAccessible(true); Object res = m.invoke(null); if (res instanceof String) { return (String) res; } } catch (NoSuchMethodException e) { // fall-through } catch (InvocationTargetException e) { // fall-through } catch (IllegalAccessException e) { // fall-through } return null; } }