1 /* Copyright 2004 The Apache Software Foundation 2 * 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package org.apache.xmlbeans.impl.xpath.saxon; 17 18 import java.util.List; 19 import java.util.Map; 20 import java.util.ListIterator; 21 22 import javax.xml.transform.dom.DOMSource; 23 import javax.xml.transform.TransformerException; 24 25 import org.w3c.dom.Node; 26 27 import net.sf.saxon.Configuration; 28 import net.sf.saxon.dom.NodeWrapper; 29 import net.sf.saxon.om.NodeInfo; 30 import net.sf.saxon.om.VirtualNode; 31 import net.sf.saxon.om.Item; 32 import net.sf.saxon.value.Value; 33 import net.sf.saxon.sxpath.XPathEvaluator; 34 import net.sf.saxon.sxpath.XPathExpression; 35 import net.sf.saxon.sxpath.IndependentContext; 36 import net.sf.saxon.sxpath.XPathDynamicContext; 37 import net.sf.saxon.sxpath.XPathVariable; 38 39 import org.apache.xmlbeans.impl.store.PathDelegate; 40 41 public class XBeansXPath 42 implements PathDelegate.SelectPathInterface 43 { 44 private Object[] namespaceMap; 45 private String path; 46 private String contextVar; 47 private String defaultNS; 48 49 /** 50 * Construct given an XPath expression string. 51 * @param path The XPath expression 52 * @param contextVar The name of the context variable 53 * @param namespaceMap a map of prefix/uri bindings for NS support 54 * @param defaultNS the uri for the default element NS, if any 55 */ 56 public XBeansXPath(String path, String contextVar, 57 Map namespaceMap, String defaultNS) 58 { 59 this.path = path; 60 this.contextVar = contextVar; 61 this.defaultNS = defaultNS; 62 this.namespaceMap = namespaceMap.entrySet().toArray(); 63 } 64 65 /** 66 * Select all nodes that are selectable by this XPath 67 * expression. If multiple nodes match, multiple nodes 68 * will be returned. 69 * <p/> 70 * <p/> 71 * <b>NOTE:</b> In most cases, nodes will be returned 72 * in document-order, as defined by the XML Canonicalization 73 * specification. The exception occurs when using XPath 74 * expressions involving the <code>union</code> operator 75 * (denoted with the pipe '|' character). 76 * </p> 77 * <p/> 78 * <p/> 79 * <b>NOTE:</b> Param node must be a DOM node which will be used 80 * during the xpath execution and iteration through the results. 81 * A call of node.dispose() must be done after reading all results. 82 * </p> 83 * 84 * @param node The node, nodeset or Context object for evaluation. 85 * This value can be null. 86 * @return The <code>List</code> of all items selected 87 * by this XPath expression. 88 */ 89 public List selectNodes(Object node) 90 { 91 try 92 { 93 Node contextNode = (Node)node; 94 XPathEvaluator xpe = new XPathEvaluator(); 95 Configuration config = new Configuration(); 96 config.setDOMLevel(2); 97 config.setTreeModel(net.sf.saxon.event.Builder.STANDARD_TREE); 98 IndependentContext sc = new IndependentContext(config); 99 // Declare ns bindings 100 if (defaultNS != null) 101 sc.setDefaultElementNamespace(defaultNS); 102 103 for (int i = 0; i < namespaceMap.length; i++) 104 { 105 Map.Entry entry = (Map.Entry) namespaceMap[i]; 106 sc.declareNamespace((String) entry.getKey(), 107 (String) entry.getValue()); 108 } 109 xpe.setStaticContext(sc); 110 XPathVariable thisVar = xpe.declareVariable("", contextVar); 111 XPathExpression xpath = xpe.createExpression(path); 112 NodeInfo contextItem = 113 //config.buildDocument(new DOMSource(contextNode)); 114 config.unravel(new DOMSource(contextNode)); 115 XPathDynamicContext dc = xpath.createDynamicContext(null); 116 dc.setContextItem(contextItem); 117 dc.setVariable(thisVar, contextItem); 118 119 List saxonNodes = xpath.evaluate(dc); 120 for (ListIterator it = saxonNodes.listIterator(); it.hasNext(); ) 121 { 122 Object o = it.next(); 123 if (o instanceof NodeInfo) 124 { 125 if (o instanceof NodeWrapper) 126 { 127 Node n = getUnderlyingNode((NodeWrapper)o); 128 it.set(n); 129 } 130 else 131 { 132 it.set(((NodeInfo)o).getStringValue()); 133 } 134 } 135 else if (o instanceof Item) 136 it.set(Value.convertToJava((Item)o)); 137 } 138 return saxonNodes; 139 } 140 catch (TransformerException e) 141 { 142 throw new RuntimeException(e); 143 } 144 } 145 146 public List selectPath(Object node) 147 { 148 return selectNodes(node); 149 } 150 151 /** 152 * According to the Saxon javadoc: 153 * <code>getUnderlyingNode</code> in <code>NodeWrapper</code> implements 154 * the method specified in the interface <code>VirtualNode</code>, and 155 * the specification of the latter says that it may return another 156 * <code>VirtualNode</code>, and you may have to drill down through 157 * several layers of wrapping. 158 * To be safe, this method is provided to drill down through multiple 159 * layers of wrapping. 160 * @param v The <code>VirtualNode</code> 161 * @return The underlying node 162 */ 163 private static Node getUnderlyingNode(VirtualNode v) 164 { 165 Object o = v; 166 while (o instanceof VirtualNode) 167 { 168 o = ((VirtualNode)o).getUnderlyingNode(); 169 } 170 return (Node)o; 171 } 172 173 }