/* Copyright 2004 The Apache Software Foundation | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package org.apache.xmlbeans.impl.xpath.saxon; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.ListIterator; | |
import javax.xml.transform.dom.DOMSource; | |
import javax.xml.transform.TransformerException; | |
import org.w3c.dom.Node; | |
import net.sf.saxon.Configuration; | |
import net.sf.saxon.dom.NodeWrapper; | |
import net.sf.saxon.om.NodeInfo; | |
import net.sf.saxon.om.VirtualNode; | |
import net.sf.saxon.om.Item; | |
import net.sf.saxon.value.Value; | |
import net.sf.saxon.sxpath.XPathEvaluator; | |
import net.sf.saxon.sxpath.XPathExpression; | |
import net.sf.saxon.sxpath.IndependentContext; | |
import net.sf.saxon.sxpath.XPathDynamicContext; | |
import net.sf.saxon.sxpath.XPathVariable; | |
import org.apache.xmlbeans.impl.store.PathDelegate; | |
public class XBeansXPath | |
implements PathDelegate.SelectPathInterface | |
{ | |
private Object[] namespaceMap; | |
private String path; | |
private String contextVar; | |
private String defaultNS; | |
/** | |
* Construct given an XPath expression string. | |
* @param path The XPath expression | |
* @param contextVar The name of the context variable | |
* @param namespaceMap a map of prefix/uri bindings for NS support | |
* @param defaultNS the uri for the default element NS, if any | |
*/ | |
public XBeansXPath(String path, String contextVar, | |
Map namespaceMap, String defaultNS) | |
{ | |
this.path = path; | |
this.contextVar = contextVar; | |
this.defaultNS = defaultNS; | |
this.namespaceMap = namespaceMap.entrySet().toArray(); | |
} | |
/** | |
* Select all nodes that are selectable by this XPath | |
* expression. If multiple nodes match, multiple nodes | |
* will be returned. | |
* <p/> | |
* <p/> | |
* <b>NOTE:</b> In most cases, nodes will be returned | |
* in document-order, as defined by the XML Canonicalization | |
* specification. The exception occurs when using XPath | |
* expressions involving the <code>union</code> operator | |
* (denoted with the pipe '|' character). | |
* </p> | |
* <p/> | |
* <p/> | |
* <b>NOTE:</b> Param node must be a DOM node which will be used | |
* during the xpath execution and iteration through the results. | |
* A call of node.dispose() must be done after reading all results. | |
* </p> | |
* | |
* @param node The node, nodeset or Context object for evaluation. | |
* This value can be null. | |
* @return The <code>List</code> of all items selected | |
* by this XPath expression. | |
*/ | |
public List selectNodes(Object node) | |
{ | |
try | |
{ | |
Node contextNode = (Node)node; | |
XPathEvaluator xpe = new XPathEvaluator(); | |
Configuration config = new Configuration(); | |
config.setDOMLevel(2); | |
config.setTreeModel(net.sf.saxon.event.Builder.STANDARD_TREE); | |
IndependentContext sc = new IndependentContext(config); | |
// Declare ns bindings | |
if (defaultNS != null) | |
sc.setDefaultElementNamespace(defaultNS); | |
for (int i = 0; i < namespaceMap.length; i++) | |
{ | |
Map.Entry entry = (Map.Entry) namespaceMap[i]; | |
sc.declareNamespace((String) entry.getKey(), | |
(String) entry.getValue()); | |
} | |
xpe.setStaticContext(sc); | |
XPathVariable thisVar = xpe.declareVariable("", contextVar); | |
XPathExpression xpath = xpe.createExpression(path); | |
NodeInfo contextItem = | |
//config.buildDocument(new DOMSource(contextNode)); | |
config.unravel(new DOMSource(contextNode)); | |
XPathDynamicContext dc = xpath.createDynamicContext(null); | |
dc.setContextItem(contextItem); | |
dc.setVariable(thisVar, contextItem); | |
List saxonNodes = xpath.evaluate(dc); | |
for (ListIterator it = saxonNodes.listIterator(); it.hasNext(); ) | |
{ | |
Object o = it.next(); | |
if (o instanceof NodeInfo) | |
{ | |
if (o instanceof NodeWrapper) | |
{ | |
Node n = getUnderlyingNode((NodeWrapper)o); | |
it.set(n); | |
} | |
else | |
{ | |
it.set(((NodeInfo)o).getStringValue()); | |
} | |
} | |
else if (o instanceof Item) | |
it.set(Value.convertToJava((Item)o)); | |
} | |
return saxonNodes; | |
} | |
catch (TransformerException e) | |
{ | |
throw new RuntimeException(e); | |
} | |
} | |
public List selectPath(Object node) | |
{ | |
return selectNodes(node); | |
} | |
/** | |
* According to the Saxon javadoc: | |
* <code>getUnderlyingNode</code> in <code>NodeWrapper</code> implements | |
* the method specified in the interface <code>VirtualNode</code>, and | |
* the specification of the latter says that it may return another | |
* <code>VirtualNode</code>, and you may have to drill down through | |
* several layers of wrapping. | |
* To be safe, this method is provided to drill down through multiple | |
* layers of wrapping. | |
* @param v The <code>VirtualNode</code> | |
* @return The underlying node | |
*/ | |
private static Node getUnderlyingNode(VirtualNode v) | |
{ | |
Object o = v; | |
while (o instanceof VirtualNode) | |
{ | |
o = ((VirtualNode)o).getUnderlyingNode(); | |
} | |
return (Node)o; | |
} | |
} |