View Javadoc
1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.cpd;
5   
6   import java.io.StringWriter;
7   import java.util.Iterator;
8   
9   import javax.xml.parsers.DocumentBuilder;
10  import javax.xml.parsers.DocumentBuilderFactory;
11  import javax.xml.parsers.ParserConfigurationException;
12  import javax.xml.transform.OutputKeys;
13  import javax.xml.transform.Transformer;
14  import javax.xml.transform.TransformerException;
15  import javax.xml.transform.TransformerFactory;
16  import javax.xml.transform.dom.DOMSource;
17  import javax.xml.transform.stream.StreamResult;
18  
19  import org.w3c.dom.Document;
20  import org.w3c.dom.Element;
21  
22  /**
23   * @author Philippe T'Seyen - original implementation
24   * @author Romain Pelisse - javax.xml implementation
25   * 
26   */
27  public final class XMLRenderer implements Renderer {
28  
29      private String encoding;
30  
31      /**
32       * Creates a XML Renderer with the default (platform dependent) encoding.
33       */
34      public XMLRenderer() {
35          this(null);
36      }
37  
38      /**
39       * Creates a XML Renderer with a specific output encoding.
40       * 
41       * @param encoding the encoding to use or null. If null, default (platform
42       *            dependent) encoding is used.
43       */
44      public XMLRenderer(String encoding) {
45          setEncoding(encoding);
46      }
47  
48      public void setEncoding(String encoding) {
49          if (encoding != null) {
50              this.encoding = encoding;
51          } else {
52              this.encoding = System.getProperty("file.encoding");
53          }
54      }
55  
56      public String getEncoding() {
57          return this.encoding;
58      }
59  
60      private Document createDocument() {
61          try {
62              DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
63              DocumentBuilder parser = factory.newDocumentBuilder();
64              return parser.newDocument();
65          } catch (ParserConfigurationException e) {
66              throw new IllegalStateException(e);
67          }
68      }
69  
70      private String xmlDocToString(Document doc) {
71          try {
72              TransformerFactory tf = TransformerFactory.newInstance();
73              Transformer transformer = tf.newTransformer();
74              transformer.setOutputProperty(OutputKeys.METHOD, "xml");
75              transformer.setOutputProperty(OutputKeys.ENCODING, encoding);
76              transformer.setOutputProperty(OutputKeys.INDENT, "yes");
77              transformer.setOutputProperty(OutputKeys.CDATA_SECTION_ELEMENTS, "codefragment");
78              StringWriter writer = new StringWriter();
79              transformer.transform(new DOMSource(doc), new StreamResult(writer));
80              return writer.getBuffer().toString();
81          } catch (TransformerException e) {
82              throw new IllegalStateException(e);
83          }
84      }
85  
86      public String render(Iterator<Match> matches) {
87          Document doc = createDocument();
88          Element root = doc.createElement("pmd-cpd");
89          doc.appendChild(root);
90  
91          Match match;
92          while (matches.hasNext()) {
93              match = matches.next();
94              root.appendChild(addCodeSnippet(doc,
95                      addFilesToDuplicationElement(doc, createDuplicationElement(doc, match), match), match));
96          }
97          return xmlDocToString(doc);
98      }
99  
100     private Element addFilesToDuplicationElement(Document doc, Element duplication, Match match) {
101         Mark mark;
102         for (Iterator<Mark> iterator = match.iterator(); iterator.hasNext();) {
103             mark = iterator.next();
104             Element file = doc.createElement("file");
105             file.setAttribute("line", String.valueOf(mark.getBeginLine()));
106             file.setAttribute("path", mark.getFilename());
107             duplication.appendChild(file);
108         }
109         return duplication;
110     }
111 
112     private Element addCodeSnippet(Document doc, Element duplication, Match match) {
113         String codeSnipet = match.getSourceCodeSlice();
114         if (codeSnipet != null) {
115             Element codefragment = doc.createElement("codefragment");
116             codefragment.appendChild(doc.createCDATASection(codeSnipet));
117             duplication.appendChild(codefragment);
118         }
119         return duplication;
120     }
121 
122     private Element createDuplicationElement(Document doc, Match match) {
123         Element duplication = doc.createElement("duplication");
124         duplication.setAttribute("lines", String.valueOf(match.getLineCount()));
125         duplication.setAttribute("tokens", String.valueOf(match.getTokenCount()));
126         return duplication;
127     }
128 }