/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.renderers;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.RuleViolation;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Iterator;
/**
* <p>A console renderer with optional color support under *nix systems.</p>
*
* <pre>
* * file: ./src/gilot/Test.java
* src: Test.java:12
* rule: AtLeastOneConstructor
* msg: Each class should declare at least one constructor
* code: public class Test
*
* * file: ./src/gilot/log/format/LogInterpreter.java
* src: LogInterpreter.java:317
* rule: AvoidDuplicateLiterals
* msg: The same String literal appears 4 times in this file; the first occurrence is on line 317
* code: logger.error( "missing attribute 'app_arg' in rule '" + ((Element)element.getParent()).getAttributeValue( "name" ) + "'" );
*
* src: LogInterpreter.java:317
* rule: AvoidDuplicateLiterals
* msg: The same String literal appears 5 times in this file; the first occurrence is on line 317
* code: logger.error( "missing attribute 'app_arg' in rule '" + ((Element)element.getParent()).getAttributeValue( "name" ) + "'" );
*
* * warnings: 3
*
* </pre>
*
* <p>Colorization is turned on by supplying -D<b>pmd.color</b> - any value other than
* '0' or 'false', enables color - including an empty value (''). <b>Nota Bene:</b>
* colorization is atm only supported under *nix terminals accepting ansi escape
* sequences, such as xterm, rxvt et cetera.</p>
*/
public class PapariTextRenderer implements Renderer
{
/**
* Directory from where java was invoked.
*/
private String pwd = null;
private String yellowBold = "";
private String whiteBold = "";
private String redBold = "";
private String cyan = "";
private String green = "";
private String colorReset = "";
/**
* Enables colors on *nix systems - not windows. Color support depends
* on the pmd.color property, which should be set with the -D option
* during execution - a set value other than 'false' or '0' enables color.
*
* btw, is it possible to do this on windows (ie; console colors)?
*/
private void initColors()
{
if (System.getProperty("pmd.color") != null &&
!(System.getProperty("pmd.color").equals("0") || System.getProperty("pmd.color").equals("false")))
{
this.yellowBold = "\u001B[1;33m";
this.whiteBold = "\u001B[1;37m";
this.redBold = "\u001B[1;31m";
this.green = "\u001B[0;32m";
this.cyan = "\u001B[0;36m";
this.colorReset = "\u001B[0m";
}
}
public String render(Report report)
{
StringBuffer buf = new StringBuffer(PMD.EOL);
// init colors, if supported
this.initColors();
// last file
String fileName = null;
// keeps track of violations and errors
int errors = 0;
int warnings = 0;
// iterating rule violations
for (Iterator i = report.iterator(); i.hasNext();)
{
warnings++;
RuleViolation rv = (RuleViolation) i.next();
if (!rv.getFilename().equals(fileName))
{
fileName = rv.getFilename();
buf.append( this.yellowBold + "*" + this.colorReset + " file: " + this.whiteBold + this.getRelativePath(fileName) + this.colorReset + PMD.EOL);
}
buf.append(this.green + " src: " + this.cyan + fileName.substring( fileName.lastIndexOf(File.separator)+1)+ this.colorReset + ":" + this.cyan + rv.getLine() + this.colorReset + PMD.EOL);
buf.append(this.green + " rule: " + this.colorReset + rv.getRule().getName() + PMD.EOL);
buf.append(this.green + " msg: " + this.colorReset + rv.getDescription() + PMD.EOL);
buf.append(this.green + " code: " + this.colorReset + this.getLine( fileName, rv.getLine() ) + PMD.EOL + PMD.EOL);
}
// iterating errors
for (Iterator i = report.errors(); i.hasNext();)
{
errors++;
Report.ProcessingError error = (Report.ProcessingError)i.next();
if (error.getFile().equals(fileName))
{
fileName = error.getFile();
buf.append( this.redBold + "*" + this.colorReset + " file: " + this.whiteBold + this.getRelativePath(fileName) + this.colorReset + PMD.EOL);
}
buf.append(this.green + " err: " + this.cyan + error.getMsg() + this.colorReset + PMD.EOL + PMD.EOL);
}
// adding error message count, if any
if ( errors > 0 )
{
buf.append(this.redBold + "*" + this.colorReset + " errors: "+ this.whiteBold + warnings + this.colorReset + PMD.EOL);
}
buf.append(this.yellowBold + "*" + this.colorReset + " warnings: "+ this.whiteBold + warnings + this.colorReset + PMD.EOL);
return buf.toString();
}
/**
* Retrieves the requested line from the specified file.
*
* @param sourceFile the java or cpp source file
* @param line line number to extract
*
* @return a trimmed line of source code
*/
private String getLine( String sourceFile, int line )
{
String code = null;
try
{
File file = new File( "." );
BufferedReader br = new BufferedReader( new FileReader( new File( sourceFile ) ) );
for ( int i = 0; line > i; i++ )
{
code = br.readLine().trim();
}
br.close();
}
catch ( IOException ioErr )
{
ioErr.printStackTrace();
}
return code;
}
/**
* Attempts to determine the relative path to the file. If relative path cannot be found,
* the original path is returnedi, ie - the current path for the supplied file.
*
* @param fileName well, the file with its original path.
* @return the relative path to the file
*/
private String getRelativePath( String fileName )
{
String relativePath = null;
// check if working directory need to be assigned
if (pwd == null)
{
try
{
this.pwd = new File(".").getCanonicalPath();
}
catch (IOException ioErr)
{
// to avoid further error
this.pwd = "";
}
}
// make sure that strings match before doing any substring-ing
if (fileName.indexOf(this.pwd) == 0)
{
relativePath = "." + fileName.substring( this.pwd.length() );
// remove current dir occuring twice - occurs if . was supplied as path
if ( relativePath.startsWith( "." + File.separator + "." + File.separator ) )
{
relativePath = relativePath.substring(2);
}
}
else
{
// this happens when pmd's supplied argument deviates from the pwd 'branch' (god knows this terminolgy - i hope i make some sense).
// for instance, if supplied=/usr/lots/of/src and pwd=/usr/lots/of/shared/source
// TODO: a fix to get relative path?
relativePath = fileName;
}
return relativePath;
}
}