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