/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.cpd;
import java.io.StringWriter;
import java.util.Iterator;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* @author Philippe T'Seyen - original implementation
* @author Romain Pelisse - javax.xml implementation
*
*/
public final class XMLRenderer implements Renderer {
private String encoding;
/**
* Creates a XML Renderer with the default (platform dependent) encoding.
*/
public XMLRenderer() {
this(null);
}
/**
* Creates a XML Renderer with a specific output encoding.
*
* @param encoding
* the encoding to use or null. If null, default (platform
* dependent) encoding is used.
*/
public XMLRenderer(String encoding) {
setEncoding(encoding);
}
public void setEncoding(String encoding) {
if (encoding != null) {
this.encoding = encoding;
} else {
this.encoding = System.getProperty("file.encoding");
}
}
public String getEncoding() {
return this.encoding;
}
private Document createDocument() {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder parser = factory.newDocumentBuilder();
return parser.newDocument();
} catch (ParserConfigurationException e) {
throw new IllegalStateException(e);
}
}
private String xmlDocToString(Document doc) {
try {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.ENCODING, encoding);
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.CDATA_SECTION_ELEMENTS, "codefragment");
StringWriter writer = new StringWriter();
transformer.transform(new DOMSource(doc), new StreamResult(writer));
return writer.toString();
} catch (TransformerException e) {
throw new IllegalStateException(e);
}
}
@Override
public String render(Iterator<Match> matches) {
Document doc = createDocument();
Element root = doc.createElement("pmd-cpd");
doc.appendChild(root);
Match match;
while (matches.hasNext()) {
match = matches.next();
root.appendChild(addCodeSnippet(doc,
addFilesToDuplicationElement(doc, createDuplicationElement(doc, match), match), match));
}
return xmlDocToString(doc);
}
private Element addFilesToDuplicationElement(Document doc, Element duplication, Match match) {
Mark mark;
for (Iterator<Mark> iterator = match.iterator(); iterator.hasNext();) {
mark = iterator.next();
Element file = doc.createElement("file");
file.setAttribute("line", String.valueOf(mark.getBeginLine()));
file.setAttribute("path", mark.getFilename());
duplication.appendChild(file);
}
return duplication;
}
private Element addCodeSnippet(Document doc, Element duplication, Match match) {
String codeSnipet = match.getSourceCodeSlice();
if (codeSnipet != null) {
Element codefragment = doc.createElement("codefragment");
codefragment.appendChild(doc.createCDATASection(codeSnipet));
duplication.appendChild(codefragment);
}
return duplication;
}
private Element createDuplicationElement(Document doc, Match match) {
Element duplication = doc.createElement("duplication");
duplication.setAttribute("lines", String.valueOf(match.getLineCount()));
duplication.setAttribute("tokens", String.valueOf(match.getTokenCount()));
return duplication;
}
}