View Javadoc

1   /*
2    * Sonar, entreprise quality control tool.
3    * Copyright (C) 2007-2008 Hortis-GRC SA
4    * mailto:be_agile HAT hortis DOT ch
5    *
6    * Sonar is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public
8    * License as published by the Free Software Foundation; either
9    * version 3 of the License, or (at your option) any later version.
10   *
11   * Sonar is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   * Lesser General Public License for more details.
15   *
16   * You should have received a copy of the GNU Lesser General Public
17   * License along with Sonar; if not, write to the Free Software
18   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
19   */
20  package org.sonar.plugins.utils;
21  
22  import org.apache.commons.io.IOUtils;
23  import org.slf4j.Logger;
24  import org.slf4j.LoggerFactory;
25  import org.w3c.dom.Document;
26  import org.w3c.dom.Element;
27  import org.w3c.dom.Node;
28  import org.w3c.dom.NodeList;
29  import org.xml.sax.SAXException;
30  
31  import javax.xml.namespace.QName;
32  import javax.xml.parsers.DocumentBuilder;
33  import javax.xml.parsers.DocumentBuilderFactory;
34  import javax.xml.parsers.ParserConfigurationException;
35  import javax.xml.xpath.XPath;
36  import javax.xml.xpath.XPathConstants;
37  import javax.xml.xpath.XPathExpression;
38  import javax.xml.xpath.XPathExpressionException;
39  import javax.xml.xpath.XPathFactory;
40  import java.io.BufferedReader;
41  import java.io.ByteArrayInputStream;
42  import java.io.File;
43  import java.io.FileReader;
44  import java.io.IOException;
45  import java.io.InputStream;
46  import java.io.InputStreamReader;
47  import java.util.ArrayList;
48  import java.util.HashMap;
49  import java.util.List;
50  import java.util.Map;
51  import java.util.regex.Matcher;
52  import java.util.regex.Pattern;
53  
54  public class XmlReportParser {
55  
56    private Element root = null;
57    private Document doc = null;
58    private DocumentBuilder builder;
59    private XPathFactory factory;
60    private XPath xpath;
61    private Map<String, XPathExpression> compiledExprs = new HashMap<String, XPathExpression>();
62  
63    public XmlReportParser() {
64      DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance();
65      try {
66        bf.setFeature("http://apache.org/xml/features/validation/schema", false);
67        bf.setFeature("http://xml.org/sax/features/external-general-entities", false);
68        bf.setFeature("http://xml.org/sax/features/validation", false);
69        bf.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
70        bf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
71        bf.setFeature("http://apache.org/xml/features/allow-java-encodings", true);
72      } catch (ParserConfigurationException e) {
73        Logger log = LoggerFactory.getLogger(this.getClass().getName());
74        log.error("Error occured during features set up.", e);
75      }
76      try {
77        bf.setNamespaceAware(false);
78        bf.setValidating(false);
79        builder = bf.newDocumentBuilder();
80      } catch (ParserConfigurationException e) {
81        throw new XmlParserException("can not instance a XML parser", e);
82      }
83    }
84  
85    public void parse(File file) {
86      try {
87        BufferedReader buffer = new BufferedReader(new FileReader(file));
88        parse(buffer);
89      } catch (IOException e) {
90        throw new XmlParserException("can not parse the file " + file.getAbsolutePath(), e);
91      }
92    }
93  
94    public void parse(InputStream stream)  {
95      try {
96        BufferedReader buffer = new BufferedReader(new InputStreamReader(stream));
97        parse(buffer);
98      } catch (IOException e) {
99        throw new XmlParserException("can not parse the stream", e);
100     }
101   }
102 
103   private void parse(BufferedReader buffer) throws IOException {
104     parse(IOUtils.toString(buffer));
105   }
106 
107   public void parse(String xml) {
108     try {
109       xml = fixUnicodeChar(xml);
110       doc = builder.parse(new ByteArrayInputStream(xml.getBytes()));
111       factory = XPathFactory.newInstance();
112       xpath = factory.newXPath();
113       
114     } catch (SAXException e) {
115       throw new XmlParserException("can not parse xml : " + xml, e);
116     } catch (IOException e) {
117       throw new XmlParserException("can not parse xml : " + xml, e);
118     }
119   }
120 
121   public Element getRoot() {
122     if (root == null && doc != null) {
123       root = doc.getDocumentElement();
124     }
125     return root;
126   }
127   
128   public Document getDocument() {
129     return doc;
130   }
131 
132   public Element getChildElement(Element base, String elementName) {
133     NodeList childrens = base.getElementsByTagName(elementName);
134     for (int i = 0; i < childrens.getLength(); i++) {
135       Node nde = childrens.item(i);
136       if (nde.getNodeType() == Node.ELEMENT_NODE) {
137         return (Element) nde;
138       }
139     }
140     return null;
141   }
142 
143   public Element getChildElement(String elementName) {
144     NodeList childrens = getRoot().getElementsByTagName(elementName);
145     for (int i = 0; i < childrens.getLength(); i++) {
146       Node nde = childrens.item(i);
147       if (nde.getNodeType() == Node.ELEMENT_NODE) {
148         return (Element) nde;
149       }
150     }
151     return null;
152   }
153 
154   public List<Element> getChildElements(String elementName) {
155     List<Element> rtrVal = new ArrayList<Element>();
156     NodeList childrens = getRoot().getElementsByTagName(elementName);
157     for (int i = 0; i < childrens.getLength(); i++) {
158       Node nde = childrens.item(i);
159       if (nde.getNodeType() == Node.ELEMENT_NODE) {
160         rtrVal.add((Element) nde);
161       }
162     }
163     return rtrVal;
164   }
165 
166   public List<Element> getChildElements(Element base, String elementName) {
167     List<Element> rtrVal = new ArrayList<Element>();
168     NodeList childrens = base.getElementsByTagName(elementName);
169     for (int i = 0; i < childrens.getLength(); i++) {
170       Node nde = childrens.item(i);
171       if (nde.getNodeType() == Node.ELEMENT_NODE) {
172         rtrVal.add((Element) nde);
173       }
174     }
175     return rtrVal;
176   }
177 
178   public String getChildElementValue(Element base, String elementName) {
179     NodeList childrens = base.getElementsByTagName(elementName);
180     for (int i = 0; i < childrens.getLength(); i++) {
181       if (childrens.item(i).getNodeType() == Node.ELEMENT_NODE) {
182         return childrens.item(i).getFirstChild().getNodeValue();
183       }
184     }
185     return null;
186   }
187   
188   public String getElementValue(Node base) {
189     if ( base.getNextSibling() != null && base.getNextSibling().getNodeType() == Node.TEXT_NODE ) {
190       return base.getNextSibling().getNodeValue();
191     } else if ( base.getFirstChild() != null && base.getFirstChild().getNodeType() == Node.TEXT_NODE ) {
192       return base.getFirstChild().getNodeValue();
193     }
194     return null;
195   }
196 
197   public String getChildElementValue(String elementName) {
198     NodeList childrens = getRoot().getElementsByTagName(elementName);
199     for (int i = 0; i < childrens.getLength(); i++) {
200       if (childrens.item(i).getNodeType() == Node.ELEMENT_NODE) {
201         return childrens.item(i).getFirstChild().getNodeValue();
202       }
203     }
204     return null;
205   }
206 
207   public Object executeXPath(Node node, QName qname, String xPathExpression) {
208     XPathExpression expr = compiledExprs.get(xPathExpression);
209     try {
210       if ( expr == null ) {
211         expr = xpath.compile(xPathExpression);
212         compiledExprs.put(xPathExpression, expr);
213       }
214       return expr.evaluate(node, qname);
215     } catch (XPathExpressionException e) {
216       throw new XmlParserException("Unable to evaluate xpath expression :" + xPathExpression, e);
217     }
218   }
219 
220   public String executeXPath(String xPathExpression) {
221     return (String) executeXPath(doc, XPathConstants.STRING, xPathExpression);
222   }
223 
224   public String executeXPath(Node node, String xPathExpression) {
225     return (String) executeXPath(node, XPathConstants.STRING, xPathExpression);
226   }
227 
228   public NodeList executeXPathNodeList(String xPathExpression)  {
229     return (NodeList) executeXPath(doc, XPathConstants.NODESET, xPathExpression);
230   }
231 
232   public NodeList executeXPathNodeList(Node node, String xPathExpression) {
233     return (NodeList) executeXPath(node, XPathConstants.NODESET, xPathExpression);
234   }
235   
236   public Node executeXPathNode(Node node, String xPathExpression) {
237     return (Node) executeXPath(node, XPathConstants.NODE, xPathExpression);
238   }
239 
240   /**
241    * Fix the error occured when parsing a string containing unicode character
242    * Example : &u20ac; will be replaced by &#x20ac;
243    */
244   protected String fixUnicodeChar(String text){
245     String unicode = "&u";
246     StringBuilder replace = new StringBuilder(text);
247     if (text.indexOf(unicode) >= 0) {
248       Pattern p = Pattern.compile("&u([0-9a-fA-F]{1,4});");
249       Matcher m = p.matcher(replace.toString());
250       int nbFind = 0;
251       while (m.find()) {
252         // Add one index each time because we add one character each time (&u -> &#x)
253         replace.replace(m.start() + nbFind, m.end() + nbFind, "&#x" + m.group(1) + ";");
254         nbFind++;
255       }
256     }
257     return replace.toString();
258   }
259 }