XMLRenderer.java
01 /**
02  * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
03  */
04 package net.sourceforge.pmd.cpd;
05 
06 import net.sourceforge.pmd.PMD;
07 import net.sourceforge.pmd.util.StringUtil;
08 
09 import java.util.Iterator;
10 
11 /**
12  @author Philippe T'Seyen
13  */
14 public class XMLRenderer implements Renderer {
15 
16     private String encoding;
17 
18     public XMLRenderer() {
19         this(System.getProperty("file.encoding"));
20     }
21     
22     public XMLRenderer(String e) {
23         this.encoding = e;
24     }
25     
26   // FUTURE: Use a XML API - rather than StringBuffer to generate XML Report. 
27     // The most convenient would be to use one shipped in the JRE, and this should
28     // also allow us to get ride of the encode below, as the XML API choosed will
29     // do that for us...
30     public String render(Iterator<Match> matches) {
31         StringBuffer buffer = new StringBuffer(300);
32         buffer.append("<?xml version=\"1.0\" encoding=\"");
33         buffer.append(encoding);
34         buffer.append("\"?>").append(PMD.EOL);
35         buffer.append("<pmd-cpd>").append(PMD.EOL);
36         Match match;
37         while (matches.hasNext()) {
38             match = matches.next();
39             buffer.append("<duplication lines=\"");
40             buffer.append(match.getLineCount());
41             buffer.append("\" tokens=\"");
42             buffer.append(match.getTokenCount());
43             buffer.append("\">").append(PMD.EOL);
44 
45             TokenEntry mark;
46             for (Iterator<TokenEntry> iterator = match.iterator(); iterator.hasNext();) {
47                 mark = iterator.next();
48                 buffer.append("<file line=\"");
49                 buffer.append(mark.getBeginLine());
50                 buffer.append("\" path=\"");
51                 buffer.append(XMLRenderer.encode(mark.getTokenSrcID()));
52                 buffer.append("\"/>").append(PMD.EOL);
53             }
54             String codeFragment = match.getSourceCodeSlice();
55             if (codeFragment != null) {
56                 buffer.append("<codefragment>").append(PMD.EOL);
57                 buffer.append("<![CDATA[").append(PMD.EOL);
58                 buffer.append(StringUtil.replaceString(codeFragment, "]]>""]]&gt;")).append(PMD.EOL + "]]>" + PMD.EOL + "</codefragment>" + PMD.EOL);
59             }
60             buffer.append("</duplication>").append(PMD.EOL);
61         }
62         buffer.append("</pmd-cpd>");
63         return buffer.toString();
64     }
65 
66     /*
67     * <p>Fixes bug : https://sourceforge.net/tracker/?func=detail&aid=2832322&group_id=56262&atid=479921</p>
68     
69     * TODO: The following method - and its static arrays - should
70     * most likely be place somewhere else, like some kind of utility
71     * classes to solve issue on encoding.
72   */
73   private static String encode(String path) {
74     for int i = 0; i < BASIC_ESCAPE.length; i++ ) {
75       if path.indexOf(BASIC_ESCAPE[i][0]) != -) {
76         path = path.replaceAll(BASIC_ESCAPE[i][0],BASIC_ESCAPE[i][1]);
77       }
78     }
79     return path;
80   }
81   
82   /* 
83    * Cut'n'paster from Apache Commons Lang
84    
85    */
86   public static final String[][] BASIC_ESCAPE = {
87         {"\"""&quot;"}// " - double-quote
88         {"&""&amp;"},   // & - ampersand
89         {"<""&lt;"},    // < - less-than
90         {">""&gt;"},    // > - greater-than
91     };
92 }