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 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         // adding error message count, if any
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         // check if working directory need to be assigned
167         if (pwd == null) {
168             try {
169                 this.pwd = new File(".").getCanonicalPath();
170             } catch (IOException ioErr) {
171                 // to avoid further error
172                 this.pwd = "";
173             }
174         }
175 
176         // make sure that strings match before doing any substring-ing
177         if (fileName.indexOf(this.pwd) == 0) {
178             relativePath = "." + fileName.substring(this.pwd.length());
179 
180             // remove current dir occuring twice - occurs if . was supplied as path
181             if (relativePath.startsWith("." + File.separator + "." + File.separator)) {
182                 relativePath = relativePath.substring(2);
183             }
184         } else {
185             // this happens when pmd's supplied argument deviates from the pwd 'branch' (god knows this terminolgy - i hope i make some sense).
186             // for instance, if supplied=/usr/lots/of/src and pwd=/usr/lots/of/shared/source
187             // TODO: a fix to get relative path?
188             relativePath = fileName;
189         }
190 
191         return relativePath;
192     }
193 }