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 java.io.IOException;
7   import java.io.Writer;
8   import java.text.SimpleDateFormat;
9   import java.util.Date;
10  import java.util.Iterator;
11  
12  import net.sourceforge.pmd.PMD;
13  import net.sourceforge.pmd.Report;
14  import net.sourceforge.pmd.RuleViolation;
15  import net.sourceforge.pmd.lang.rule.properties.StringProperty;
16  import net.sourceforge.pmd.util.StringUtil;
17  
18  /**
19   * Renderer to XML format.
20   */
21  public class XMLRenderer extends AbstractIncrementingRenderer {
22  
23      public static final String NAME = "xml";
24  
25      public static final StringProperty ENCODING = new StringProperty("encoding",
26              "XML encoding format, defaults to UTF-8.", "UTF-8", 0);
27  
28      public XMLRenderer() {
29          super(NAME, "XML format.");
30          definePropertyDescriptor(ENCODING);
31      }
32  
33      public XMLRenderer(String encoding) {
34          this();
35          setProperty(ENCODING, encoding);
36      }
37  
38      public String defaultFileExtension() {
39          return "xml";
40      }
41  
42      /**
43       * {@inheritDoc}
44       */
45      @Override
46      public void start() throws IOException {
47          String encoding = getProperty(ENCODING);
48  
49          Writer writer = getWriter();
50          StringBuilder buf = new StringBuilder(500);
51          buf.append("<?xml version=\"1.0\" encoding=\"" + encoding + "\"?>").append(PMD.EOL);
52          createVersionAttr(buf);
53          createTimestampAttr(buf);
54          // FIXME: elapsed time not available until the end of the processing
55          // buf.append(createTimeElapsedAttr(report));
56          buf.append('>').append(PMD.EOL);
57          writer.write(buf.toString());
58      }
59  
60      /**
61       * {@inheritDoc}
62       */
63      @Override
64      public void renderFileViolations(Iterator<RuleViolation> violations) throws IOException {
65          Writer writer = getWriter();
66          StringBuilder buf = new StringBuilder(500);
67          String filename = null;
68  
69          // rule violations
70          while (violations.hasNext()) {
71              buf.setLength(0);
72              RuleViolation rv = violations.next();
73              if (!rv.getFilename().equals(filename)) { // New File
74                  if (filename != null) {// Not first file ?
75                      buf.append("</file>").append(PMD.EOL);
76                  }
77                  filename = rv.getFilename();
78                  buf.append("<file name=\"");
79                  StringUtil.appendXmlEscaped(buf, filename);
80                  buf.append("\">").append(PMD.EOL);
81              }
82  
83              buf.append("<violation beginline=\"").append(rv.getBeginLine());
84              buf.append("\" endline=\"").append(rv.getEndLine());
85              buf.append("\" begincolumn=\"").append(rv.getBeginColumn());
86              buf.append("\" endcolumn=\"").append(rv.getEndColumn());
87              buf.append("\" rule=\"");
88              StringUtil.appendXmlEscaped(buf, rv.getRule().getName());
89              buf.append("\" ruleset=\"");
90              StringUtil.appendXmlEscaped(buf, rv.getRule().getRuleSetName());
91              buf.append('"');
92              maybeAdd("package", rv.getPackageName(), buf);
93              maybeAdd("class", rv.getClassName(), buf);
94              maybeAdd("method", rv.getMethodName(), buf);
95              maybeAdd("variable", rv.getVariableName(), buf);
96              maybeAdd("externalInfoUrl", rv.getRule().getExternalInfoUrl(), buf);
97              buf.append(" priority=\"");
98              buf.append(rv.getRule().getPriority().getPriority());
99              buf.append("\">").append(PMD.EOL);
100             StringUtil.appendXmlEscaped(buf, rv.getDescription());
101 
102             buf.append(PMD.EOL);
103             buf.append("</violation>");
104             buf.append(PMD.EOL);
105             writer.write(buf.toString());
106         }
107         if (filename != null) { // Not first file ?
108             writer.write("</file>");
109             writer.write(PMD.EOL);
110         }
111     }
112 
113     /**
114      * {@inheritDoc}
115      */
116     @Override
117     public void end() throws IOException {
118         Writer writer = getWriter();
119         StringBuilder buf = new StringBuilder(500);
120         // errors
121         for (Report.ProcessingError pe : errors) {
122             buf.setLength(0);
123             buf.append("<error ").append("filename=\"");
124             StringUtil.appendXmlEscaped(buf, pe.getFile());
125             buf.append("\" msg=\"");
126             StringUtil.appendXmlEscaped(buf, pe.getMsg());
127             buf.append("\"/>").append(PMD.EOL);
128             writer.write(buf.toString());
129         }
130 
131         // suppressed violations
132         if (showSuppressedViolations) {
133             for (Report.SuppressedViolation s : suppressed) {
134                 buf.setLength(0);
135                 buf.append("<suppressedviolation ").append("filename=\"");
136                 StringUtil.appendXmlEscaped(buf, s.getRuleViolation().getFilename());
137                 buf.append("\" suppressiontype=\"");
138                 StringUtil.appendXmlEscaped(buf, s.suppressedByNOPMD() ? "nopmd" : "annotation");
139                 buf.append("\" msg=\"");
140                 StringUtil.appendXmlEscaped(buf, s.getRuleViolation().getDescription());
141                 buf.append("\" usermsg=\"");
142                 StringUtil.appendXmlEscaped(buf, s.getUserMessage() == null ? "" : s.getUserMessage());
143                 buf.append("\"/>").append(PMD.EOL);
144                 writer.write(buf.toString());
145             }
146         }
147 
148         writer.write("</pmd>" + PMD.EOL);
149     }
150 
151     private void maybeAdd(String attr, String value, StringBuilder buf) {
152         if (value != null && value.length() > 0) {
153             buf.append(' ').append(attr).append("=\"");
154             StringUtil.appendXmlEscaped(buf, value);
155             buf.append('"');
156         }
157     }
158 
159     private void createVersionAttr(StringBuilder buffer) {
160         buffer.append("<pmd version=\"").append(PMD.VERSION).append('"');
161     }
162 
163     private void createTimestampAttr(StringBuilder buffer) {
164         buffer.append(" timestamp=\"").append(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS").format(new Date()))
165                 .append('"');
166     }
167 
168     // FIXME: elapsed time not available until the end of the processing
169     /*
170      * private String createTimeElapsedAttr(Report rpt) {
171      * Report.ReadableDuration d = new
172      * Report.ReadableDuration(rpt.getElapsedTimeInMillis()); return
173      * " elapsedTime=\"" + d.getTime() + "\""; }
174      */
175 }