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> violations) throws 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 }
|