blob: 6e73f567139f2942dcb909c467ed423f7e415d0f [file] [log] [blame]
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 = DOMUtils.toDOMDocument(contextNode);
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);
}
}