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 }