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