XMLRenderer.java
001 /**
002  * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003  */
004 package net.sourceforge.pmd.renderers;
005 
006 import java.io.IOException;
007 import java.io.Writer;
008 import java.text.SimpleDateFormat;
009 import java.util.Date;
010 import java.util.Iterator;
011 import java.util.Properties;
012 
013 import net.sourceforge.pmd.PMD;
014 import net.sourceforge.pmd.Report;
015 import net.sourceforge.pmd.RuleViolation;
016 import net.sourceforge.pmd.util.StringUtil;
017 
018 /**
019  * Renderer to XML format.
020  */
021 public class XMLRenderer extends AbstractIncrementingRenderer {
022 
023     public static final String NAME = "xml";
024 
025     public static final String ENCODING = "encoding";
026 
027     // FIXME - hardcoded character encoding, booooooo
028     protected String encoding = "UTF-8";
029 
030     public XMLRenderer(Properties properties) {
031   super(NAME, "XML format.", properties);
032   defineProperty(ENCODING, "XML encoding format, defaults to UTF-8.");
033 
034   if (properties.containsKey(ENCODING)) {
035       this.encoding = properties.getProperty(ENCODING);
036   }
037     }
038 
039     /**
040      * {@inheritDoc}
041      */
042     @Override
043     public void start() throws IOException {
044   Writer writer = getWriter();
045   StringBuffer buf = new StringBuffer(500);
046   buf.append("<?xml version=\"1.0\" encoding=\"" this.encoding + "\"?>").append(PMD.EOL);
047   createVersionAttr(buf);
048   createTimestampAttr(buf);
049   // FIXME: elapsed time not available until the end of the processing
050   //buf.append(createTimeElapsedAttr(report));
051   buf.append('>').append(PMD.EOL);
052   writer.write(buf.toString());
053     }
054 
055     /**
056      * {@inheritDoc}
057      */
058     @Override
059     public void renderFileViolations(Iterator<RuleViolation> violationsthrows IOException {
060   Writer writer = getWriter();
061   StringBuffer buf = new StringBuffer(500);
062   String filename = null;
063 
064   // rule violations
065   while (violations.hasNext()) {
066       buf.setLength(0);
067       RuleViolation rv = violations.next();
068       if (!rv.getFilename().equals(filename)) { // New File
069     if (filename != null) {// Not first file ?
070         buf.append("</file>").append(PMD.EOL);
071     }
072     filename = rv.getFilename();
073     buf.append("<file name=\"");
074     StringUtil.appendXmlEscaped(buf, filename);
075     buf.append("\">").append(PMD.EOL);
076       }
077 
078       buf.append("<violation beginline=\"").append(rv.getBeginLine());
079       buf.append("\" endline=\"").append(rv.getEndLine());
080       buf.append("\" begincolumn=\"").append(rv.getBeginColumn());
081       buf.append("\" endcolumn=\"").append(rv.getEndColumn());
082       buf.append("\" rule=\"");
083       StringUtil.appendXmlEscaped(buf, rv.getRule().getName());
084       buf.append("\" ruleset=\"");
085       StringUtil.appendXmlEscaped(buf, rv.getRule().getRuleSetName());
086       buf.append('"');
087       maybeAdd("package", rv.getPackageName(), buf);
088       maybeAdd("class", rv.getClassName(), buf);
089       maybeAdd("method", rv.getMethodName(), buf);
090       maybeAdd("variable", rv.getVariableName(), buf);
091       maybeAdd("externalInfoUrl", rv.getRule().getExternalInfoUrl(), buf);
092       buf.append(" priority=\"");
093       buf.append(rv.getRule().getPriority().getPriority());
094       buf.append("\">").append(PMD.EOL);
095       StringUtil.appendXmlEscaped(buf, rv.getDescription());
096 
097       buf.append(PMD.EOL);
098       buf.append("</violation>");
099       buf.append(PMD.EOL);
100       writer.write(buf.toString());
101   }
102   if (filename != null) { // Not first file ?
103       writer.write("</file>");
104       writer.write(PMD.EOL);
105   }
106     }
107 
108     /**
109      * {@inheritDoc}
110      */
111     @Override
112     public void end() throws IOException {
113   Writer writer = getWriter();
114   StringBuffer buf = new StringBuffer(500);
115   // errors
116   for (Report.ProcessingError pe : errors) {
117       buf.setLength(0);
118       buf.append("<error ").append("filename=\"");
119       StringUtil.appendXmlEscaped(buf, pe.getFile());
120       buf.append("\" msg=\"");
121       StringUtil.appendXmlEscaped(buf, pe.getMsg());
122       buf.append("\"/>").append(PMD.EOL);
123       writer.write(buf.toString());
124   }
125 
126   // suppressed violations
127   if (showSuppressedViolations) {
128       for (Report.SuppressedViolation s : suppressed) {
129     buf.setLength(0);
130     buf.append("<suppressedviolation ").append("filename=\"");
131     StringUtil.appendXmlEscaped(buf, s.getRuleViolation().getFilename());
132     buf.append("\" suppressiontype=\"");
133     StringUtil.appendXmlEscaped(buf, s.suppressedByNOPMD() "nopmd" "annotation");
134     buf.append("\" msg=\"");
135     StringUtil.appendXmlEscaped(buf, s.getRuleViolation().getDescription());
136     buf.append("\" usermsg=\"");
137     StringUtil.appendXmlEscaped(buf, s.getUserMessage() == null "" : s.getUserMessage());
138     buf.append("\"/>").append(PMD.EOL);
139     writer.write(buf.toString());
140       }
141   }
142 
143   writer.write("</pmd>" + PMD.EOL);
144     }
145 
146     private void maybeAdd(String attr, String value, StringBuffer buf) {
147   if (value != null && value.length() 0) {
148       buf.append(' ').append(attr).append("=\"");
149       StringUtil.appendXmlEscaped(buf, value);
150       buf.append('"');
151   }
152     }
153 
154     private void createVersionAttr(StringBuffer buffer) {
155   buffer.append("<pmd version=\"").append(PMD.VERSION).append('"');
156     }
157 
158     private void createTimestampAttr(StringBuffer buffer) {
159   buffer.append(" timestamp=\"").append(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS").format(new Date()))
160     .append('"');
161     }
162 
163     // FIXME: elapsed time not available until the end of the processing
164     /*
165     private String createTimeElapsedAttr(Report rpt) {
166         Report.ReadableDuration d = new Report.ReadableDuration(rpt.getElapsedTimeInMillis());
167         return " elapsedTime=\"" + d.getTime() + "\"";
168     }
169     */
170 }