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.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          // init colors, if supported
88          this.initColors();
89  
90          // last file
91          String fileName = null;
92  
93          // keeps track of violations and errors
94          int errors = 0;
95          int warnings = 0;
96  
97          // iterating rule violations
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         // iterating errors
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         // adding error message count, if any
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         // check if working directory need to be assigned
181         if (pwd == null)
182         {
183             try
184             {
185                 this.pwd = new File(".").getCanonicalPath();
186             }
187             catch (IOException ioErr)
188             {
189                 // to avoid further error
190                 this.pwd = "";
191             }
192         }
193 
194         // make sure that strings match before doing any substring-ing
195         if (fileName.indexOf(this.pwd) == 0)
196         {
197             relativePath = "." + fileName.substring( this.pwd.length() );
198 
199             // remove current dir occuring twice - occurs if . was supplied as path
200             if ( relativePath.startsWith( "." + File.separator + "." + File.separator ) )
201             {
202                 relativePath = relativePath.substring(2);
203             }
204         }
205 		else
206 		{
207 			// this happens when pmd's supplied argument deviates from the pwd 'branch' (god knows this terminolgy - i hope i make some sense).
208 			// for instance, if supplied=/usr/lots/of/src and pwd=/usr/lots/of/shared/source
209 			// TODO: a fix to get relative path?
210 			relativePath = fileName;
211 		}
212 
213         return relativePath;
214     }
215 }