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