All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sourceforge.pmd.ant.Formatter Maven / Gradle / Ivy

The newest version!
/**
 * 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.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.Parameter;

import net.sourceforge.pmd.internal.util.IOUtil;
import net.sourceforge.pmd.lang.document.TextFile;
import net.sourceforge.pmd.renderers.Renderer;
import net.sourceforge.pmd.renderers.RendererFactory;
import net.sourceforge.pmd.reporting.FileAnalysisListener;
import net.sourceforge.pmd.reporting.FileNameRenderer;
import net.sourceforge.pmd.reporting.GlobalAnalysisListener;
import net.sourceforge.pmd.reporting.ListenerInitializer;

/**
 * Part of PMD Ant task configuration. Setters of this class are interpreted by Ant as properties
 * settable in the XML. This is therefore published API.
 *
 * 

This class is used to configure a specific {@link Renderer} for outputting the violations. This is called * a formatter in PMD Ant task configuration and might look like this: * *

{@code
 * 
 *   
 * 
 * }
* * @see PMDTask#addFormatter(Formatter) */ public class Formatter { private File toFile; private String type; private boolean toConsole; private boolean showSuppressed; private final List 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); } private 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); } catch (IOException ioe) { throw new BuildException(ioe.getMessage(), ioe); } } boolean isNoOutputSupplied() { return toFile == null && !toConsole; } @Override public String toString() { return "file = " + toFile + "; renderer = " + type; } private static String[] validRendererCodes() { return RendererFactory.supportedRenderers().toArray(new String[0]); } 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(); } Renderer createRenderer() { if (StringUtils.isBlank(type)) { throw new BuildException(unknownRendererMessage("")); } 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 = Files.newOutputStream(file.toPath()); writer = new BufferedWriter(new OutputStreamWriter(output, charset)); isOnError = false; } finally { if (isOnError) { IOUtil.closeQuietly(output); IOUtil.closeQuietly(writer); } } return writer; } private static String getConsoleEncoding() { Console console = System.console(); // in case of pipe or redirect, no interactive console, we get null if (console != null) { // Since Java 22, this returns a console even for redirected streams. // In that case, we need to check Console.isTerminal() // See: JLine As The Default Console Provider (JDK-8308591) try { Boolean isTerminal = (Boolean) MethodUtils.invokeMethod(console, "isTerminal"); if (!isTerminal) { // stop here, we don't have an interactive console. return null; } } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException ignored) { // fall-through - we use a Java Runtime < 22. } try { Object res = FieldUtils.readDeclaredField(console, "cs", true); if (res instanceof Charset) { return ((Charset) res).name(); } } catch (IllegalArgumentException | ReflectiveOperationException ignored) { // fall-through } // Maybe this is Java17+? Then there will be // https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/Console.html#charset() // instead of the field "cs". try { Method charsetMethod = Console.class.getDeclaredMethod("charset"); Charset charset = (Charset) charsetMethod.invoke(console); return charset.name(); } catch (IllegalArgumentException | ReflectiveOperationException ignored) { // fall-through } return getNativeConsoleEncoding(); } return null; } private static String getNativeConsoleEncoding() { try { Object res = MethodUtils.invokeStaticMethod(Console.class, "encoding"); if (res instanceof String) { return (String) res; } } catch (IllegalArgumentException | ReflectiveOperationException ignored) { // fall-through } return null; } GlobalAnalysisListener newListener(Project project) throws IOException { start(project.getBaseDir().toString()); return new GlobalAnalysisListener() { final GlobalAnalysisListener listener = renderer.newListener(); @Override public ListenerInitializer initializer() { return new ListenerInitializer() { @Override public void setFileNameRenderer(FileNameRenderer fileNameRenderer) { renderer.setFileNameRenderer(fileNameRenderer); } }; } @Override public FileAnalysisListener startFileAnalysis(TextFile file) { return listener.startFileAnalysis(file); } @Override public void close() throws Exception { listener.close(); if (!toConsole) { writer.close(); } } }; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy