View Javadoc
1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.renderers;
5   
6   import java.io.IOException;
7   import java.util.ArrayList;
8   import java.util.HashMap;
9   import java.util.Iterator;
10  import java.util.List;
11  import java.util.Map;
12  
13  import net.sourceforge.pmd.PMD;
14  import net.sourceforge.pmd.PropertySource;
15  import net.sourceforge.pmd.RuleViolation;
16  import net.sourceforge.pmd.lang.rule.properties.BooleanProperty;
17  import net.sourceforge.pmd.renderers.ColumnDescriptor.Accessor;
18  import net.sourceforge.pmd.util.StringUtil;
19  
20  /**
21   * Renderer the results to a comma-delimited text format. All available columns
22   * are present by default. IDEs can enable/disable columns individually (cmd-line
23   * control to follow eventually)
24   */
25  public class CSVRenderer extends AbstractIncrementingRenderer {
26  
27      private String separator;
28      private String cr;
29  
30      private CSVWriter<RuleViolation> csvWriter;
31  
32      private static final String DEFAULT_SEPARATOR = ",";
33  
34      private static final Map<String, BooleanProperty> PROPERTY_DESCRIPTORS_BY_ID = new HashMap<>();
35  
36      public static final String NAME = "csv";
37  
38      @SuppressWarnings("unchecked")
39  	private static final ColumnDescriptor<RuleViolation>[] ALL_COLUMNS = new ColumnDescriptor[] {
40      	new ColumnDescriptor<>("problem", 	"Problem", 		new Accessor<RuleViolation>() { public String get(int idx, RuleViolation rv, String cr) { return Integer.toString(idx); }} ),
41      	new ColumnDescriptor<>("package",	"Package", 		new Accessor<RuleViolation>() { public String get(int idx, RuleViolation rv, String cr) { return rv.getPackageName(); }} ),
42      	new ColumnDescriptor<>("file",		"File", 		new Accessor<RuleViolation>() { public String get(int idx, RuleViolation rv, String cr) { return rv.getFilename(); }} ),
43      	new ColumnDescriptor<>("priority",	"Priority", 	new Accessor<RuleViolation>() { public String get(int idx, RuleViolation rv, String cr) { return Integer.toString(rv.getRule().getPriority().getPriority()); }} ),
44      	new ColumnDescriptor<>("line",		"Line", 		new Accessor<RuleViolation>() { public String get(int idx, RuleViolation rv, String cr) { return Integer.toString(rv.getBeginLine()); }} ),
45      	new ColumnDescriptor<>("desc",		"Description", 	new Accessor<RuleViolation>() { public String get(int idx, RuleViolation rv, String cr) { return StringUtil.replaceString(rv.getDescription(), '\"', "'"); }} ),
46      	new ColumnDescriptor<>("ruleSet",	"Rule set", 	new Accessor<RuleViolation>() { public String get(int idx, RuleViolation rv, String cr) { return rv.getRule().getRuleSetName(); }} ),
47      	new ColumnDescriptor<>("rule",		"Rule", 		new Accessor<RuleViolation>() { public String get(int idx, RuleViolation rv, String cr) { return rv.getRule().getName(); }} )
48      	};
49  
50  
51      private static BooleanProperty booleanPropertyFor(String id, String label) {
52  
53      	BooleanProperty prop = PROPERTY_DESCRIPTORS_BY_ID.get(id);
54      	if (prop != null) {
55      	    return prop;
56      	}
57  
58      	prop = new BooleanProperty(id, "Include " + label + " column", true, 1.0f);
59      	PROPERTY_DESCRIPTORS_BY_ID.put(id, prop);
60      	return prop;
61      }
62  
63      public CSVRenderer(ColumnDescriptor<RuleViolation>[] columns, String theSeparator, String theCR) {
64      	super(NAME, "Comma-separated values tabular format.");
65  
66      	separator = theSeparator;
67      	cr = theCR;
68  
69      	for (ColumnDescriptor<RuleViolation> desc : columns) {
70      		definePropertyDescriptor(booleanPropertyFor(desc.id, desc.title));
71      		}
72      }
73  
74      private List<ColumnDescriptor<RuleViolation>> activeColumns() {
75  
76      	List<ColumnDescriptor<RuleViolation>> actives = new ArrayList<>();
77  
78       	for (ColumnDescriptor<RuleViolation> desc : ALL_COLUMNS) {
79      		BooleanProperty prop = booleanPropertyFor(desc.id, null);
80      		if (getProperty(prop)) {
81      			actives.add(desc);
82      			} else {
83  //    				System.out.println("disabled: " + prop);
84      			}
85      		}
86       	return actives;
87      }
88  
89      private CSVWriter<RuleViolation> csvWriter() {
90      	if (csvWriter != null) {
91      	    return csvWriter;
92      	}
93  
94      	csvWriter = new CSVWriter<>(activeColumns(), separator, cr);
95      	return csvWriter;
96      }
97  
98      public CSVRenderer() {
99      	this( ALL_COLUMNS, DEFAULT_SEPARATOR, PMD.EOL);
100     }
101 
102     /**
103      * {@inheritDoc}
104      */
105     @Override
106     public void start() throws IOException {
107     	csvWriter().writeTitles(getWriter());
108     }
109 
110     public String defaultFileExtension() { return "csv"; }
111 
112     /**
113      * {@inheritDoc}
114      */
115     @Override
116     public void renderFileViolations(Iterator<RuleViolation> violations) throws IOException {
117     	csvWriter().writeData(getWriter(), violations);
118     }
119 
120 	 /**
121 	  * We can't show any violations if we don't have any visible columns.
122 	  *
123 	  * @see PropertySource#dysfunctionReason()
124 	  */
125     @Override
126 	 public String dysfunctionReason() {
127 		 return activeColumns().isEmpty() ? "No columns selected" : null;
128 	 }
129 }