/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.ode.bpel.elang.xpath20.runtime; import java.util.Iterator; import javax.xml.namespace.QName; import javax.xml.transform.TransformerException; import javax.xml.xpath.XPathExpression; import net.sf.saxon.expr.AxisExpression; import net.sf.saxon.expr.Expression; import net.sf.saxon.expr.ItemChecker; import net.sf.saxon.expr.PathExpression; import net.sf.saxon.expr.VariableReference; import net.sf.saxon.om.Axis; import net.sf.saxon.om.NamePool; import net.sf.saxon.pattern.NameTest; import net.sf.saxon.pattern.NodeKindTest; import net.sf.saxon.pattern.NodeTest; import net.sf.saxon.xpath.XPathExpressionImpl; import org.apache.ode.utils.DOMUtils; import org.apache.ode.utils.NSContext; import org.w3c.dom.Attr; import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * A helper utility that modifies XPath Expression in-place. This is meant * to be reusable across the XPath and XQuery runtimes. */ public class XPath20ExpressionModifier { private NSContext contextUris; private NamePool namePool; /** * Creates a new XPath20ExpressionModifier object. * * @param contextUris * @param namePool */ public XPath20ExpressionModifier(NSContext contextUris, NamePool namePool) { this.contextUris = contextUris; this.namePool = namePool; } /** * Insert nodes into the specified XPath expression wherever * required To be precise, an node is added to its parent if: * a) the node is an element... * b) that corresponds to an step... * c) that has a child axis... * d) whose parent had no children with its name... * e) and all preceding steps are element name tests. * * @param xpathExpr * @param namePool * * @throws DOMException * @throws TransformerException */ @SuppressWarnings("unchecked") public void insertMissingData(XPathExpression xpathExpr, Node contextNode) throws DOMException, TransformerException { if ((contextNode == null) || !(contextNode instanceof Element) || !(xpathExpr instanceof XPathExpressionImpl)) { return; } Expression expression = ((XPathExpressionImpl) xpathExpr).getInternalExpression(); Iterator<Expression> subExpressions = (Iterator<Expression>) expression.iterateSubExpressions(); if (!subExpressions.hasNext()) { return; } Expression subExpr = (Expression) subExpressions.next(); if (!(subExpr instanceof PathExpression)) { return; } Document document = contextNode.getOwnerDocument(); PathExpression pathExpr = (PathExpression) subExpr; Expression step = pathExpr.getFirstStep(); while (step != null) { if (step instanceof AxisExpression) { AxisExpression axisExpr = (AxisExpression) step; NodeTest nodeTest = axisExpr.getNodeTest(); if (!(nodeTest instanceof NameTest)) { break; } NameTest nameTest = (NameTest) nodeTest; QName childName = getQualifiedName(nameTest.getFingerprint(), namePool, contextUris); if (Axis.CHILD == axisExpr.getAxis()) { if (NodeKindTest.ELEMENT.getNodeKindMask() != nameTest.getNodeKindMask()) { break; } NodeList children = ((Element) contextNode).getElementsByTagNameNS(childName.getNamespaceURI(), childName.getLocalPart()); if ((children == null) || (children.getLength() == 0)) { Node child = document.createElementNS(childName.getNamespaceURI(), DOMUtils.getQualifiedName(childName)); contextNode.appendChild(child); contextNode = child; } else if (children.getLength() == 1) { contextNode = children.item(0); } else { break; } } else if (Axis.ATTRIBUTE == axisExpr.getAxis()) { if (NodeKindTest.ATTRIBUTE.getNodeKindMask() != nameTest.getNodeKindMask()) { break; } Attr attribute = ((Element) contextNode).getAttributeNodeNS(childName.getNamespaceURI(), childName.getLocalPart()); if (attribute == null) { attribute = document.createAttributeNS(childName.getNamespaceURI(), childName.getLocalPart()); ((Element) contextNode).setAttributeNode(attribute); contextNode = attribute; } else { break; } } else { break; } } else if (step instanceof ItemChecker) { ItemChecker itemChecker = (ItemChecker) step; Expression baseExpr = itemChecker.getBaseExpression(); if (!(baseExpr instanceof VariableReference)) { break; } } else { break; } if (pathExpr != null) { Expression remainingSteps = pathExpr.getRemainingSteps(); if (remainingSteps instanceof PathExpression) { pathExpr = (PathExpression) remainingSteps; step = pathExpr.getFirstStep(); } else if (remainingSteps instanceof AxisExpression) { pathExpr = null; step = (AxisExpression) remainingSteps; } } else { break; } } } /** * Create the QName by running the given finger print against the * given context * * @param fingerprint * @param namePool * @param nsContext * * @return The QName corresponding to the finger print */ private QName getQualifiedName(int fingerprint, NamePool namePool, NSContext nsContext) { String localName = namePool.getLocalName(fingerprint); String prefix = namePool.getPrefix(fingerprint); String uri = namePool.getURI(fingerprint); // Unfortunately, NSContext.getPrefix(String URI) doesn't always work // So, we need to find the prefix for the URI the hard way if ((prefix == null) || "".equals(prefix)) { for (String nsPrefix : nsContext.getPrefixes()) { String nsUri = nsContext.getNamespaceURI(nsPrefix); if (nsUri.equals(uri)) { prefix = nsPrefix; } } } return new QName(uri, localName, prefix); } }