/** * 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.text.SimpleDateFormat; import java.util.Date; import java.util.Iterator; 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 XML format. */ public class XMLRenderer extends AbstractIncrementingRenderer { public static final String NAME = "xml"; public static final StringProperty ENCODING = new StringProperty("encoding", "XML encoding format, defaults to UTF-8.", "UTF-8", 0); private boolean useUTF8 = false; public XMLRenderer() { super(NAME, "XML format."); definePropertyDescriptor(ENCODING); } public XMLRenderer(String encoding) { this(); setProperty(ENCODING, encoding); } @Override public String defaultFileExtension() { return "xml"; } /** * {@inheritDoc} */ @Override public void start() throws IOException { String encoding = getProperty(ENCODING); if ("utf-8".equalsIgnoreCase(encoding)) { useUTF8 = true; } Writer writer = getWriter(); StringBuilder buf = new StringBuilder(500); buf.append("<?xml version=\"1.0\" encoding=\"" + encoding + "\"?>").append(PMD.EOL); createVersionAttr(buf); createTimestampAttr(buf); // FIXME: elapsed time not available until the end of the processing // buf.append(createTimeElapsedAttr(report)); buf.append('>').append(PMD.EOL); writer.write(buf.toString()); } /** * {@inheritDoc} */ @Override public void renderFileViolations(Iterator<RuleViolation> violations) throws IOException { Writer writer = getWriter(); StringBuilder buf = new StringBuilder(500); String filename = null; // rule violations while (violations.hasNext()) { buf.setLength(0); RuleViolation rv = violations.next(); if (!rv.getFilename().equals(filename)) { // New File if (filename != null) { // Not first file ? buf.append("</file>").append(PMD.EOL); } filename = rv.getFilename(); buf.append("<file name=\""); StringUtil.appendXmlEscaped(buf, filename, useUTF8); buf.append("\">").append(PMD.EOL); } buf.append("<violation beginline=\"").append(rv.getBeginLine()); buf.append("\" endline=\"").append(rv.getEndLine()); buf.append("\" begincolumn=\"").append(rv.getBeginColumn()); buf.append("\" endcolumn=\"").append(rv.getEndColumn()); buf.append("\" rule=\""); StringUtil.appendXmlEscaped(buf, rv.getRule().getName(), useUTF8); buf.append("\" ruleset=\""); StringUtil.appendXmlEscaped(buf, rv.getRule().getRuleSetName(), useUTF8); buf.append('"'); maybeAdd("package", rv.getPackageName(), buf); maybeAdd("class", rv.getClassName(), buf); maybeAdd("method", rv.getMethodName(), buf); maybeAdd("variable", rv.getVariableName(), buf); maybeAdd("externalInfoUrl", rv.getRule().getExternalInfoUrl(), buf); buf.append(" priority=\""); buf.append(rv.getRule().getPriority().getPriority()); buf.append("\">").append(PMD.EOL); StringUtil.appendXmlEscaped(buf, rv.getDescription(), useUTF8); buf.append(PMD.EOL); buf.append("</violation>"); buf.append(PMD.EOL); writer.write(buf.toString()); } if (filename != null) { // Not first file ? writer.write("</file>"); writer.write(PMD.EOL); } } /** * {@inheritDoc} */ @Override public void end() throws IOException { Writer writer = getWriter(); StringBuilder buf = new StringBuilder(500); // errors for (Report.ProcessingError pe : errors) { buf.setLength(0); buf.append("<error ").append("filename=\""); StringUtil.appendXmlEscaped(buf, pe.getFile(), useUTF8); buf.append("\" msg=\""); StringUtil.appendXmlEscaped(buf, pe.getMsg(), useUTF8); buf.append("\"/>").append(PMD.EOL); writer.write(buf.toString()); } // suppressed violations if (showSuppressedViolations) { for (Report.SuppressedViolation s : suppressed) { buf.setLength(0); buf.append("<suppressedviolation ").append("filename=\""); StringUtil.appendXmlEscaped(buf, s.getRuleViolation().getFilename(), useUTF8); buf.append("\" suppressiontype=\""); StringUtil.appendXmlEscaped(buf, s.suppressedByNOPMD() ? "nopmd" : "annotation", useUTF8); buf.append("\" msg=\""); StringUtil.appendXmlEscaped(buf, s.getRuleViolation().getDescription(), useUTF8); buf.append("\" usermsg=\""); StringUtil.appendXmlEscaped(buf, s.getUserMessage() == null ? "" : s.getUserMessage(), useUTF8); buf.append("\"/>").append(PMD.EOL); writer.write(buf.toString()); } } writer.write("</pmd>" + PMD.EOL); } private void maybeAdd(String attr, String value, StringBuilder buf) { if (value != null && value.length() > 0) { buf.append(' ').append(attr).append("=\""); StringUtil.appendXmlEscaped(buf, value, useUTF8); buf.append('"'); } } private void createVersionAttr(StringBuilder buffer) { buffer.append("<pmd version=\"").append(PMD.VERSION).append('"'); } private void createTimestampAttr(StringBuilder buffer) { buffer.append(" timestamp=\"").append(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS").format(new Date())) .append('"'); } // FIXME: elapsed time not available until the end of the processing /* * private String createTimeElapsedAttr(Report rpt) { * Report.ReadableDuration d = new * Report.ReadableDuration(rpt.getElapsedTimeInMillis()); return * " elapsedTime=\"" + d.getTime() + "\""; } */ }