001: /*
002: * Copyright 2006-2007 The Scriptella Project Team.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package scriptella.driver.xpath;
017:
018: import org.w3c.dom.Document;
019: import org.w3c.dom.Element;
020: import org.w3c.dom.Node;
021: import org.w3c.dom.NodeList;
022: import scriptella.expression.PropertiesSubstitutor;
023: import scriptella.spi.AbstractConnection;
024: import scriptella.spi.ParametersCallback;
025: import scriptella.spi.QueryCallback;
026: import scriptella.spi.Resource;
027: import scriptella.util.IOUtils;
028: import scriptella.util.StringUtils;
029:
030: import javax.xml.xpath.XPathConstants;
031: import javax.xml.xpath.XPathExpression;
032: import javax.xml.xpath.XPathExpressionException;
033: import java.io.IOException;
034:
035: /**
036: * Executor for XPath queries.
037: *
038: * @author Fyodor Kupolov
039: * @version 1.0
040: */
041: public class XPathQueryExecutor implements ParametersCallback {
042: private Node node;
043: private PropertiesSubstitutor substitutor = new PropertiesSubstitutor();
044: private Document document;
045: private String expressionStr;
046: private XPathExpressionCompiler compiler;
047: private AbstractConnection.StatementCounter counter;
048:
049: /**
050: * Crates executor to query document using a specified xpath expression.
051: *
052: * @param document document to query.
053: * @param xpathResource resource with xpath expression.
054: * @param counter statement counter.
055: */
056: public XPathQueryExecutor(Document document,
057: Resource xpathResource, XPathExpressionCompiler compiler,
058: AbstractConnection.StatementCounter counter) {
059: this .document = document;
060: this .compiler = compiler;
061: this .counter = counter;
062: try {
063: expressionStr = IOUtils.toString(xpathResource.open());
064: } catch (IOException e) {
065: throw new XPathProviderException(
066: "Unable to read XPath query content");
067: }
068: }
069:
070: /**
071: * Executes a query and notifies queryCallback for each found node.
072: *
073: * @param queryCallback callback to notify for each found node.
074: * @param parentParameters parent parameters to inherit.
075: */
076: public void execute(final QueryCallback queryCallback,
077: final ParametersCallback parentParameters) {
078: try {
079: substitutor.setParameters(parentParameters);
080: XPathExpression xpathExpression = compiler
081: .compile(substitutor.substitute(expressionStr));
082: NodeList nList = (NodeList) xpathExpression.evaluate(
083: document, XPathConstants.NODESET);
084: counter.statements++;
085: int n = nList.getLength();
086: for (int i = 0; i < n; i++) {
087: node = nList.item(i);
088: queryCallback.processRow(this );
089: }
090: } catch (XPathExpressionException e) {
091: throw new XPathProviderException(
092: "Failed to evaluate XPath query", e);
093: } finally {
094: substitutor.setParameters(null);
095: }
096: }
097:
098: public Object getParameter(final String name) {
099: Object result = null;
100: //If name is a number and node has attributes - try to get an attribute by index
101:
102: if (node instanceof Element) { //if element
103: //Now we use a trick to determine if node contains "name" attribute
104: //element.getAttribute returns "" for declared and declared attributes
105: Node item = node.getAttributes().getNamedItem(name);
106: result = item == null ? null : StringUtils
107: .nullsafeTrim(item.getNodeValue()); //Get attribute value for name
108: }
109: //If previos check was unsucessful and the selected node has specified name
110: if (result == null && name.equals(node.getNodeName())) {
111: result = StringUtils.nullsafeTrim(node.getTextContent()); //returns its text content
112: }
113: //If previos check was unsucessful and the selected node is an element
114: if (result == null && node instanceof Element) {
115: Element element = (Element) node;
116: NodeList list = element.getElementsByTagName(name);
117: int n = list.getLength();
118: //If element contains children with specified name
119: //Convert these elements to text and return a string instance or array
120: if (n > 0) {
121: String[] r = new String[n];
122: for (int i = 0; i < n; i++) {
123: r[i] = StringUtils.nullsafeTrim(list.item(i)
124: .getTextContent());
125: }
126: result = r.length > 1 ? r : r[0];
127: }
128: }
129: //if result=null fallback to parent parameters
130: return result == null ? substitutor.getParameters()
131: .getParameter(name) : result;
132: }
133: }
|