org.eclipse.persistence.internal.oxm.UnmarshalXPathEngine Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.internal.oxm;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.persistence.exceptions.XMLMarshalException;
import org.eclipse.persistence.internal.oxm.mappings.Field;
import org.eclipse.persistence.oxm.mappings.nullpolicy.AbstractNullPolicy;
import org.eclipse.persistence.oxm.mappings.nullpolicy.XMLNullRepresentationType;
import org.eclipse.persistence.oxm.record.XMLEntry;
import org.eclipse.persistence.oxm.record.XMLRecord;
import org.eclipse.persistence.platform.xml.XMLNamespaceResolver;
import org.eclipse.persistence.platform.xml.XMLNodeList;
import org.eclipse.persistence.platform.xml.XMLPlatform;
import org.eclipse.persistence.platform.xml.XMLPlatformFactory;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* INTERNAL:
* Purpose: Utility class for finding XML nodes using XPath
* expressions.
* @since Oracle TopLink 10.1.3
*/
public class UnmarshalXPathEngine <
XML_FIELD extends Field
> {
private static UnmarshalXPathEngine instance = null;
private XMLPlatform xmlPlatform;
/**
* The default constructor creates a platform instance
*/
public UnmarshalXPathEngine() {
xmlPlatform = XMLPlatformFactory.getInstance().getXMLPlatform();
}
/**
* Return the XPathEngine
singleton.
*/
public static UnmarshalXPathEngine getInstance() {
if (instance == null) {
instance = new UnmarshalXPathEngine();
}
return instance;
}
/**
* Execute the XPath statement relative to the context node.
*
* @param contextNode the node relative to which the XPath statement will be executed
* @param xmlField the field containing the XPath statement to be executed
* @param xmlNamespaceResolver used to resolve namespace prefixes to the corresponding namespace URI
* @return the first node located matching the XPath statement
*/
public Object selectSingleNode(Node contextNode, XML_FIELD xmlField, XMLNamespaceResolver xmlNamespaceResolver, boolean checkForXsiNil) throws XMLMarshalException {
try {
if (contextNode == null) {
return null;
}
XPathFragment xPathFragment = xmlField.getXPathFragment();
// allow the platform to handle the advanced case
if (xPathFragment.shouldExecuteSelectNodes()) {
return xmlPlatform.selectSingleNodeAdvanced(contextNode, xmlField.getXPath(), xmlNamespaceResolver);
}
Object result = selectSingleNode(contextNode, xPathFragment, xmlNamespaceResolver, checkForXsiNil);
if(result == XMLRecord.noEntry) {
if(xmlField.getLastXPathFragment().nameIsText() || xmlField.getLastXPathFragment().isAttribute()) {
return result;
} else {
return null;
}
}
return result;
} catch (Exception x) {
throw XMLMarshalException.invalidXPathString(xmlField.getXPath(), x);
}
}
public Object selectSingleNode(Node contextNode, XML_FIELD xmlField, XMLNamespaceResolver xmlNamespaceResolver) throws XMLMarshalException {
return this.selectSingleNode(contextNode, xmlField, xmlNamespaceResolver, false);
}
private Object selectSingleNode(Node contextNode, XPathFragment xPathFragment, XMLNamespaceResolver xmlNamespaceResolver, boolean checkForXsiNil) {
Node resultNode = getSingleNode(contextNode, xPathFragment, xmlNamespaceResolver);
if (checkForXsiNil) {
// Check for presence of xsi:nil="true"
String nil = ((Element) contextNode).getAttributeNS(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, Constants.SCHEMA_NIL_ATTRIBUTE);
if (nil.equals(Constants.BOOLEAN_STRING_TRUE)) {
return XMLRecord.NIL;
}
}
if (resultNode == null) {
if(!xPathFragment.nameIsText()) {
return XMLRecord.noEntry;
}
return null;
}
if(xPathFragment.getNextFragment() == null) {
return resultNode;
}
return selectSingleNode(resultNode, xPathFragment.getNextFragment(), xmlNamespaceResolver, checkForXsiNil);
}
/**
* Execute the XPath statement relative to the context node.
*
* @param contextNode the node relative to which the XPath statement will be executed
* @param xmlField the field containing the XPath statement to be executed
* @param xmlNamespaceResolver used to resolve namespace prefixes to the corresponding namespace URI
* @return a list of nodes matching the XPath statement
*/
public NodeList selectNodes(Node contextNode, XML_FIELD xmlField, XMLNamespaceResolver xmlNamespaceResolver) throws XMLMarshalException {
return this.selectNodes(contextNode, xmlField, xmlNamespaceResolver, null);
}
public NodeList selectNodes(Node contextNode, XML_FIELD xmlField, XMLNamespaceResolver xmlNamespaceResolver, AbstractNullPolicy nullPolicy) throws XMLMarshalException {
return selectNodes(contextNode, xmlField, xmlNamespaceResolver, nullPolicy, false);
}
public NodeList selectNodes(Node contextNode, XML_FIELD xmlField, XMLNamespaceResolver xmlNamespaceResolver, AbstractNullPolicy nullPolicy, boolean omitText) throws XMLMarshalException {
return selectNodes(contextNode, xmlField, xmlNamespaceResolver, nullPolicy, omitText, true);
}
public NodeList selectNodes(Node contextNode, XML_FIELD xmlField, XMLNamespaceResolver xmlNamespaceResolver, AbstractNullPolicy nullPolicy, boolean omitText, boolean concatinateTextNodes) {
try {
if (contextNode == null) {
return null;
}
XPathFragment xPathFragment = xmlField.getXPathFragment();
// allow the platform to handle the advanced case
if (xPathFragment.shouldExecuteSelectNodes()) {
return xmlPlatform.selectNodesAdvanced(contextNode, xmlField.getXPath(), xmlNamespaceResolver);
}
return selectNodes(contextNode, xPathFragment, xmlNamespaceResolver, nullPolicy, omitText, concatinateTextNodes);
} catch (Exception x) {
throw XMLMarshalException.invalidXPathString(xmlField.getXPath(), x);
}
}
public List selectNodes(Node contextNode, List xmlFields, XMLNamespaceResolver xmlNamespaceResolver) throws XMLMarshalException {
List nodes = new ArrayList<>();
List firstFragments = new ArrayList<>(xmlFields.size());
for(XML_FIELD nextField:xmlFields) {
firstFragments.add(nextField.getXPathFragment());
}
selectNodes(contextNode, firstFragments, xmlNamespaceResolver, nodes);
return nodes;
}
private void selectNodes(Node contextNode, List xpathFragments, XMLNamespaceResolver xmlNamespaceResolver, List entries) {
NodeList childNodes = contextNode.getChildNodes();
for(int i = 0, size = childNodes.getLength(); i < size; i++) {
Node nextChild = childNodes.item(i);
List matchingFragments = new ArrayList<>();
for(XPathFragment nextFragment:xpathFragments) {
if((nextChild.getNodeType() == Node.TEXT_NODE || nextChild.getNodeType() == Node.CDATA_SECTION_NODE) && nextFragment.nameIsText()) {
addXMLEntry(nextChild, nextFragment.getXMLField(), entries);
} else if(nextChild.getNodeType() == Node.ATTRIBUTE_NODE && nextFragment.isAttribute()) {
String attributeNamespaceURI = null;
if (nextFragment.hasNamespace()) {
attributeNamespaceURI = xmlNamespaceResolver.resolveNamespacePrefix(nextFragment.getPrefix());
}
if(sameName(nextChild, nextFragment.getLocalName()) && sameNamespaceURI(nextChild, attributeNamespaceURI)) {
addXMLEntry(nextChild, nextFragment.getXMLField(), entries);
}
} else if(nextChild.getNodeType() == Node.ELEMENT_NODE) {
String elementNamespaceURI = null;
if(nextFragment.hasNamespace()) {
elementNamespaceURI = xmlNamespaceResolver.resolveNamespacePrefix(nextFragment.getPrefix());
}
if(sameName(nextChild, nextFragment.getLocalName()) && sameNamespaceURI(nextChild, elementNamespaceURI)) {
if(nextFragment.getNextFragment() == null) {
addXMLEntry(nextChild, nextFragment.getXMLField(), entries);
} else {
matchingFragments.add(nextFragment.getNextFragment());
}
}
}
}
if(matchingFragments.size() > 0) {
selectNodes(nextChild, matchingFragments, xmlNamespaceResolver, entries);
}
}
}
private void addXMLEntry(Node entryNode, XML_FIELD xmlField, List entries) {
XMLEntry entry = new XMLEntry();
entry.setValue(entryNode);
entry.setXMLField(xmlField);
entries.add(entry);
}
private NodeList selectNodes(Node contextNode, XPathFragment xPathFragment, XMLNamespaceResolver xmlNamespaceResolver, AbstractNullPolicy nullPolicy, boolean omitText, boolean concatText) {
NodeList resultNodes = getNodes(contextNode, xPathFragment, xmlNamespaceResolver, nullPolicy, concatText);
if (xPathFragment.getNextFragment() != null && !(omitText && xPathFragment.getNextFragment().nameIsText())) {
Node resultNode;
XMLNodeList result = new XMLNodeList();
int numberOfResultNodes = resultNodes.getLength();
for (int x = 0; x < numberOfResultNodes; x++) {
resultNode = resultNodes.item(x);
result.addAll(selectNodes(resultNode, xPathFragment.getNextFragment(), xmlNamespaceResolver, nullPolicy, omitText, concatText));
}
return result;
}
return resultNodes;
}
private Node getSingleNode(Node contextNode, XPathFragment xPathFragment, XMLNamespaceResolver xmlNamespaceResolver) {
if (xPathFragment.isAttribute()) {
return selectSingleAttribute(contextNode, xPathFragment, xmlNamespaceResolver);
} else if (xPathFragment.nameIsText()) {
return selectSingleText(contextNode);
} else if (xPathFragment.isSelfFragment()) {
return contextNode;
}
if (xPathFragment.containsIndex()) {
return selectSingleElement(contextNode, xPathFragment, xmlNamespaceResolver, xPathFragment.getIndexValue());
}
return selectSingleElement(contextNode, xPathFragment, xmlNamespaceResolver);
}
private NodeList getNodes(Node contextNode, XPathFragment xPathFragment, XMLNamespaceResolver xmlNamespaceResolver, AbstractNullPolicy nullPolicy, boolean concatText) {
if (xPathFragment.isAttribute()) {
return selectAttributeNodes(contextNode, xPathFragment, xmlNamespaceResolver);
} else if (xPathFragment.nameIsText()) {
return selectTextNodes(contextNode, nullPolicy, concatText);
} else if (xPathFragment.isSelfFragment()) {
XMLNodeList xmlNodeList = new XMLNodeList(1);
xmlNodeList.add(contextNode);
return xmlNodeList;
}
if (xPathFragment.containsIndex()) {
return selectElementNodes(contextNode, xPathFragment, xmlNamespaceResolver, xPathFragment.getIndexValue());
}
return selectElementNodes(contextNode, xPathFragment, xmlNamespaceResolver);
}
private Node selectSingleAttribute(Node contextNode, XPathFragment xPathFragment, XMLNamespaceResolver xmlNamespaceResolver) {
if (xPathFragment.hasNamespace()) {
if(Node.ELEMENT_NODE == contextNode.getNodeType()) {
String attributeNamespaceURI = xmlNamespaceResolver.resolveNamespacePrefix(xPathFragment.getPrefix());
return contextNode.getAttributes().getNamedItemNS(attributeNamespaceURI, xPathFragment.getLocalName());
} else {
return null;
}
} else {
if(Node.ELEMENT_NODE == contextNode.getNodeType()) {
return contextNode.getAttributes().getNamedItem(xPathFragment.getShortName());
} else {
return null;
}
}
}
private NodeList selectAttributeNodes(Node contextNode, XPathFragment xPathFragment, XMLNamespaceResolver xmlNamespaceResolver) {
XMLNodeList xmlNodeList = new XMLNodeList();
Node child = selectSingleAttribute(contextNode, xPathFragment, xmlNamespaceResolver);
if (null != child) {
xmlNodeList.add(child);
}
return xmlNodeList;
}
private Node selectSingleElement(Node contextNode, XPathFragment xPathFragment, XMLNamespaceResolver xmlNamespaceResolver) {
Node child = contextNode.getFirstChild();
while (null != child) {
String elementNamespaceURI = null;
if(xmlNamespaceResolver != null) {
elementNamespaceURI = xmlNamespaceResolver.resolveNamespacePrefix(xPathFragment.getPrefix());
}
if ((child.getNodeType() == Node.ELEMENT_NODE) && sameName(child, xPathFragment.getLocalName()) && sameNamespaceURI(child, elementNamespaceURI)) {
return child;
}
child = child.getNextSibling();
}
return null;
}
public NodeList selectElementNodes(Node contextNode, XPathFragment xPathFragment, XMLNamespaceResolver xmlNamespaceResolver) {
XMLNodeList xmlNodeList = new XMLNodeList();
Node child = contextNode.getFirstChild();
while (null != child) {
String elementNamespaceURI = null;
if(xmlNamespaceResolver != null) {
elementNamespaceURI = xmlNamespaceResolver.resolveNamespacePrefix(xPathFragment.getPrefix());
}
if ((child.getNodeType() == Node.ELEMENT_NODE) && sameName(child, xPathFragment.getLocalName()) && sameNamespaceURI(child, elementNamespaceURI)) {
XPathPredicate predicate = xPathFragment.getPredicate();
if(predicate != null) {
XPathFragment predicateFragment = predicate.getXPathFragment();
if(predicateFragment.isAttribute() && child.getAttributes() != null) {
Attr attr = (Attr)child.getAttributes().getNamedItemNS(predicateFragment.getNamespaceURI(), predicateFragment.getLocalName());
if(attr != null) {
String attribute = attr.getValue();
if(xPathFragment.getPredicate().getValue().equals(attribute)) {
xmlNodeList.add(child);
}
}
}
} else {
xmlNodeList.add(child);
}
}
child = child.getNextSibling();
}
return xmlNodeList;
}
private Node selectSingleElement(Node contextNode, XPathFragment xPathFragment, XMLNamespaceResolver xmlNamespaceResolver, int position) {
Node child = contextNode.getFirstChild();
while (null != child) {
String elementNamespaceURI = null;
if(xmlNamespaceResolver != null) {
elementNamespaceURI = xmlNamespaceResolver.resolveNamespacePrefix(xPathFragment.getPrefix());
}
if ((child.getNodeType() == Node.ELEMENT_NODE) && sameName(child, xPathFragment.getShortName()) && sameNamespaceURI(child, elementNamespaceURI)) {
if (0 == --position) {
return child;
}
}
child = child.getNextSibling();
}
return null;
}
private NodeList selectElementNodes(Node contextNode, XPathFragment xPathFragment, XMLNamespaceResolver xmlNamespaceResolver, int position) {
XMLNodeList xmlNodeList = new XMLNodeList();
Node child = selectSingleElement(contextNode, xPathFragment, xmlNamespaceResolver, position);
if (null != child) {
xmlNodeList.add(child);
}
return xmlNodeList;
}
private Node selectSingleText(Node contextNode) {
NodeList childrenNodes = contextNode.getChildNodes();
int numberOfNodes = childrenNodes.getLength();
if (numberOfNodes == 0) {
return null;
}
if (numberOfNodes == 1) {
Node child = childrenNodes.item(0);
if (child.getNodeType() == Node.TEXT_NODE || child.getNodeType() == Node.CDATA_SECTION_NODE) {
return child;
}
return null;
}
String returnVal = null;
for (int i = 0; i < numberOfNodes; i++) {
Node next = childrenNodes.item(i);
if (next.getNodeType() == Node.TEXT_NODE || next.getNodeType() == Node.CDATA_SECTION_NODE) {
String val = next.getNodeValue();
if (val != null) {
if (returnVal == null) {
returnVal = "";
}
if(next.getNodeType() == Node.CDATA_SECTION_NODE) {
val = val.trim();
}
returnVal += val;
}
}
}
//bug#4515249 a new text node was being created when null should have been returned
//case where contextNode had several children but no Text children
if (returnVal != null) {
return contextNode.getOwnerDocument().createTextNode(returnVal);
}
return null;
}
private NodeList selectTextNodes(Node contextNode, AbstractNullPolicy nullPolicy, boolean concatText) {
if(!concatText) {
return selectAllText(contextNode);
}
Node n = selectSingleText(contextNode);
XMLNodeList xmlNodeList = new XMLNodeList();
if (n == null && nullPolicy != null) {
if (nullPolicy.valueIsNull((Element) contextNode)) {
if (nullPolicy.getMarshalNullRepresentation() != XMLNullRepresentationType.ABSENT_NODE) {
xmlNodeList.add(null);
}
} else {
xmlNodeList.add(contextNode.getOwnerDocument().createTextNode(Constants.EMPTY_STRING));
}
} else {
if (nullPolicy != null && nullPolicy.isNullRepresentedByXsiNil() && nullPolicy.valueIsNull((Element) contextNode)) {
xmlNodeList.add(null);
}else if (n != null) {
xmlNodeList.add(n);
}
}
return xmlNodeList;
}
private NodeList selectAllText(Node contextNode) {
XMLNodeList nodes = new XMLNodeList();
NodeList children = contextNode.getChildNodes();
for(int i = 0; i < children.getLength(); i++) {
Node next = children.item(i);
if (next.getNodeType() == Node.TEXT_NODE || next.getNodeType() == Node.CDATA_SECTION_NODE) {
nodes.add(next);
}
}
return nodes;
}
private boolean sameNamespaceURI(Node node, String namespaceURI) {
// HANDLE THE NULL CASE
String nodeNamespaceURI = node.getNamespaceURI();
if (nodeNamespaceURI == namespaceURI) {
return true;
}
if ((nodeNamespaceURI == null) && namespaceURI.length() == 0) {
return true;
}
if ((namespaceURI == null) && nodeNamespaceURI.length() == 0) {
return true;
}
// HANDLE THE NON-NULL CASE
return (null != nodeNamespaceURI) && nodeNamespaceURI.equals(namespaceURI);
}
private boolean sameName(Node node, String name) {
return name.equals(node.getLocalName()) || name.equals(node.getNodeName());
}
}