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 net.sourceforge.pmd.IRuleViolation;
7 import net.sourceforge.pmd.PMD;
8 import net.sourceforge.pmd.Report;
9
10 import java.io.BufferedReader;
11 import java.io.File;
12 import java.io.FileReader;
13 import java.io.IOException;
14 import java.io.Writer;
15 import java.util.Iterator;
16 import java.util.Map;
17
18 /***
19 * <p>A console renderer with optional color support under *nix systems.</p>
20 * <p/>
21 * <pre>
22 * * file: ./src/gilot/Test.java
23 * src: Test.java:12
24 * rule: AtLeastOneConstructor
25 * msg: Each class should declare at least one constructor
26 * code: public class Test
27 * <p/>
28 * * file: ./src/gilot/log/format/LogInterpreter.java
29 * src: LogInterpreter.java:317
30 * rule: AvoidDuplicateLiterals
31 * msg: The same String literal appears 4 times in this file; the first occurrence is on line 317
32 * code: logger.error( "missing attribute 'app_arg' in rule '" + ((Element)element.getParent()).getAttributeValue( "name" ) + "'" );
33 * <p/>
34 * src: LogInterpreter.java:317
35 * rule: AvoidDuplicateLiterals
36 * msg: The same String literal appears 5 times in this file; the first occurrence is on line 317
37 * code: logger.error( "missing attribute 'app_arg' in rule '" + ((Element)element.getParent()).getAttributeValue( "name" ) + "'" );
38 * <p/>
39 * * warnings: 3
40 * <p/>
41 * </pre>
42 * <p/>
43 * <p>Colorization is turned on by supplying -D<b>pmd.color</b> - any value other than
44 * '0' or 'false', enables color - including an empty value (''). <b>Nota Bene:</b>
45 * colorization is atm only supported under *nix terminals accepting ansi escape
46 * sequences, such as xterm, rxvt et cetera.</p>
47 */
48 public class PapariTextRenderer extends AbstractRenderer {
49 /***
50 * Directory from where java was invoked.
51 */
52 private String pwd;
53
54 private String yellowBold = "";
55 private String whiteBold = "";
56 private String redBold = "";
57 private String cyan = "";
58 private String green = "";
59
60 private String colorReset = "";
61
62 /***
63 * Enables colors on *nix systems - not windows. Color support depends
64 * on the pmd.color property, which should be set with the -D option
65 * during execution - a set value other than 'false' or '0' enables color.
66 * <p/>
67 * btw, is it possible to do this on windows (ie; console colors)?
68 */
69 private void initializeColorsIfSupported() {
70 if (System.getProperty("pmd.color") != null &&
71 !(System.getProperty("pmd.color").equals("0") || System.getProperty("pmd.color").equals("false"))) {
72 this.yellowBold = "\u001B[1;33m";
73 this.whiteBold = "\u001B[1;37m";
74 this.redBold = "\u001B[1;31m";
75 this.green = "\u001B[0;32m";
76 this.cyan = "\u001B[0;36m";
77
78 this.colorReset = "\u001B[0m";
79 }
80 }
81
82 public void render(Writer writer, Report report) throws IOException {
83 StringBuffer buf = new StringBuffer(PMD.EOL);
84 initializeColorsIfSupported();
85 String lastFile = null;
86 int numberOfErrors = 0;
87 int numberOfWarnings = 0;
88
89 for (Iterator i = report.iterator(); i.hasNext();) {
90 buf.setLength(0);
91 numberOfWarnings++;
92 IRuleViolation rv = (IRuleViolation) i.next();
93 if (!rv.getFilename().equals(lastFile)) {
94 lastFile = rv.getFilename();
95 buf.append(this.yellowBold + "*" + this.colorReset + " file: " + this.whiteBold + this.getRelativePath(lastFile) + this.colorReset + PMD.EOL);
96 }
97 buf.append(this.green + " src: " + this.cyan + lastFile.substring(lastFile.lastIndexOf(File.separator) + 1) + this.colorReset + ":" + this.cyan + rv.getBeginLine() + (rv.getEndLine() == -1 ? "" : ":" + rv.getEndLine()) + this.colorReset + PMD.EOL);
98 buf.append(this.green + " rule: " + this.colorReset + rv.getRule().getName() + PMD.EOL);
99 buf.append(this.green + " msg: " + this.colorReset + rv.getDescription() + PMD.EOL);
100 buf.append(this.green + " code: " + this.colorReset + this.getLine(lastFile, rv.getBeginLine()) + PMD.EOL + PMD.EOL);
101 writer.write(buf.toString());
102 }
103 writer.write(PMD.EOL + PMD.EOL);
104 writer.write("Summary:" + PMD.EOL + PMD.EOL);
105 Map summary = report.getCountSummary();
106 for (Iterator i = summary.entrySet().iterator(); i.hasNext();) {
107 buf.setLength(0);
108 Map.Entry entry = (Map.Entry) i.next();
109 String key = (String) entry.getKey();
110 buf.append(key + " : " + ((Integer) entry.getValue()).intValue() + PMD.EOL);
111 writer.write(buf.toString());
112 }
113
114 for (Iterator i = report.errors(); i.hasNext();) {
115 buf.setLength(0);
116 numberOfErrors++;
117 Report.ProcessingError error = (Report.ProcessingError) i.next();
118 if (error.getFile().equals(lastFile)) {
119 lastFile = error.getFile();
120 buf.append(this.redBold + "*" + this.colorReset + " file: " + this.whiteBold + this.getRelativePath(lastFile) + this.colorReset + PMD.EOL);
121 }
122 buf.append(this.green + " err: " + this.cyan + error.getMsg() + this.colorReset + PMD.EOL + PMD.EOL);
123 writer.write(buf.toString());
124 }
125
126
127 if (numberOfErrors > 0) {
128 writer.write(this.redBold + "*" + this.colorReset + " errors: " + this.whiteBold + numberOfWarnings + this.colorReset + PMD.EOL);
129 }
130 writer.write(this.yellowBold + "*" + this.colorReset + " warnings: " + this.whiteBold + numberOfWarnings + this.colorReset + PMD.EOL);
131 }
132
133 /***
134 * Retrieves the requested line from the specified file.
135 *
136 * @param sourceFile the java or cpp source file
137 * @param line line number to extract
138 * @return a trimmed line of source code
139 */
140 private String getLine(String sourceFile, int line) {
141 String code = null;
142 BufferedReader br = null;
143 try {
144 br = new BufferedReader(new FileReader(new File(sourceFile)));
145 for (int i = 0; line > i; i++) {
146 code = br.readLine().trim();
147 }
148 } catch (IOException ioErr) {
149 ioErr.printStackTrace();
150 } finally {
151 if (br != null) {
152 try {
153 br.close();
154 } catch (IOException ioErr) {
155 ioErr.printStackTrace();
156 }
157 }
158 }
159 return code;
160 }
161
162 /***
163 * Attempts to determine the relative path to the file. If relative path cannot be found,
164 * the original path is returnedi, ie - the current path for the supplied file.
165 *
166 * @param fileName well, the file with its original path.
167 * @return the relative path to the file
168 */
169 private String getRelativePath(String fileName) {
170 String relativePath;
171
172
173 if (pwd == null) {
174 try {
175 this.pwd = new File(".").getCanonicalPath();
176 } catch (IOException ioErr) {
177
178 this.pwd = "";
179 }
180 }
181
182
183 if (fileName.indexOf(this.pwd) == 0) {
184 relativePath = "." + fileName.substring(this.pwd.length());
185
186
187 if (relativePath.startsWith("." + File.separator + "." + File.separator)) {
188 relativePath = relativePath.substring(2);
189 }
190 } else {
191
192
193
194 relativePath = fileName;
195 }
196
197 return relativePath;
198 }
199 }