1 | /** |
2 | * BSD-style license; for more info see http://xradar.sourceforge.net/license.html |
3 | */ |
4 | package org.sourceforge.xradar.results; |
5 | |
6 | import java.io.IOException; |
7 | import java.io.InputStream; |
8 | import java.util.ArrayList; |
9 | import java.util.List; |
10 | import java.util.logging.Logger; |
11 | |
12 | import javax.xml.parsers.DocumentBuilder; |
13 | import javax.xml.parsers.DocumentBuilderFactory; |
14 | import javax.xml.parsers.ParserConfigurationException; |
15 | import javax.xml.xpath.XPath; |
16 | import javax.xml.xpath.XPathConstants; |
17 | import javax.xml.xpath.XPathExpression; |
18 | import javax.xml.xpath.XPathExpressionException; |
19 | import javax.xml.xpath.XPathFactory; |
20 | |
21 | import org.w3c.dom.Document; |
22 | import org.w3c.dom.Node; |
23 | import org.w3c.dom.NodeList; |
24 | import org.xml.sax.SAXException; |
25 | |
26 | /** |
27 | * <p>Service to execute XPath query on the provided input stream.</p> |
28 | * |
29 | * @author Romain PELISSE, <belaran@gmail.com> |
30 | */ |
31 | public class XPathQueryAnalyser implements Analyser { |
32 | |
33 | private final static Logger logger = Logger.getLogger(XPathQueryAnalyser.class.getSimpleName()); |
34 | |
35 | private DocumentBuilder builder; |
36 | private XPath xpath; |
37 | private InputStream inputStream; |
38 | private Document doc; |
39 | |
40 | |
41 | /** |
42 | * @return the inputstream |
43 | */ |
44 | public InputStream getInputStream() { |
45 | return inputStream; |
46 | } |
47 | |
48 | /** |
49 | * |
50 | * @param inputStream |
51 | */ |
52 | public void setInputStream(InputStream inputStream) { |
53 | this.inputStream = inputStream; |
54 | this.doc = null; // any new inputstream invalids the existing doc, if any. |
55 | } |
56 | |
57 | public DocumentBuilder getBuilder() { |
58 | return builder; |
59 | } |
60 | |
61 | |
62 | public void setBuilder(DocumentBuilder builder) { |
63 | this.builder = builder; |
64 | } |
65 | |
66 | |
67 | /** |
68 | * Provided for commodity, not use if app's client wants to use an other implementation |
69 | * of document builder. |
70 | * |
71 | * @return a DocumentBuilder instance or null if failed to create it. |
72 | */ |
73 | public DocumentBuilder createDefaultBuilder() { |
74 | DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); |
75 | domFactory.setNamespaceAware(true); |
76 | try { |
77 | return domFactory.newDocumentBuilder(); |
78 | } catch (ParserConfigurationException e) { |
79 | // This should never happen ? |
80 | logger.severe(e.getMessage()); |
81 | } |
82 | return null; |
83 | } |
84 | |
85 | /** |
86 | * @return the xpath |
87 | */ |
88 | public XPath getXpath() { |
89 | return xpath; |
90 | } |
91 | |
92 | /** |
93 | * @param xpath the xpath to set |
94 | */ |
95 | public void setXpath(XPath xpath) { |
96 | this.xpath = xpath; |
97 | } |
98 | |
99 | /* |
100 | * Return the Document instance, associated to the current InputStream |
101 | * @return |
102 | * @throws XRadarResultsAnalyserException |
103 | */ |
104 | private Document getCurrentDocument() throws XRadarResultsAnalyserException { |
105 | if ( doc == null ) { |
106 | if ( inputStream == null ) |
107 | throw new XRadarResultsAnalyserException("No input stream toward XML data provided !"); |
108 | else |
109 | try { |
110 | doc = builder.parse(this.inputStream); |
111 | } catch (SAXException e) { |
112 | new XRadarResultsAnalyserException(e); |
113 | } catch (IOException e) { |
114 | new XRadarResultsAnalyserException(e); |
115 | } |
116 | } |
117 | return doc; |
118 | } |
119 | |
120 | /** |
121 | * <p>Execute the provided query on the configured InputStream. Check |
122 | * |
123 | * @param src |
124 | * @param query |
125 | * @return |
126 | */ |
127 | public List<Violation> analyse(QueryData data) throws XRadarResultsAnalyserException { |
128 | List<Violation> results = new ArrayList<Violation>(0); |
129 | String query = initialize(data); |
130 | |
131 | if ( query != null && ! "".equals(query) ) { |
132 | try { |
133 | doc = getCurrentDocument(); |
134 | List<Node> nodes = executeXPathQuery(doc, query); |
135 | for (Node node : nodes) { |
136 | results.add(this.nodeToViolation(node)); |
137 | } |
138 | } catch (XPathExpressionException e) { |
139 | new XRadarResultsAnalyserException(e); |
140 | } |
141 | } |
142 | return results; |
143 | } |
144 | |
145 | private String initialize(QueryData data) { |
146 | if ( this.builder == null ) |
147 | this.builder = createDefaultBuilder(); |
148 | if ( this.xpath == null ) |
149 | this.xpath = XPathFactory.newInstance().newXPath(); |
150 | this.setInputStream(data.getResultsData()); |
151 | return data.getQuery(); |
152 | } |
153 | |
154 | private Violation nodeToViolation(Node node) { |
155 | Violation violation = new Violation(); |
156 | violation.setName(node.getNodeName()); |
157 | return violation; |
158 | } |
159 | |
160 | /* |
161 | * |
162 | */ |
163 | public List<Node> executeXPathQuery(Document doc,String query) throws XPathExpressionException { |
164 | List<Node> results = new ArrayList<Node>(0); |
165 | XPathExpression expr = xpath.compile(query); |
166 | Object result = expr.evaluate(doc, XPathConstants.NODESET); |
167 | NodeList nodes = (NodeList) result; |
168 | logger.finest("Query:" + query + " returned " + nodes.getLength() + " items."); |
169 | results = new ArrayList<Node>(nodes.getLength()); |
170 | for (int indexItem = 0; indexItem < nodes.getLength(); indexItem++) { |
171 | results.add(nodes.item(indexItem)); |
172 | } |
173 | return results; |
174 | } |
175 | } |