/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.renderers; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.PropertySource; import net.sourceforge.pmd.RuleViolation; import net.sourceforge.pmd.lang.rule.properties.BooleanProperty; import net.sourceforge.pmd.renderers.ColumnDescriptor.Accessor; import net.sourceforge.pmd.util.StringUtil; /** * Renderer the results to a comma-delimited text format. All available columns * are present by default. IDEs can enable/disable columns individually * (cmd-line control to follow eventually) */ public class CSVRenderer extends AbstractIncrementingRenderer { private String separator; private String cr; private CSVWriter<RuleViolation> csvWriter; private static final String DEFAULT_SEPARATOR = ","; private static final Map<String, BooleanProperty> PROPERTY_DESCRIPTORS_BY_ID = new HashMap<>(); public static final String NAME = "csv"; @SuppressWarnings("unchecked") private static final ColumnDescriptor<RuleViolation>[] ALL_COLUMNS = new ColumnDescriptor[] { new ColumnDescriptor<>("problem", "Problem", new Accessor<RuleViolation>() { @Override public String get(int idx, RuleViolation rv, String cr) { return Integer.toString(idx); } }), new ColumnDescriptor<>("package", "Package", new Accessor<RuleViolation>() { @Override public String get(int idx, RuleViolation rv, String cr) { return rv.getPackageName(); } }), new ColumnDescriptor<>("file", "File", new Accessor<RuleViolation>() { @Override public String get(int idx, RuleViolation rv, String cr) { return rv.getFilename(); } }), new ColumnDescriptor<>("priority", "Priority", new Accessor<RuleViolation>() { @Override public String get(int idx, RuleViolation rv, String cr) { return Integer.toString(rv.getRule().getPriority().getPriority()); } }), new ColumnDescriptor<>("line", "Line", new Accessor<RuleViolation>() { @Override public String get(int idx, RuleViolation rv, String cr) { return Integer.toString(rv.getBeginLine()); } }), new ColumnDescriptor<>("desc", "Description", new Accessor<RuleViolation>() { @Override public String get(int idx, RuleViolation rv, String cr) { return StringUtil.replaceString(rv.getDescription(), '\"', "'"); } }), new ColumnDescriptor<>("ruleSet", "Rule set", new Accessor<RuleViolation>() { @Override public String get(int idx, RuleViolation rv, String cr) { return rv.getRule().getRuleSetName(); } }), new ColumnDescriptor<>("rule", "Rule", new Accessor<RuleViolation>() { @Override public String get(int idx, RuleViolation rv, String cr) { return rv.getRule().getName(); } }), }; public CSVRenderer(ColumnDescriptor<RuleViolation>[] columns, String theSeparator, String theCR) { super(NAME, "Comma-separated values tabular format."); separator = theSeparator; cr = theCR; for (ColumnDescriptor<RuleViolation> desc : columns) { definePropertyDescriptor(booleanPropertyFor(desc.id, desc.title)); } } public CSVRenderer() { this(ALL_COLUMNS, DEFAULT_SEPARATOR, PMD.EOL); } private static BooleanProperty booleanPropertyFor(String id, String label) { BooleanProperty prop = PROPERTY_DESCRIPTORS_BY_ID.get(id); if (prop != null) { return prop; } prop = new BooleanProperty(id, "Include " + label + " column", true, 1.0f); PROPERTY_DESCRIPTORS_BY_ID.put(id, prop); return prop; } private List<ColumnDescriptor<RuleViolation>> activeColumns() { List<ColumnDescriptor<RuleViolation>> actives = new ArrayList<>(); for (ColumnDescriptor<RuleViolation> desc : ALL_COLUMNS) { BooleanProperty prop = booleanPropertyFor(desc.id, null); if (getProperty(prop)) { actives.add(desc); } else { // System.out.println("disabled: " + prop); } } return actives; } private CSVWriter<RuleViolation> csvWriter() { if (csvWriter != null) { return csvWriter; } csvWriter = new CSVWriter<>(activeColumns(), separator, cr); return csvWriter; } /** * {@inheritDoc} */ @Override public void start() throws IOException { csvWriter().writeTitles(getWriter()); } @Override public String defaultFileExtension() { return "csv"; } /** * {@inheritDoc} */ @Override public void renderFileViolations(Iterator<RuleViolation> violations) throws IOException { csvWriter().writeData(getWriter(), violations); } /** * We can't show any violations if we don't have any visible columns. * * @see PropertySource#dysfunctionReason() */ @Override public String dysfunctionReason() { return activeColumns().isEmpty() ? "No columns selected" : null; } }