org.eclipse.persistence.internal.oxm.XPathEngine 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, 2018 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.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import javax.xml.namespace.QName;
import org.eclipse.persistence.exceptions.ConversionException;
import org.eclipse.persistence.exceptions.XMLMarshalException;
import org.eclipse.persistence.internal.core.helper.CoreClassConstants;
import org.eclipse.persistence.internal.core.sessions.CoreAbstractSession;
import org.eclipse.persistence.internal.oxm.documentpreservation.NoDocumentPreservationPolicy;
import org.eclipse.persistence.internal.oxm.documentpreservation.XMLBinderPolicy;
import org.eclipse.persistence.internal.oxm.mappings.Field;
import org.eclipse.persistence.internal.oxm.mappings.UnionField;
import org.eclipse.persistence.internal.oxm.record.XMLRecord;
import org.eclipse.persistence.oxm.NamespaceResolver;
import org.eclipse.persistence.oxm.XMLField;
import org.eclipse.persistence.oxm.documentpreservation.DocumentPreservationPolicy;
import org.eclipse.persistence.oxm.record.XMLEntry;
import org.eclipse.persistence.platform.xml.XMLNodeList;
import org.w3c.dom.Attr;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
/**
* INTERNAL:
* Purpose: Utility class for creating and removing XML nodes using
* XPath expressions.
* @author Rick Barkhouse - [email protected]
* @since OracleAS TopLink 10g (10.0.3), 03/11/2003 10:21:42
*/
public class XPathEngine <
XML_FIELD extends Field
>{
private static XPathEngine instance = null;
private UnmarshalXPathEngine unmarshalXPathEngine;
private DocumentPreservationPolicy noDocPresPolicy = new NoDocumentPreservationPolicy();//handles xpath engine calls without a policy
private DocumentPreservationPolicy xmlBinderPolicy = new XMLBinderPolicy();//used for adding new elements to a collection.
/**
* Return the XPathEngine
singleton.
*/
public static XPathEngine getInstance() {
if (instance == null) {
instance = new XPathEngine();
}
return instance;
}
private XPathEngine() {
super();
unmarshalXPathEngine = new UnmarshalXPathEngine();
}
/**
* Create the node path specified by xpathString
under element
.
* This method also supports creating attributes and indexed elements using the appropriate
* XPath syntax ('@
' and '[ ]
' respectively).
*
* @param xmlField XMLField containing xpath expression representing the node path to create
* @param element Root element under which to create path
*
* @return The last XMLNode
in the path
*
* @exception org.eclipse.persistence.oxm.exceptions.XMLMarshalException Thrown if passed an invalid XPath string
*/
public Node create(Field xmlField, Node element, CoreAbstractSession session) throws XMLMarshalException {
return create(xmlField, element, this, session);
}
public Node create(Field xmlField, Node element, Object value, CoreAbstractSession session) {
return create(xmlField, element, value, null, noDocPresPolicy, session);
}
/**
* Create the node path specified by xpathString
under element
* and initialize the leaf node with value
.
* This method also supports creating attributes and integer-indexed elements using the
* appropriate XPath syntax ('@
' and '[ ]
' respectively).
*
* @param xmlField XMLField containing xpath expression representing the node path to create
* @param element Root element under which to create path
* @param value Initial value for the leaf node (should not be a list)
*
* @return The last XMLNode
in the path
*
* @exception org.eclipse.persistence.oxm.exceptions.XMLMarshalException Thrown if passed an invalid XPath string
*/
public Node create(Field xmlField, Node element, Object value, Field lastUpdated, DocumentPreservationPolicy docPresPolicy, CoreAbstractSession session) throws XMLMarshalException {
if (null == value) {
return null;
}
if (docPresPolicy == null) {
//EIS case and others
docPresPolicy = this.noDocPresPolicy;
}
XPathFragment fragment = xmlField.getXPathFragment();
if (fragment.getNextFragment() == null) {
if (fragment.nameIsText()) {
Object textValue = getValueToWrite(value, xmlField, session);
if (textValue instanceof String) {
if (xmlField.isTypedTextField()) {
XMLNodeList createdElements = new XMLNodeList();
createdElements.add(element);
addTypeAttributes(createdElements, xmlField, value, resolveNamespacePrefixForURI(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, getNamespaceResolverForField(xmlField)), session);
}
return addText(xmlField, element, (String)textValue);
}
return null;
}
}
NodeList created = createCollection(xmlField, element, value, lastUpdated, docPresPolicy, session);
if ((created == null) || (created.getLength() == 0)) {
return null;
}
return created.item(0);
}
public void create(List xmlFields, Node contextNode, List values, Field lastUpdatedField, DocumentPreservationPolicy docPresPolicy, CoreAbstractSession session) {
List itemsToWrite = new ArrayList();
for(int i = 0, size = values.size(); i < size; i++) {
XMLEntry nextEntry = values.get(i);
itemsToWrite.add(nextEntry.getValue());
if(i == (values.size() -1) || values.get(i+1).getXMLField() != nextEntry.getXMLField()) {
create(nextEntry.getXMLField(), contextNode, itemsToWrite, lastUpdatedField, docPresPolicy, session);
itemsToWrite = new ArrayList();
lastUpdatedField = nextEntry.getXMLField();
}
}
}
/**
* Create the node path specified by xpathString
under element
* and initialize the leaf node with value
.
* This method also supports creating attributes and integer-indexed elements using the
* appropriate XPath syntax ('@
' and '[ ]
' respectively).
*
* @param xmlField XMLField containing xpath expression representing the node path to create
* @param element Root element under which to create path
* @param value Initial value for the leaf node (this can be a value or a collection of values)
*
* @return The last XMLNode
in the path
*
* @exception org.eclipse.persistence.oxm.exceptions.XMLMarshalException Thrown if passed an invalid XPath string
*/
private NodeList createCollection(Field xmlField, Node element, Object value, Field lastUpdated, DocumentPreservationPolicy docPresPolicy, CoreAbstractSession session) throws XMLMarshalException {
XMLNodeList createdElements = new XMLNodeList();
//CR:### If the value is null, then the node(s) must not be created.
if ((value == null) || (value instanceof Collection && (((Collection)value).size() == 0))) {
return createdElements;
}
Node nextElement = element;
Element sibling = null;
XPathFragment siblingFragment = null;
if(lastUpdated != null) {
siblingFragment = lastUpdated.getXPathFragment();
}
if ((lastUpdated != null) && !siblingFragment.isAttribute() && !siblingFragment.nameIsText()) {
//find the sibling element.
NodeList nodes = unmarshalXPathEngine.selectElementNodes(element, siblingFragment, getNamespaceResolverForField(lastUpdated));
if (nodes.getLength() > 0) {
sibling = (Element)nodes.item(nodes.getLength() - 1);
}
}
NodeList elements;
XPathFragment next = xmlField.getXPathFragment();
while (next != null) {
if (next.isAttribute()) {
addAttribute(next, xmlField, nextElement, value, session);
} else if (next.containsIndex()) {
// If we are creating multiple nodes from this XPath, assume the value is for the last node.
boolean hasMore = !(next.getHasText() || (next.getNextFragment() == null));
if (hasMore) {
nextElement = addIndexedElement(next, xmlField, nextElement, this, !hasMore, session);
} else {
Object valueToWrite = getValueToWrite(value, xmlField, session);
nextElement = addIndexedElement(next, xmlField, nextElement, valueToWrite, !hasMore, session);
createdElements.add(nextElement);
}
} else {
boolean hasMore = !(next.getHasText() || (next.getNextFragment() == null));
if (hasMore) {
elements = addElements(next, xmlField, nextElement, this, !hasMore, sibling, docPresPolicy, session);
} else {
XPathFragment nextFragment = next.getNextFragment();
if ((nextFragment != null) && nextFragment.isAttribute() && !(value instanceof List)) {
elements = addElements(next, xmlField, nextElement, this, hasMore, sibling, docPresPolicy, session);
} else {
Object valueToWrite = getValueToWrite(value, xmlField, session);
elements = addElements(next, xmlField, nextElement, valueToWrite, !hasMore, sibling, docPresPolicy, session);
createdElements.addAll(elements);
}
}
nextElement = elements.item(elements.getLength() - 1);
}
if(siblingFragment != null && sibling != null && siblingFragment.equals(next)) {
//if the sibling shares a grouping element, update the sibling
siblingFragment = siblingFragment.getNextFragment();
if ((siblingFragment != null) && !siblingFragment.isAttribute() && !siblingFragment.nameIsText()) {
//find the sibling element.
NodeList nodes = unmarshalXPathEngine.selectElementNodes(nextElement, siblingFragment, getNamespaceResolverForField(lastUpdated));
if (nodes.getLength() > 0) {
sibling = (Element)nodes.item(nodes.getLength() - 1);
} else {
sibling = null;
}
} else {
sibling = null;
}
} else {
sibling = null;
}
next = next.getNextFragment();
if ((next != null) && next.nameIsText()) {
next = null;
}
}
if (xmlField.isTypedTextField()) {
addTypeAttributes(createdElements, xmlField, value, resolveNamespacePrefixForURI(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, getNamespaceResolverForField(xmlField)), session);
}
return createdElements;
}
private Object getNonNodeValueToWrite(Object value, Field xmlField, CoreAbstractSession session) {
if (this == value) {
return this;
}
QName schemaType = null;
if(xmlField.getLeafElementType() != null){
schemaType = xmlField.getLeafElementType();
}else if (xmlField.isUnionField()) {
return getValueToWriteForUnion((UnionField)xmlField, value, session);
}else if (xmlField.isTypedTextField()) {
ConversionManager conversionManager = (ConversionManager) session.getDatasourcePlatform().getConversionManager();
schemaType = xmlField.getXMLType(value.getClass(), conversionManager);
}else if (xmlField.getSchemaType() != null) {
schemaType = xmlField.getSchemaType();
}
if (value instanceof List) {
if (xmlField.usesSingleNode()) {
StringBuilder returnStringBuilder = new StringBuilder();
for (int i = 0; i < ((List)value).size(); i++) {
Object nextItem = ((List)value).get(i);
String nextConvertedItem = null;
if(schemaType != null && schemaType.equals(Constants.QNAME_QNAME)){
nextConvertedItem = getStringForQName((QName)nextItem, getNamespaceResolverForField(xmlField));
}else{
nextConvertedItem = (String) ((ConversionManager)session.getDatasourcePlatform().getConversionManager()).convertObject(nextItem, CoreClassConstants.STRING, schemaType);
}
returnStringBuilder.append(nextConvertedItem);
if (i < (((List)value).size() - 1)) {
returnStringBuilder.append(' ');
}
}
return returnStringBuilder.toString();
} else {
ArrayList items = new ArrayList(((List)value).size());
for (int index = 0; index < ((List)value).size(); index++) {
Object nextItem = ((List)value).get(index);
if (nextItem instanceof Node || nextItem == XMLRecord.NIL) {
items.add(nextItem);
} else {
if(schemaType != null && schemaType.equals(Constants.QNAME_QNAME)){
String nextConvertedItem = getStringForQName((QName)nextItem, getNamespaceResolverForField(xmlField));
items.add(nextConvertedItem);
}else{
String nextConvertedItem = (String) ((ConversionManager)session.getDatasourcePlatform().getConversionManager()).convertObject(nextItem, CoreClassConstants.STRING, schemaType);
items.add(nextConvertedItem);
}
}
}
return items;
}
} else {
if(schemaType != null && schemaType.equals(Constants.QNAME_QNAME)){
return getStringForQName((QName)value, getNamespaceResolverForField(xmlField));
}
return ((ConversionManager)session.getDatasourcePlatform().getConversionManager()).convertObject(value, CoreClassConstants.STRING, schemaType);
}
}
private Object getValueToWrite(Object value, Field xmlField, CoreAbstractSession session) {
if (value instanceof Node || value == XMLRecord.NIL) {
return value;
}
return getNonNodeValueToWrite(value, xmlField, session);
}
private String getSingleValueToWriteForUnion(UnionField xmlField, Object value, CoreAbstractSession session) {
List schemaTypes = xmlField.getSchemaTypes();
QName schemaType = null;
for (int i = 0; i < schemaTypes.size(); i++) {
QName nextQName = (QName)(xmlField).getSchemaTypes().get(i);
try {
if (nextQName != null) {
ConversionManager conversionManager = (ConversionManager)session.getDatasourcePlatform().getConversionManager();
Class javaClass = xmlField.getJavaClass(nextQName, conversionManager);
value = conversionManager.convertObject(value, javaClass, nextQName);
schemaType = nextQName;
break;
}
} catch (ConversionException ce) {
if (i == (schemaTypes.size() - 1)) {
schemaType = nextQName;
}
}
}
return (String) ((ConversionManager)session.getDatasourcePlatform().getConversionManager()).convertObject(value, CoreClassConstants.STRING, schemaType);
}
private Object getValueToWriteForUnion(UnionField xmlField, Object value, CoreAbstractSession session) {
if (value instanceof List) {
if (xmlField.usesSingleNode()) {
StringBuilder returnStringBuilder = new StringBuilder();
Object next = null;
for (int i = 0; i < ((List)value).size(); i++) {
next = ((List)value).get(i);
returnStringBuilder.append(getSingleValueToWriteForUnion(xmlField, next, session));
if (i < (((List)value).size() - 1)) {
returnStringBuilder.append(' ');
}
}
return returnStringBuilder.toString();
} else {
ArrayList items = new ArrayList(((List)value).size());
Object next = null;
for (int i = 0; i < ((List)value).size(); i++) {
next = ((List)value).get(i);
items.add(getSingleValueToWriteForUnion(xmlField, next, session));
}
return items;
}
} else {
return getSingleValueToWriteForUnion(xmlField, value, session);
}
}
/**
* Add a new indexed element
to the parent
element.
* Will overwrite if an element already exists at that position. Currently only supports
* integer indices.
*
* @param xpathString element and index to create (in the form 'element[index]')
* @param namespaceResolover namespaceResolover of the element being created
* @param parent Parent element
* @param schemaType schemaType for the new node
* @param value Value for the new node
* @param forceCreate If true, create a new element even if one with the same name currently exists
*
* @return The XMLElement
that was created/found
*
* @exception org.eclipse.persistence.oxm.exceptions.XMLMarshalException Thrown if passed an invalid XPath string
*/
private Node addIndexedElement(XPathFragment fragment, Field xmlField, Node parent, Object value, boolean forceCreate, CoreAbstractSession session) throws XMLMarshalException {
String element = fragment.getShortName();
int index = fragment.getIndexValue();
if (index < 0) {
throw XMLMarshalException.invalidXPathIndexString(fragment.getXPath());
}
Node existingElement;
NamespaceResolver namespaceResolver = getNamespaceResolverForField(xmlField);
for (int i = 1; i < index; i++) {
Field field = new XMLField(element + "[" + i + "]");
field.setNamespaceResolver(namespaceResolver);
existingElement = (Node)unmarshalXPathEngine.selectSingleNode(parent, field, namespaceResolver);
if (existingElement == null) {
addElement(new XPathFragment(element), xmlField, parent, this, true, session);
}
}
Field field = new XMLField(fragment.getXPath());
field.setNamespaceResolver(namespaceResolver);
existingElement = (Node)unmarshalXPathEngine.selectSingleNode(parent, field, namespaceResolver);
if (existingElement == null) {
return addElement(new XPathFragment(element), field, parent, value, true, session);
}
if ((existingElement != null) && !forceCreate) {
return existingElement;
}
String namespace = resolveNamespacePrefix(fragment, namespaceResolver);
Element elementToReturn = parent.getOwnerDocument().createElementNS(namespace, element);
if ((value != this) && (value != null)) {
if (value instanceof String) {
addText(xmlField, elementToReturn, (String)value);
}
}
parent.replaceChild(elementToReturn, existingElement);
return elementToReturn;
}
/**
* Add a new element
to the parent
element. If an element with
* this name already exists, return it (unless forceCreate
is true
).
*
* @param element Name of element to create
* @param parent Parent element
* @param value Value for the new node
* @param forceCreate If true, create a new element even if one with the same name currently exists
*
* @return The XMLElement
that was created/found
*/
private Node addElement(XPathFragment fragment, Field xmlField, Node parent, Object value, boolean forceCreate, CoreAbstractSession session) {
return addElement(fragment, xmlField, parent, null, value, forceCreate, session);
}
private Node addElement(XPathFragment fragment, Field xmlField, Node parent, QName schemaType, Object value, boolean forceCreate, CoreAbstractSession session) {
NodeList list = addElements(fragment, xmlField, parent, value, forceCreate, null, noDocPresPolicy, session);
if (list.getLength() > 0) {
return list.item(0);
}
return null;
}
/**
* Add a new element
to the parent
element. If an element with
* this name already exists, return it (unless forceCreate
is true
).
*
* @param fragment Name of element to create
* @param namespace namespace of element to create
* @param parent Parent element
* @param schemaType schemaType of element to create
* @param value Value for the new node
* @param forceCreate If true, create a new element even if one with the same name currently exists
* @return The NodeList
that was created/found
*/
private NodeList addElements(XPathFragment fragment, Field xmlField, Node parent, Object value, boolean forceCreate, Element sibling, DocumentPreservationPolicy docPresPolicy, CoreAbstractSession session) {
if (!forceCreate) {
NodeList nodes = unmarshalXPathEngine.selectElementNodes(parent, fragment, getNamespaceResolverForField(xmlField));
if (nodes.getLength() > 0) {
return nodes;
}
}
XMLNodeList elementsToReturn = new XMLNodeList();
if (value == this) {
String namespace = resolveNamespacePrefix(fragment, getNamespaceResolverForField(xmlField));
Element newElement = parent.getOwnerDocument().createElementNS(namespace, fragment.getShortName());
XPathPredicate predicate = fragment.getPredicate();
if(predicate != null) {
XPathFragment predicateFragment = predicate.getXPathFragment();
if(predicateFragment.isAttribute()) {
if(predicateFragment.getNamespaceURI() == null || predicateFragment.getNamespaceURI().length() == 0) {
newElement.setAttribute(predicateFragment.getLocalName(), fragment.getPredicate().getValue());
} else {
String name = predicateFragment.getLocalName();
if(predicateFragment.getPrefix() != null && predicateFragment.getPrefix().length() != 0) {
name = predicateFragment.getPrefix() + Constants.COLON + name;
}
newElement.setAttributeNS(predicateFragment.getNamespaceURI(), name, fragment.getPredicate().getValue());
}
}
}
elementsToReturn.add(newElement);
docPresPolicy.getNodeOrderingPolicy().appendNode(parent, newElement, sibling);
} else if (value == null) {
elementsToReturn.add(parent);
} else {
// Value may be a direct value, node, or list of values.
if (value instanceof List) {
List values = (List)value;
for (int index = 0; index < values.size(); index++) {
Element newElement = null;
if (values.get(index) != XMLRecord.NIL) {
newElement = (Element) createElement(parent, fragment, xmlField, values.get(index), session);
} else {
newElement = (Element) createElement(parent, fragment, xmlField, Constants.EMPTY_STRING, session);
addXsiNilToElement(newElement, xmlField);
}
XPathPredicate predicate = fragment.getPredicate();
if(predicate != null) {
XPathFragment predicateFragment = predicate.getXPathFragment();
if(predicateFragment.isAttribute()) {
if(predicateFragment.getNamespaceURI() == null || predicateFragment.getNamespaceURI().length() == 0) {
newElement.setAttribute(predicateFragment.getLocalName(), fragment.getPredicate().getValue());
} else {
String name = predicateFragment.getLocalName();
if(predicateFragment.getPrefix() != null && predicateFragment.getPrefix().length() != 0) {
name = predicateFragment.getPrefix() + Constants.COLON + name;
}
newElement.setAttributeNS(predicateFragment.getNamespaceURI(), name, fragment.getPredicate().getValue());
}
}
}
docPresPolicy.getNodeOrderingPolicy().appendNode(parent, newElement, sibling);
elementsToReturn.add(newElement);
sibling = newElement;
}
} else {
Element newElement = null;
if (value != XMLRecord.NIL) {
newElement = (Element)createElement(parent, fragment, xmlField, value, session);
} else {
newElement = (Element) createElement(parent, fragment, xmlField, Constants.EMPTY_STRING, session);
addXsiNilToElement(newElement, xmlField);
}
XPathPredicate predicate = fragment.getPredicate();
if(predicate != null) {
XPathFragment predicateFragment = predicate.getXPathFragment();
if(predicateFragment.isAttribute()) {
if(predicateFragment.getNamespaceURI() == null || predicateFragment.getNamespaceURI().length() == 0) {
newElement.setAttribute(predicateFragment.getLocalName(), fragment.getPredicate().getValue());
} else {
String name = predicateFragment.getLocalName();
if(predicateFragment.getPrefix() != null && predicateFragment.getPrefix().length() != 0) {
name = predicateFragment.getPrefix() + Constants.COLON + name;
}
newElement.setAttributeNS(predicateFragment.getNamespaceURI(), name, fragment.getPredicate().getValue());
}
}
}
docPresPolicy.getNodeOrderingPolicy().appendNode(parent, newElement, sibling);
elementsToReturn.add(newElement);
}
}
return elementsToReturn;
}
/**
* Creates a new Element and appends a value to an element.
*
* @param parent Element which will own the newly created element
* @param elementName tag name for the new element
* @param value Object to add
*/
private Node createElement(Node parent, XPathFragment fragment, Field xmlField, Object value, CoreAbstractSession session) {
if (value == null) {
return parent;
}
if (value instanceof Node) {
return createElement(parent, fragment, getNamespaceResolverForField(xmlField), (Node)value);
}
Element element = null;
if (parent.getOwnerDocument() == null) {
element = ((Document)parent).getDocumentElement();
} else {
String namespace = resolveNamespacePrefix(fragment, getNamespaceResolverForField(xmlField));
NamespaceResolver domResolver = new NamespaceResolver();
domResolver.setDOM(parent);
String existingPrefix = domResolver.resolveNamespaceURI(namespace);
String elementName = fragment.getShortName();
if(existingPrefix != null) {
if(existingPrefix.length() > 0) {
elementName = existingPrefix + Constants.COLON + fragment.getLocalName();
} else {
elementName = fragment.getLocalName();
}
}
element = parent.getOwnerDocument().createElementNS(namespace, elementName);
if (fragment.isGeneratedPrefix() && existingPrefix == null) {
element.setAttributeNS(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, javax.xml.XMLConstants.XMLNS_ATTRIBUTE + Constants.COLON + fragment.getPrefix(), fragment.getNamespaceURI());
}
XPathPredicate predicate = fragment.getPredicate();
if(predicate != null) {
XPathFragment predicateFragment = predicate.getXPathFragment();
if(predicateFragment.isAttribute()) {
element.setAttributeNS(predicateFragment.getNamespaceURI(), predicateFragment.getLocalName(), fragment.getPredicate().getValue());
}
}
}
XPathFragment nextFragment = fragment.getNextFragment();
if ((nextFragment != null) && nextFragment.isAttribute()) {
addAttribute(nextFragment, xmlField, element, value, session);
} else if (value instanceof String && ((String)value).length() > 0) {
addText(xmlField, element, (String)value);
} else if (value == XMLRecord.NIL) {
addXsiNilToElement(element, xmlField);
}
return element;
}
public Element createUnownedElement(Node parent, Field xmlField) {
XPathFragment lastFragment = xmlField.getXPathFragment();
while (lastFragment.getNextFragment() != null) {
lastFragment = lastFragment.getNextFragment();
}
String nodeName = lastFragment.getShortName();
String namespace = resolveNamespacePrefix(lastFragment, getNamespaceResolverForField(xmlField));
NamespaceResolver domResolver = new NamespaceResolver();
domResolver.setDOM(parent);
String existingPrefix = domResolver.resolveNamespaceURI(namespace);
String elementName = nodeName;
if(existingPrefix != null) {
if(existingPrefix.length() > 0) {
elementName = existingPrefix + Constants.COLON + lastFragment.getLocalName();
} else {
elementName = lastFragment.getLocalName();
}
}
Element elem = parent.getOwnerDocument().createElementNS(namespace, elementName);
if (lastFragment.isGeneratedPrefix() && existingPrefix == null) {
elem.setAttributeNS(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, javax.xml.XMLConstants.XMLNS_ATTRIBUTE + Constants.COLON + lastFragment.getPrefix(), lastFragment.getNamespaceURI());
}
return elem;
}
/**
* Adds a type attribute on an element, the value of the attribute is determined
* by performing a lookup in the SimpleTypeTranslator to find the Schema type
* for the value.
*
* @param elements NodeList which will have a type attribute added to them
* @param simpleTypeTranslator SimpleTypeTranslator to perform lookup in
* @param value Object to base the lookup on
* @param schemaInstancePrefix the prefix representing the schema instance namespace
*/
private void addTypeAttributes(NodeList elements, Field field, Object value, String schemaInstancePrefix, CoreAbstractSession session) {
NamespaceResolver namespaceResolver = getNamespaceResolverForField(field);
if (!field.isTypedTextField()) {
return;
}
List values;
if (value instanceof List) {
values = (List)value;
} else {
values = new ArrayList();
values.add(value);
}
int size = elements.getLength();
int valuesSize = values.size();
if (size != valuesSize) {
return;
}
Node next = null;
for (int i = 0; i < size; i++) {
next = elements.item(i);
if (next.getNodeType() == Node.ELEMENT_NODE) {
Class valueClass = values.get(i).getClass();
if(valueClass != CoreClassConstants.STRING){
ConversionManager conversionManager = (ConversionManager) session.getDatasourcePlatform().getConversionManager();
QName qname = field.getXMLType(valueClass, conversionManager);
if (qname != null) {
if (null == schemaInstancePrefix) {
schemaInstancePrefix = namespaceResolver.generatePrefix(Constants.SCHEMA_INSTANCE_PREFIX);
((Element)next).setAttributeNS(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, javax.xml.XMLConstants.XMLNS_ATTRIBUTE + Constants.COLON + schemaInstancePrefix, javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);
}
String type;
String prefix = this.resolveNamespacePrefixForURI(qname.getNamespaceURI(), namespaceResolver);
if (prefix == null || prefix.length() == 0) {
if(qname.getNamespaceURI().equals(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI)){
prefix = namespaceResolver.generatePrefix(Constants.SCHEMA_PREFIX);
}else{
prefix = namespaceResolver.generatePrefix();
}
((Element)next).setAttributeNS(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, javax.xml.XMLConstants.XMLNS_ATTRIBUTE + Constants.COLON + prefix, qname.getNamespaceURI());
}
type = prefix + Constants.COLON + qname.getLocalPart();
((Element)next).setAttributeNS(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, schemaInstancePrefix + Constants.COLON + Constants.SCHEMA_TYPE_ATTRIBUTE, type);
}
}
}
}
}
/**
* Creates a new Element and appends a value to an element.
*
* @param parent Element which will own the newly created element
* @param elementName tag name for the new element
* @param value Node to add
*
*/
private Node createElement(Node parent, XPathFragment fragment, NamespaceResolver namespaceResolver, Node value) {
// The case of the parent being the document (element is the root) needs to be handled.
// Document have no owner document, but are the document.
Document document = parent.getOwnerDocument();
if ((document == null) && (parent.getNodeType() == Node.DOCUMENT_NODE)) {
document = (Document)parent;
}
String nodeUri = value.getNamespaceURI();
String nodeName = value.getLocalName();
//String fragUri = resolveNamespacePrefix(fragment, namespaceResolver);
String fragUri = fragment.getNamespaceURI();
String fragName = fragment.getLocalName();
if ((nodeName != null) && nodeName.equals(fragName) && (((nodeUri != null) && nodeUri.equals(fragUri)) || ((nodeUri == null) && (fragUri == null)))) {
if (document != value.getOwnerDocument()) {
return document.importNode(value, true);
}
return value;
} else {
// Need to reset the node name.
String namespace = resolveNamespacePrefix(fragment, namespaceResolver);
Element clone = document.createElementNS(namespace, fragName);
NamedNodeMap attributes = value.getAttributes();
int attributesLength = attributes.getLength();
for (int index = 0; index < attributesLength; index++) {
Node attribute = document.importNode(attributes.item(index), true);
clone.setAttributeNode((Attr)attribute);
}
NodeList elements = value.getChildNodes();
int elementsLength = elements.getLength();
for (int index = 0; index < elementsLength; index++) {
Node attribute = document.importNode(elements.item(index), true);
clone.appendChild(attribute);
}
return clone;
}
}
/**
* Add a new attribute to an element. If the attribute already exists, return the element.
*
* @param attributeName Name of the attribute to add
* @param parent Element to create the attribute on
* @param value Value for the new attribute
*
* @return The XMLElement
that the attribute was added to (same as the parent
parameter).
*/
private Node addAttribute(XPathFragment attributeFragment, Field xmlField, Node parent, Object value, CoreAbstractSession session) {
Object valueToWrite = null;
if (!(parent instanceof Element)) {
return parent;
}
Element parentElement = (Element)parent;
if (value instanceof Node) {
if (((Node)value).getNodeType() == Node.ATTRIBUTE_NODE) {
Attr attr = (Attr)value;
if (parent.getAttributes().getNamedItemNS(attr.getNamespaceURI(), attr.getLocalName()) == null) {
String pfx = null;
if (xmlField.getNamespaceResolver() != null) {
pfx = getNamespaceResolverForField(xmlField).resolveNamespaceURI(attr.getNamespaceURI());
}
if (pfx != null) {
// If the namespace resolver has a prefix for the node's URI, use it
parentElement.setAttributeNS(attr.getNamespaceURI(), pfx + Constants.COLON + attr.getLocalName(), attr.getNodeValue());
} else {
// No entry for the node's URI in the resolver, so use the node's
// prefix/uri pair and define the URI locally
parentElement.setAttributeNS(attr.getNamespaceURI(), attr.getName(), attr.getNodeValue());
}
}
return parent;
}
valueToWrite = value;
} else {
valueToWrite = getNonNodeValueToWrite(value, xmlField, session);
}
String attributeName = attributeFragment.getLocalName();
String attributeNamespace = resolveNamespacePrefix(attributeFragment, getNamespaceResolverForField(xmlField));
if ((valueToWrite != null) && (parent.getAttributes().getNamedItemNS(attributeNamespace, attributeName) == null)) {
if (valueToWrite == this) {
parentElement.setAttributeNS(attributeNamespace, attributeFragment.getShortName(), Constants.EMPTY_STRING);
} else if (valueToWrite instanceof String) {
parentElement.setAttributeNS(attributeNamespace, attributeFragment.getShortName(), (String)valueToWrite);
}
if (attributeFragment.isGeneratedPrefix()) {
parentElement.setAttributeNS(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, javax.xml.XMLConstants.XMLNS_ATTRIBUTE + Constants.COLON + attributeFragment.getPrefix(), attributeFragment.getNamespaceURI());
}
}
return parent;
}
// ==========================================================================================
/**
* Remove a node. If xpathString
points to an indexed element, the element will not be removed,
* but will instead be reinitialzed (to maintain the index of the collection).
*
* @param xmlField Field containing XPath query string
* @param element Root element at which to begin search
*
* @return NodeList
containing the nodes that were removed.
*
* @exception org.eclipse.persistence.oxm.exceptions.XMLMarshalException Thrown if passed an invalid XPath string
*/
public NodeList remove(Field xmlField, Node element) throws XMLMarshalException {
return remove(xmlField, element, false);
}
/**
* Remove a node.
*
* @param xmlField Field containing XPath query string
* @param element Root element at which to begin search
* @param forceRemove If true
, then indexed elements will be truly deleted, otherwise they will be reinitialized
*
* @return NodeList
containing the nodes that were removed.
*
* @exception org.eclipse.persistence.oxm.exceptions.XMLMarshalException Thrown if passed an invalid XPath string
*/
public NodeList remove(Field xmlField, Node element, boolean forceRemove) throws XMLMarshalException {
String xpathString = xmlField.getXPath();
NodeList nodes = unmarshalXPathEngine.selectNodes(element, xmlField, getNamespaceResolverForField(xmlField));
int numberOfNodes = nodes.getLength();
boolean shouldNullOutNode = containsIndex(xpathString) && !forceRemove;
// Remove the element or attribute, for positional element null-out instead of remove.
for (int i = 0; i < numberOfNodes; i++) {
Node node = nodes.item(i);
if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
((Attr)node).getOwnerElement().removeAttribute(node.getNodeName());
} else {
if (shouldNullOutNode) {
Node blankNode = node.getParentNode().getOwnerDocument().createElementNS(node.getNamespaceURI(), node.getNodeName());
node.getParentNode().replaceChild(blankNode, node);
} else {
node.getParentNode().removeChild(node);
}
}
}
return nodes;
}
// ==========================================================================================
/**
* Replace the value of the nodes matching xpathString
with value
.
* This method handles elements, indexed elements, and attributes.
*
* @param xmlField Field containing XPath query string
* @param parent Parent element
* @param value New value for the node
*
* @return NodeList
containing the nodes that were replaced.
*/
public NodeList replaceValue(Field xmlField, Node parent, Object value, CoreAbstractSession session) throws XMLMarshalException {
NodeList nodes = unmarshalXPathEngine.selectNodes(parent, xmlField, getNamespaceResolverForField(xmlField), null, false, false);
int numberOfNodes = nodes.getLength();
if(numberOfNodes == 0 && xmlField.getLastXPathFragment().nameIsText()) {
nodes = unmarshalXPathEngine.selectNodes(parent, xmlField, getNamespaceResolverForField(xmlField), null, true);
XMLNodeList textNodes = new XMLNodeList();
for(int i = 0; i < nodes.getLength(); i++) {
Element nextNode = (Element)nodes.item(i);
Text text = nextNode.getOwnerDocument().createTextNode("");
nextNode.appendChild(text);
textNodes.add(text);
}
numberOfNodes = textNodes.getLength();
nodes = textNodes;
}
XMLNodeList createdElements = new XMLNodeList();
for (int i = 0; i < numberOfNodes; i++) {
Node node = nodes.item(i);
// Handle Attributes and Text
if (node.getNodeType() != Node.ELEMENT_NODE) {
if (((node.getNodeType() == Node.TEXT_NODE) || (node.getNodeType() == Node.CDATA_SECTION_NODE)) && (value == null)) {
//if parent has only text children, remove parent. If parent has non-text children,
//remove all text children.
Node parentNode = node.getParentNode();
if(parentNode != null) {
Node grandParentNode = parentNode.getParentNode();
NodeList childNodes = parentNode.getChildNodes();
if(childNodes.getLength() == numberOfNodes) {
grandParentNode.removeChild(parentNode);
} else {
for(int x = 0; x < childNodes.getLength(); x++) {
Node next = childNodes.item(x);
if(next.getNodeType() == Node.TEXT_NODE || next.getNodeType() == Node.CDATA_SECTION_NODE) {
parentNode.removeChild(next);
}
}
}
}
} else {
if(value == null) {
((Attr)node).getOwnerElement().removeAttributeNode((Attr)node);
} else {
if(value == XMLRecord.NIL && ((node.getNodeType() == Node.TEXT_NODE) || (node.getNodeType() == Node.CDATA_SECTION_NODE))) {
Element parentElement = (Element)node.getParentNode();
addXsiNilToElement(parentElement, xmlField);
parentElement.removeChild(node);
} else {
String stringValue = (String)session.getDatasourcePlatform().getConversionManager().convertObject(value, CoreClassConstants.STRING);
Element parentElement = (Element)node.getParentNode();
if(parentElement == null && parent.getNodeType() == Node.ELEMENT_NODE) {
parentElement = (Element)parent;
}
if(stringValue.length() == 0 && ((node.getNodeType() == Node.TEXT_NODE) || (node.getNodeType() == Node.CDATA_SECTION_NODE)) && parentElement != null) {
parentElement.removeChild(node);
} else {
node.setNodeValue(stringValue);
if(((node.getNodeType() == Node.TEXT_NODE) || (node.getNodeType() == Node.CDATA_SECTION_NODE)) && parentElement != null) {
Attr nil = parentElement.getAttributeNodeNS(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, Constants.SCHEMA_NIL_ATTRIBUTE);
if(nil != null) {
parentElement.removeAttributeNode(nil);
}
}
}
}
}
}
} else {
Element element = (Element)node;
Node parentNode = element.getParentNode();
if (value == null) {
parentNode.removeChild(element);
} else {
String elementName = element.getTagName();
Element newElement = null;
Object valueToWrite = getValueToWrite(value, xmlField, session);
XPathFragment childFrag = new XPathFragment(elementName);
childFrag.setNamespaceURI(element.getNamespaceURI());
newElement = (Element)createElement(parentNode, childFrag, xmlField, valueToWrite, session);
createdElements.add(newElement);
if (newElement != element) {
parentNode.replaceChild(newElement, element);
}
}
}
}
if (xmlField.isTypedTextField()) {
addTypeAttributes(createdElements, xmlField, value, resolveNamespacePrefixForURI(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, getNamespaceResolverForField(xmlField)), session);
}
return nodes;
}
public List replaceCollection(List xmlFields, List values, Node contextNode, DocumentPreservationPolicy docPresPolicy, Field lastUpdatedField, CoreAbstractSession session) {
List oldNodes = unmarshalXPathEngine.selectNodes(contextNode, xmlFields, getNamespaceResolverForField(xmlFields.get(0)));
if(oldNodes == null || oldNodes.size() == 0) {
return oldNodes;
}
Iterator oldValues = oldNodes.iterator();
//Remove all the old values, and then call create to add them back in.
while(oldValues.hasNext()) {
XMLEntry entry = oldValues.next();
Node nextNode = (Node)entry.getValue();
Node parent = nextNode.getParentNode();
parent.removeChild(nextNode);
while(parent != contextNode) {
if(parent.getChildNodes().getLength() == 0) {
nextNode = parent;
parent = nextNode.getParentNode();
parent.removeChild(nextNode);
} else {
break;
}
}
}
create(xmlFields, contextNode, values, lastUpdatedField, xmlBinderPolicy, session);
return oldNodes;
}
public NodeList replaceCollection(Field xmlField, Node parent, Collection values, CoreAbstractSession session) throws XMLMarshalException {
NodeList nodes = null;
if (xmlField != null) {
nodes = unmarshalXPathEngine.selectNodes(parent, xmlField, getNamespaceResolverForField(xmlField));
} else {
nodes = parent.getChildNodes();
}
if (nodes.getLength() == 0) {
return nodes;
}
Iterator collectionValues = values.iterator();
int i = 0;
int nodesLength = nodes.getLength();
Vector newNodes = new Vector();
// Iterate over both collections until one runs out, creating a collection of correct nodes
// while removing the old ones.
boolean performedReplace = true;
Object value = null;
while ((i < nodesLength) && collectionValues.hasNext()) {
//Keep track of which nodes have been replaced
Node oldChild = nodes.item(i);
Element newChild = null;
if (performedReplace) {
value = collectionValues.next();
}
Node parentNode = oldChild.getParentNode();
// Handle Attributes and Text
if (oldChild.getNodeType() != Node.ELEMENT_NODE) {
if (((oldChild.getNodeType() == Node.TEXT_NODE) || (oldChild.getNodeType() == Node.CDATA_SECTION_NODE)) && (value == null)) {
Node grandParentNode = parentNode.getParentNode();
grandParentNode.removeChild(parentNode);
} else {
oldChild.setNodeValue((String) session.getDatasourcePlatform().getConversionManager().convertObject(value, CoreClassConstants.STRING));
}
} else {
Element element = (Element)oldChild;
String elementName = element.getTagName();
Object valueToWrite = getValueToWrite(value, xmlField, session);
XPathFragment childFragment = new XPathFragment(elementName);
childFragment.setNamespaceURI(element.getNamespaceURI());
newChild = (Element)createElement(parentNode, childFragment, xmlField, valueToWrite, session);
if (!newNodes.contains(oldChild)) {
if (newChild != oldChild) {
parentNode.replaceChild(newChild, oldChild);
}
newNodes.addElement(newChild);
performedReplace = true;
} else {
performedReplace = false;
}
}
i++;
}
// This means collection was ran out first. Remove left-overs.
while (i < nodesLength) {
Node toRemove = nodes.item(i);
Node removedParent = toRemove.getParentNode();
if ((removedParent != null) && !newNodes.contains(toRemove)) {
//only remove it, if it's not already been added back in
removedParent.removeChild(toRemove);
}
i++;
}
//Now add the nodes back in, in the correct order
//for (Iterator newNodesIter = newNodes.iterator(); newNodesIter.hasNext();) {
// Element newNode = (Element)newNodesIter.next();
//this.create(xmlField, parent, newNode);
//}
if ((value != null) && !performedReplace) {
//If we didn't add in the last element we tried then add it now
if ((xmlField.getXPathFragment().getNextFragment() == null) || xmlField.getXPathFragment().getHasText()) {
//if there's no grouping element, ensure that new collection elements
//are added inline with the others
create(xmlField, parent, value, xmlField, xmlBinderPolicy, session);
} else {
create(xmlField, parent, value, session);
}
}
// Now add in any others that are left in the iterator
while (collectionValues.hasNext()) {
value = collectionValues.next();
//If there's a grouping element, then just do the normal create
if ((xmlField.getXPathFragment().getNextFragment() == null) || xmlField.getXPathFragment().getHasText()) {
//if there's no grouping element, ensure that new collection elements
//are added inline with the others
create(xmlField, parent, value, xmlField, xmlBinderPolicy, session);
} else {
create(xmlField, parent, value, session);
}
}
return nodes;
}
// ==========================================================================================
/**
* Determine if xpathString
contains an index (e.g. 'element[index]').
*
* @param xpathString XPath expression to test
*
* @return true
if xpathString
contains an index, otherwise false
.
*/
private boolean containsIndex(String xpathString) {
return (xpathString.lastIndexOf('[') != -1) && (xpathString.lastIndexOf(']') != -1);
}
private String resolveNamespacePrefix(XPathFragment fragment, NamespaceResolver namespaceResolver) {
try {
if (fragment.getNamespaceURI() != null) {
return fragment.getNamespaceURI();
}
if(fragment.getPrefix() == null && fragment.isAttribute()) {
return null;
}
return namespaceResolver.resolveNamespacePrefix(fragment.getPrefix());
} catch (Exception e) {
return null;
}
}
private String resolveNamespacePrefixForURI(String namespaceURI, NamespaceResolver namespaceResolver) {
if (null == namespaceResolver) {
return null;
}
return namespaceResolver.resolveNamespaceURI(namespaceURI);
}
private Node addText(Field xmlField, Node element, String textValue) {
if (xmlField.isCDATA()) {
CDATASection cdata = element.getOwnerDocument().createCDATASection(textValue);
element.appendChild(cdata);
return cdata;
} else {
Text text = element.getOwnerDocument().createTextNode(textValue);
element.appendChild(text);
return text;
}
}
private String getStringForQName(QName qName, NamespaceResolver namespaceResolver){
if(null == qName) {
return null;
}
if(null == qName.getNamespaceURI()) {
return qName.getLocalPart();
} else {
String namespaceURI = qName.getNamespaceURI();
if(namespaceResolver == null){
throw XMLMarshalException.namespaceResolverNotSpecified(namespaceURI);
}
String prefix = namespaceResolver.resolveNamespaceURI(namespaceURI);
if(null == prefix) {
return qName.getLocalPart();
} else {
return prefix + Constants.COLON + qName.getLocalPart();
}
}
}
private NamespaceResolver getNamespaceResolverForField(Field field){
NamespaceResolver nr = (org.eclipse.persistence.oxm.NamespaceResolver) field.getNamespaceResolver();
if(nr == null){
field.setNamespaceResolver(new NamespaceResolver());
}
return (org.eclipse.persistence.oxm.NamespaceResolver) field.getNamespaceResolver();
}
private void addXsiNilToElement(Element element, Field xmlField) {
NamespaceResolver nsr = new NamespaceResolver();
nsr.setDOM(element);
String schemaInstancePrefix = resolveNamespacePrefixForURI(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, nsr);
Node parentNode = element.getParentNode();
while(schemaInstancePrefix == null && parentNode != null && parentNode.getNodeType() == Node.ELEMENT_NODE){
nsr.setDOM(element);
schemaInstancePrefix = resolveNamespacePrefixForURI(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, nsr);
parentNode = parentNode.getParentNode();
}
if(schemaInstancePrefix == null && element.getOwnerDocument() != null){
nsr.setDOM(element.getOwnerDocument().getDocumentElement());
schemaInstancePrefix = resolveNamespacePrefixForURI(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, nsr);
}
if(schemaInstancePrefix == null) {
//Not decalred in the doc
nsr = getNamespaceResolverForField(xmlField);
schemaInstancePrefix = nsr.resolveNamespaceURI(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);
if(schemaInstancePrefix == null) {
schemaInstancePrefix = nsr.generatePrefix(Constants.SCHEMA_INSTANCE_PREFIX);
}
element.setAttributeNS(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, javax.xml.XMLConstants.XMLNS_ATTRIBUTE + Constants.COLON + schemaInstancePrefix, javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);
}
element.setAttributeNS(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, Constants.SCHEMA_INSTANCE_PREFIX + Constants.COLON + Constants.SCHEMA_NIL_ATTRIBUTE, Constants.BOOLEAN_STRING_TRUE);
}
}