/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.renderers;
import java.io.IOException;
import java.io.Writer;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.lang3.StringEscapeUtils;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.lang.rule.properties.StringProperty;
import net.sourceforge.pmd.util.StringUtil;
/**
* Renderer to basic HTML format.
*
* FIXME: this class should just work with the XMLRenderer and then apply an
* XSLT transformation + stylesheet. No need to hard-code HTML markup here.
*/
public class HTMLRenderer extends AbstractIncrementingRenderer {
public static final String NAME = "html";
public static final StringProperty LINE_PREFIX = new StringProperty("linePrefix",
"Prefix for line number anchor in the source file.", null, 1);
public static final StringProperty LINK_PREFIX = new StringProperty("linkPrefix", "Path to HTML source.", null, 0);
private String linkPrefix;
private String linePrefix;
private int violationCount = 1;
boolean colorize = true;
public HTMLRenderer() {
super(NAME, "HTML format");
definePropertyDescriptor(LINK_PREFIX);
definePropertyDescriptor(LINE_PREFIX);
}
@Override
public String defaultFileExtension() {
return "html";
}
/**
* Write the body of the main body of the HTML content.
*
* @param writer
* @param report
* @throws IOException
*/
public void renderBody(Writer writer, Report report) throws IOException {
linkPrefix = getProperty(LINK_PREFIX);
linePrefix = getProperty(LINE_PREFIX);
writer.write("<center><h3>PMD report</h3></center>");
writer.write("<center><h3>Problems found</h3></center>");
writer.write("<table align=\"center\" cellspacing=\"0\" cellpadding=\"3\"><tr>" + PMD.EOL
+ "<th>#</th><th>File</th><th>Line</th><th>Problem</th></tr>" + PMD.EOL);
setWriter(writer);
renderFileReport(report);
writer.write("</table>");
glomProcessingErrors(writer, errors);
if (showSuppressedViolations) {
glomSuppressions(writer, suppressed);
}
}
/**
* {@inheritDoc}
*/
@Override
public void start() throws IOException {
Writer writer = getWriter();
writer.write("<html><head><title>PMD</title></head><body>" + PMD.EOL);
writer.write("<center><h3>PMD report</h3></center>");
writer.write("<center><h3>Problems found</h3></center>");
writer.write("<table align=\"center\" cellspacing=\"0\" cellpadding=\"3\"><tr>" + PMD.EOL
+ "<th>#</th><th>File</th><th>Line</th><th>Problem</th></tr>" + PMD.EOL);
}
/**
* {@inheritDoc}
*/
@Override
public void renderFileViolations(Iterator<RuleViolation> violations) throws IOException {
Writer writer = getWriter();
glomRuleViolations(writer, violations);
}
/**
* {@inheritDoc}
*/
@Override
public void end() throws IOException {
Writer writer = getWriter();
writer.write("</table>");
glomProcessingErrors(writer, errors);
if (showSuppressedViolations) {
glomSuppressions(writer, suppressed);
}
writer.write("</body></html>" + PMD.EOL);
}
private void glomRuleViolations(Writer writer, Iterator<RuleViolation> violations) throws IOException {
StringBuilder buf = new StringBuilder(500);
while (violations.hasNext()) {
RuleViolation rv = violations.next();
buf.setLength(0);
buf.append("<tr");
if (colorize) {
buf.append(" bgcolor=\"lightgrey\"");
}
colorize = !colorize;
buf.append("> " + PMD.EOL);
buf.append("<td align=\"center\">" + violationCount + "</td>" + PMD.EOL);
buf.append("<td width=\"*%\">"
+ maybeWrap(StringEscapeUtils.escapeHtml4(rv.getFilename()),
linePrefix == null ? "" : linePrefix + Integer.toString(rv.getBeginLine()))
+ "</td>" + PMD.EOL);
buf.append("<td align=\"center\" width=\"5%\">" + Integer.toString(rv.getBeginLine()) + "</td>" + PMD.EOL);
String d = StringEscapeUtils.escapeHtml4(rv.getDescription());
String infoUrl = rv.getRule().getExternalInfoUrl();
if (StringUtil.isNotEmpty(infoUrl)) {
d = "<a href=\"" + infoUrl + "\">" + d + "</a>";
}
buf.append("<td width=\"*\">" + d + "</td>" + PMD.EOL);
buf.append("</tr>" + PMD.EOL);
writer.write(buf.toString());
violationCount++;
}
}
private void glomProcessingErrors(Writer writer, List<Report.ProcessingError> errors) throws IOException {
if (errors.isEmpty()) {
return;
}
writer.write("<hr/>");
writer.write("<center><h3>Processing errors</h3></center>");
writer.write("<table align=\"center\" cellspacing=\"0\" cellpadding=\"3\"><tr>" + PMD.EOL
+ "<th>File</th><th>Problem</th></tr>" + PMD.EOL);
StringBuffer buf = new StringBuffer(500);
boolean colorize = true;
for (Report.ProcessingError pe : errors) {
buf.setLength(0);
buf.append("<tr");
if (colorize) {
buf.append(" bgcolor=\"lightgrey\"");
}
colorize = !colorize;
buf.append("> " + PMD.EOL);
buf.append("<td>" + pe.getFile() + "</td>" + PMD.EOL);
buf.append("<td>" + pe.getMsg() + "</td>" + PMD.EOL);
buf.append("</tr>" + PMD.EOL);
writer.write(buf.toString());
}
writer.write("</table>");
}
private void glomSuppressions(Writer writer, List<Report.SuppressedViolation> suppressed) throws IOException {
if (suppressed.isEmpty()) {
return;
}
writer.write("<hr/>");
writer.write("<center><h3>Suppressed warnings</h3></center>");
writer.write("<table align=\"center\" cellspacing=\"0\" cellpadding=\"3\"><tr>" + PMD.EOL
+ "<th>File</th><th>Line</th><th>Rule</th><th>NOPMD or Annotation</th><th>Reason</th></tr>" + PMD.EOL);
StringBuilder buf = new StringBuilder(500);
boolean colorize = true;
for (Report.SuppressedViolation sv : suppressed) {
buf.setLength(0);
buf.append("<tr");
if (colorize) {
buf.append(" bgcolor=\"lightgrey\"");
}
colorize = !colorize;
buf.append("> " + PMD.EOL);
buf.append("<td align=\"left\">" + sv.getRuleViolation().getFilename() + "</td>" + PMD.EOL);
buf.append("<td align=\"center\">" + sv.getRuleViolation().getBeginLine() + "</td>" + PMD.EOL);
buf.append("<td align=\"center\">" + sv.getRuleViolation().getRule().getName() + "</td>" + PMD.EOL);
buf.append("<td align=\"center\">" + (sv.suppressedByNOPMD() ? "NOPMD" : "Annotation") + "</td>" + PMD.EOL);
buf.append("<td align=\"center\">" + (sv.getUserMessage() == null ? "" : sv.getUserMessage()) + "</td>"
+ PMD.EOL);
buf.append("</tr>" + PMD.EOL);
writer.write(buf.toString());
}
writer.write("</table>");
}
private String maybeWrap(String filename, String line) {
if (StringUtil.isEmpty(linkPrefix)) {
return filename;
}
String newFileName = filename;
int index = filename.lastIndexOf('.');
if (index >= 0) {
newFileName = filename.substring(0, index).replace('\\', '/');
}
return "<a href=\"" + linkPrefix + newFileName + ".html#" + line + "\">" + newFileName + "</a>";
}
}