blob: b90f019fa742d6e395f5a861bb27b8753f725056 [file] [log] [blame]
/*
* 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.sdo.helper;
import commonj.sdo.ChangeSummary;
import commonj.sdo.DataObject;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import javax.xml.namespace.QName;
import org.eclipse.persistence.sdo.SDOChangeSummary;
import org.eclipse.persistence.sdo.SDOConstants;
import org.eclipse.persistence.sdo.SDODataObject;
import org.eclipse.persistence.sdo.SDOProperty;
import org.eclipse.persistence.sdo.SDOSetting;
import org.eclipse.persistence.sdo.SDOType;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.internal.oxm.Namespace;
import org.eclipse.persistence.internal.oxm.XMLConversionManager;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.oxm.NamespaceResolver;
import org.eclipse.persistence.oxm.XMLConstants;
import org.eclipse.persistence.oxm.XMLField;
import org.eclipse.persistence.oxm.XMLMarshalListener;
import org.eclipse.persistence.oxm.XMLMarshaller;
import org.eclipse.persistence.oxm.XMLRoot;
import org.eclipse.persistence.oxm.record.DOMRecord;
import org.eclipse.persistence.oxm.record.MarshalRecord;
import org.eclipse.persistence.platform.xml.XMLPlatformFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* <p><b>Purpose</b>: Implementation of XMLMarshalListener used when marshalling DataObjects to XML
* <p><b>Responsibilities</b>:<ul>
* <li> Need to do extra work to marshal org.eclipse.persistence.sdo.SDOChangeSummary objects
* </ul>
*/
public class SDOMarshalListener implements XMLMarshalListener {
// marshalledObject may or may not be the root object
private Object marshalledObject;
private QName marshalledObjectRootQName;
private MarshalRecord rootMarshalRecord;
private SDOTypeHelper typeHelper;
/** maintain narrowed context from the larger HelperContext (inside the xmlMarshaller)<br>
* Visibility reduced from [public] in 2.1.0. May 15 2007 */
private XMLMarshaller xmlMarshaller;
public SDOMarshalListener(XMLMarshaller aMarshaller, SDOTypeHelper aTypeHelper) {
xmlMarshaller = aMarshaller;
typeHelper = aTypeHelper;
}
@Override
public void afterMarshal(Object obj) {
}
@Override
public void beforeMarshal(Object obj) {
if (obj instanceof SDOChangeSummary) {
SDOChangeSummary changeSummary = ((SDOChangeSummary)obj);
//CREATED - build a list of xpaths to write to the created attribute
//this must be done dynamically because the xpath is relative to the marshalledObject
//so it can't be calculated until we know what object is being marshalled
List createdSet = changeSummary.getCreated();
List xpaths = new ArrayList(createdSet.size());
String rootElementName = this.marshalledObjectRootQName.getLocalPart();
String rootNamespaceUri = this.marshalledObjectRootQName.getNamespaceURI();
if(rootNamespaceUri != null && !rootNamespaceUri.equals(XMLConstants.EMPTY_STRING)) {
org.eclipse.persistence.internal.oxm.NamespaceResolver resolver = getRootMarshalRecord().getNamespaceResolver();
if(resolver != null) {
String prefix = resolver.resolveNamespaceURI(this.marshalledObjectRootQName.getNamespaceURI());
if(prefix != null) {
rootElementName = prefix + XMLConstants.COLON + rootElementName;
}
}
}
if ((createdSet != null) && (createdSet.size() > 0)) {
Iterator anIterator = createdSet.iterator();
SDODataObject nextCreatedDO = null;
while (anIterator.hasNext()) {
// get path to the changeSummaryRoot (may not be the root marshalled object - may be internal)
nextCreatedDO = ((SDODataObject)anIterator.next());
String nextPath = getPathFromAncestor(nextCreatedDO, (SDODataObject)marshalledObject, changeSummary);
//Add sdoRef attribute...all modified objects written should have this
if(nextPath == SDOConstants.EMPTY_STRING) {
//if this is the root, just put the root element
xpaths.add(SDOConstants.SDO_CHANGESUMMARY_REF_PATH_PREFIX +
SDOConstants.SDO_XPATH_SEPARATOR_FRAGMENT +//
rootElementName);
} else {
xpaths.add(SDOConstants.SDO_CHANGESUMMARY_REF_PATH_PREFIX +
SDOConstants.SDO_XPATH_SEPARATOR_FRAGMENT +//
rootElementName + SDOConstants.SDO_XPATH_SEPARATOR_FRAGMENT + nextPath);
}
}
}
changeSummary.setCreatedXPaths(xpaths);
//Build xpathToCS
String xpathMarshalledObjToCS = getPathFromAncestor(changeSummary.getRootObject(), (SDODataObject)marshalledObject, changeSummary);
String xpathChangeSumProp = getXPathForProperty(changeSummary.getRootObject().getType().getChangeSummaryProperty());
String xpathToCS = SDOConstants.SDO_CHANGESUMMARY_REF_PATH_PREFIX + SDOConstants.SDO_XPATH_SEPARATOR_FRAGMENT + rootElementName;
// check if the CS is at the local-cs-root or is in a child property
if ((xpathMarshalledObjToCS != null) && !xpathMarshalledObjToCS.equals(SDOConstants.EMPTY_STRING)) {//SDO_XPATH_TO_ROOT)) {
// CS is not on the root
xpathToCS = xpathToCS + SDOConstants.SDO_XPATH_SEPARATOR_FRAGMENT + xpathMarshalledObjToCS;
}
xpathToCS = xpathToCS + SDOConstants.SDO_XPATH_SEPARATOR_FRAGMENT//
+xpathChangeSumProp + SDOConstants.SDO_XPATH_SEPARATOR_FRAGMENT;
//MODIFIED && DELETED
List deletedXPaths = new ArrayList();
Document document = XMLPlatformFactory.getInstance().getXMLPlatform().createDocument();
Element csNode = null;
List modifiedItems = changeSummary.getModified();
int modifiedSize = modifiedItems.size();
List newNodes = new ArrayList(modifiedSize);
SDODataObject nextModifiedDO = null;
//Iterate through CS modified items
for (int i = 0; i < modifiedSize; i++) {
nextModifiedDO = (SDODataObject)modifiedItems.get(i);
String sdoPrefix = typeHelper.getPrefix(SDOConstants.SDO_URL);
//List unsetPropNames = new ArrayList();
String uri = getURI(nextModifiedDO);
String qualifiedName = getQualifiedName(nextModifiedDO);
String sdoRefPrefix = SDOConstants.SDO_CHANGESUMMARY_REF_PATH_PREFIX + SDOConstants.SDO_XPATH_SEPARATOR_FRAGMENT;
if (uri == null) {
csNode = document.createElement(qualifiedName);
} else {
csNode = document.createElementNS(uri, qualifiedName);
}
String nextPath = getPathFromAncestor(nextModifiedDO, (SDODataObject)marshalledObject, changeSummary);
//Add sdoRef attribute...all modified objects written should have this
if(nextPath == SDOConstants.EMPTY_STRING) {
//if this is the root, just put the root element
csNode.setAttributeNS(SDOConstants.SDO_URL, sdoPrefix +//
SDOConstants.SDO_XPATH_NS_SEPARATOR_FRAGMENT +//
SDOConstants.CHANGESUMMARY_REF,//
sdoRefPrefix + rootElementName);
} else {
csNode.setAttributeNS(SDOConstants.SDO_URL, sdoPrefix +//
SDOConstants.SDO_XPATH_NS_SEPARATOR_FRAGMENT +//
SDOConstants.CHANGESUMMARY_REF,//
sdoRefPrefix + rootElementName + "/" + nextPath);
}
//Bug6346754 Add all namespaces if they are not yet declared above.
Vector namespaces = nextModifiedDO.getType().getXmlDescriptor().getNonNullNamespaceResolver().getNamespaces();
for (int j = 0; j < namespaces.size(); j++) {
Namespace next = (Namespace)namespaces.get(j);
if (declareNamespace(next.getNamespaceURI(), next.getPrefix(), changeSummary.getRootObject())) {
csNode.setAttributeNS(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, javax.xml.XMLConstants.XMLNS_ATTRIBUTE + XMLConstants.COLON + next.getPrefix(), next.getNamespaceURI());
}
}
List nextDOSettings = changeSummary.getOldValues(nextModifiedDO);
DOMRecord row = new DOMRecord(csNode);
Session session = ((SDOXMLHelper)typeHelper.getHelperContext().getXMLHelper()).getXmlContext().getSession();
row.setSession((AbstractSession) session);
//Iterate through SDOSettings for the current modified Object
SDOSetting nextSetting = null;
for (int j = 0; j < nextDOSettings.size(); j++) {
nextSetting = (SDOSetting)nextDOSettings.get(j);
if (nextSetting.isSet()) {
if (!nextSetting.getProperty().getType().isDataType()) {
if (nextSetting.getProperty().isMany()) {
List values = (List)nextSetting.getValue();
for (int k = 0; k < values.size(); k++) {
doMarshal(nextSetting.getProperty(), (DataObject)values.get(k),//
changeSummary, csNode, nextModifiedDO, deletedXPaths, xpathToCS, sdoPrefix, rootElementName);
}
} else {
doMarshal(nextSetting.getProperty(), (DataObject)nextSetting.getValue(),//
changeSummary, csNode, nextModifiedDO, deletedXPaths, xpathToCS, sdoPrefix, rootElementName);
}
} else {
//This writes out simple values
Object value = nextSetting.getValue();
if (value == null) {
//Marshal out xsi:nil=true
marshalNilAttribute(nextSetting.getProperty(), row);
} else {
String xPath = getXPathForProperty(nextSetting.getProperty());
XMLField field = new XMLField(xPath);
field.setNamespaceResolver(typeHelper.getNamespaceResolver());
row.put(field, value);
}
}
}
}
List unsetPropNames = changeSummary.getUnsetProps(nextModifiedDO);
if (!unsetPropNames.isEmpty()) {
XMLConversionManager xmlConversionManager = ((SDOXMLHelper)typeHelper.getHelperContext().getXMLHelper()).getXmlConversionManager();
String unsetPropsString = (String)xmlConversionManager.convertObject(unsetPropNames, String.class);
csNode.setAttributeNS(SDOConstants.SDO_URL, sdoPrefix +//
SDOConstants.SDO_XPATH_NS_SEPARATOR_FRAGMENT +//
SDOConstants.CHANGESUMMARY_UNSET, unsetPropsString);
}
newNodes.add(csNode);
}
changeSummary.setDeletedXPaths(deletedXPaths);
changeSummary.setModifiedDoms(newNodes);
}
}
private void doMarshal(SDOProperty prop, DataObject value, SDOChangeSummary cs,//
Element csNode, SDODataObject modifiedObject, List deletedXPaths, String xpathToCS, String sdoPrefix, String rootElementName) {
if (value == null) {
//Marshal out xsi:nil=true
DOMRecord row = new DOMRecord(csNode);
Session session = ((SDOXMLHelper)typeHelper.getHelperContext().getXMLHelper()).getXmlContext().getSession();
row.setSession((AbstractSession) session);
marshalNilAttribute(prop, row);
return;
}
boolean isDeleted = false;
//Do we need a second map or can we just check the values of the deleted map
Object original = cs.getReverseDeletedMap().get(value);
if ((original != null) && cs.isDeleted((DataObject)original)) {
isDeleted = true;
}
String qualifiedName = getXPathForProperty(prop);
String uri = null;
if (prop.isOpenContent()) {
uri = prop.getUri();
} else {
uri = ((XMLField)prop.getXmlMapping().getField()).getXPathFragment().getNamespaceURI();
}
if (isDeleted) {
String pathToNode = getPathFromAncestor(((SDODataObject)original), modifiedObject, cs);
String containerPath = null;
containerPath = getQualifiedName(modifiedObject);
deletedXPaths.add(xpathToCS + containerPath + SDOConstants.SDO_XPATH_SEPARATOR_FRAGMENT + pathToNode);
XMLRoot xmlroot = new XMLRoot();
xmlroot.setObject(value);//set the object to the deep copy
xmlroot.setNamespaceURI(uri);
xmlroot.setLocalName(qualifiedName);
xmlMarshaller.marshal(xmlroot, csNode);
} else {
//need sdoref
Element modifiedElement = null;
if (uri == null) {
modifiedElement = csNode.getOwnerDocument().createElement(qualifiedName);
} else {
modifiedElement = csNode.getOwnerDocument().createElementNS(uri, qualifiedName);
}
csNode.appendChild(modifiedElement);
String nextPath = getPathFromAncestor((SDODataObject)original, (SDODataObject)marshalledObject, cs);
//Add sdoRef attribute...all modified objects written should have this
if(nextPath == SDOConstants.EMPTY_STRING) {
//if this is the root, just put the root element
modifiedElement.setAttributeNS(SDOConstants.SDO_URL, sdoPrefix +//
SDOConstants.SDO_XPATH_NS_SEPARATOR_FRAGMENT +//
SDOConstants.CHANGESUMMARY_REF,//
SDOConstants.SDO_CHANGESUMMARY_REF_PATH_PREFIX +
SDOConstants.SDO_XPATH_SEPARATOR_FRAGMENT +//
rootElementName);
} else {
modifiedElement.setAttributeNS(SDOConstants.SDO_URL, sdoPrefix +//
SDOConstants.SDO_XPATH_NS_SEPARATOR_FRAGMENT +//
SDOConstants.CHANGESUMMARY_REF,//
SDOConstants.SDO_CHANGESUMMARY_REF_PATH_PREFIX +
SDOConstants.SDO_XPATH_SEPARATOR_FRAGMENT +//
rootElementName + "/" + nextPath);
}
//Added for bug 6346754
if ((((SDODataObject)original).getContainmentProperty() != null) && ((SDODataObject)original).getContainmentProperty().getType().isDataObjectType()) {
//may also need xsi:type
String schemaContext = ((SDOType)value.getType()).getXmlDescriptor().getSchemaReference().getSchemaContext();
QName schemaContextQName = ((SDOType)value.getType()).getXmlDescriptor().getSchemaReference().getSchemaContextAsQName(((SDOType)value.getType()).getXmlDescriptor().getNonNullNamespaceResolver());
if (schemaContext != null) {
String typeValue = schemaContext.substring(1, schemaContext.length());
String schemaInstancePrefix = ((SDOType)value.getType()).getXmlDescriptor().getNonNullNamespaceResolver().resolveNamespaceURI(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);
//may or may not need the xmlns declaration added.
String schemaContextUri = schemaContextQName.getNamespaceURI();
String schemaContextPrefix = ((SDOType)value.getType()).getXmlDescriptor().getNonNullNamespaceResolver().resolveNamespaceURI(schemaContextUri);
if (schemaContextPrefix != null) {
modifiedElement.setAttributeNS(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI, javax.xml.XMLConstants.XMLNS_ATTRIBUTE + XMLConstants.COLON + schemaContextPrefix, schemaContextQName.getNamespaceURI());
}
modifiedElement.setAttributeNS(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, schemaInstancePrefix + XMLConstants.COLON + XMLConstants.SCHEMA_TYPE_ATTRIBUTE, typeValue);
}
}
}
}
private String getURI(SDODataObject currentObject) {
String uri = null;
// check if we are at the root - no containmentProperty
if (currentObject.getContainmentProperty() == null) {
// get ns from type/schemaReference/schemaContext = "/ns0:PurchaseOrderType"
// get ns:name from the ns=nsr(value==uri) name=, or 2nd entry in xmlDescriptor->table[]
//containerPath = ((SDOType)modifiedObject.getType()).getXmlDescriptor().getNamespaceResolver().resolveNamespaceURI(uri);
// get uri from the type of the root object
uri = currentObject.getType().getURI();
} else {
uri = getUriForProperty(currentObject);
}
return uri;
}
private String getQualifiedName(SDODataObject currentObject) {
String qualifiedName = null;
// check if we are at the root - no containmentProperty
if (currentObject.getContainmentProperty() == null) {
// get ns (without name) from type/schemaReference/schemaContext = "/ns0:PurchaseOrderType"
//qualifiedName = ((SDOType)currentObject.getType()).getXmlDescriptor().getNamespaceResolver().resolveNamespaceURI(uri);
// property id's do not match - don't use
//qualifiedName += ((SDOXSDHelper)currentObject.getHelperContext().getXSDHelper()).getGlobalProperty(uri, currentObject.getType().getName() , true);
// hack: get 2nd table name
// get ns:name from the ns=nsr(value==uri) name=, or 2nd entry in xmlDescriptor->table[]
qualifiedName = currentObject.getType().getXmlDescriptor().getDefaultRootElement();
} else {
qualifiedName = getXPathForProperty(currentObject.getContainmentProperty());
}
return qualifiedName;
}
public void setMarshalledObject(Object marshalledObject) {
this.marshalledObject = marshalledObject;
}
public Object getMarshalledObject() {
return marshalledObject;
}
public void setMarshalledObjectRootQName(QName rootQName) {
this.marshalledObjectRootQName = rootQName;
}
public QName getMarshalledObjectRootQName() {
return this.marshalledObjectRootQName;
}
public void setRootMarshalRecord(MarshalRecord rootRecord) {
this.rootMarshalRecord = rootRecord;
}
public MarshalRecord getRootMarshalRecord() {
return this.rootMarshalRecord;
}
private boolean declareNamespace(String uri, String prefix, DataObject theDataObject) {
while (theDataObject != null) {
NamespaceResolver nr = ((SDOType)theDataObject.getType()).getXmlDescriptor().getNonNullNamespaceResolver();
String resolvedPrefix = nr.resolveNamespaceURI(uri);
if ((resolvedPrefix != null) && !resolvedPrefix.equals("") && resolvedPrefix.equals(prefix)) {
return false;
}
theDataObject = theDataObject.getContainer();
}
return true;
}
private String getUriForProperty(SDODataObject currentObject) {
SDOProperty prop = currentObject.getContainmentProperty();
if (prop.getXmlMapping() != null) {
return ((XMLField)prop.getXmlMapping().getField()).getXPathFragment().getNamespaceURI();
} else {
return prop.getUri();
}
}
private String getXPathForProperty(SDOProperty prop) {
return getXPathForProperty(prop, false, typeHelper.getNamespaceResolver());
}
private String getXPathForProperty(SDOProperty prop, boolean removeText, NamespaceResolver namespaceResolver) {
if ((prop).getXmlMapping() != null) {
String xpath = prop.getXmlMapping().getField().getName();
if (removeText && xpath.endsWith("/text()")) {
xpath = xpath.substring(0, xpath.length() - 7);
}
return xpath;
} else {
String name = prop.getName();
if (prop.isOpenContent()) {
String uri = prop.getUri();
if (uri != null) {
String prefix = namespaceResolver.resolveNamespaceURI(uri);
if ((prefix != null) && !prefix.equals(SDOConstants.EMPTY_STRING)) {
return prefix + XMLConstants.COLON + name;
}
}
}
return name;
}
}
/**
* INTERNAL:
* Return the XPath or SDO path from the anObject to the current internal node
*
* Prereq: We know that the targetObject will always have a parent as called
* from getPath()
* We require a ChangeSummary object for when there are deleted
* objects in the path
*
* Matching conditions:
* Iterate up the tree
* return a non-null string for the XPath when we reach the target node
*
* Function is partially based on SDOCopyHelper.copy(DataObject dataObject)
* Performance: This function is O(log n) where n=# of children in the tree
*
* @param currentPath
* @param targetDO
* @param currentObject
* @param aSeparator (XPath separator is written only between elements - not for the first call)
* @return String (representing the XPath)
*/
private String getPathFromAncestorPrivate(SDOChangeSummary aChangeSummary,//
String currentPath,//
SDODataObject targetDO,//
SDODataObject currentObject,//
String aSeparator) {
if ((currentObject == null) || (targetDO == null) || (aChangeSummary == null)) {
return currentPath;
}
// Base Case: check we are at the target object first
if (currentObject == targetDO) {
// check for indexed property if root is a ListWrapper
return currentPath;
}
// Recursive Case: O(log(n)) recursive calls, 1 for each tree level
// get parent property based on parent property name in target, property will always be set
// check containment for cases where we are searching for a sibling
SDOProperty parentContainmentProperty;
Object parent = null;
// for already deleted dataobjects - isDeleted=false, changeSummary= null - use oldContainer
if (null == currentObject.getContainer()) {
parent = aChangeSummary.getOldContainer(currentObject);
parentContainmentProperty = aChangeSummary.getOldContainmentProperty(currentObject);
// handle (at root) case for non-deleted objects for the cases
// case: ancestor not found
// case: ancestor is actually sibling
if ((null == parent) || (null == parentContainmentProperty)) {
return SDOConstants.SDO_XPATH_INVALID_PATH;
}
} else {
// normal non-deleted non-changeSummary case
parent = currentObject.getContainer();
parentContainmentProperty = currentObject.getContainmentProperty();
}
// get XPath using SDO path - block
String parentContainmentPropertyXPath = getXPathForProperty(parentContainmentProperty);
// Handle ListWrapper contained DataObjects
if (parentContainmentProperty.isMany()) {
int index = (((SDODataObject)parent).getList(parentContainmentProperty)).indexOf(currentObject);
if (index < 0) {
/*
* The current object has been deleted and was part of a ListWrapper (isMany=true)
* Get the parent of this indexed list item and check the oldSetting (List) for the
* original position of the indexed (Deleted) object
*/
// get the list containing the old value of the item
ChangeSummary.Setting anOldSetting = aChangeSummary.getOldValue((DataObject)parent, parentContainmentProperty);
if (anOldSetting != null) {
// get index directly from oldSettings based on current object - where parent was not deleted
List aDeletedParent = (List)anOldSetting.getValue();
// bug# 5587042: we will assume that index is never < 0 and remove handling code for this case where we lookup anOldSetting directly instead of via the deepCopies map
index = aDeletedParent.indexOf(aChangeSummary.getDeepCopies().get(currentObject));
} else {
// bug# 5587042: we will assume that oldSetting is never null and remove handling code for this case where we would hardcode to list.size()
}
// see: testGetXPathFromAncestorDeletedFromChildToAncestorInsideListWrapperLoggingOn
}
currentPath = parentContainmentPropertyXPath +//
SDOConstants.SDO_XPATH_LIST_INDEX_OPEN_BRACKET +//
(1 + index) +// [indexes] start at 1
SDOConstants.SDO_XPATH_LIST_INDEX_CLOSE_BRACKET +//
aSeparator +//
currentPath;
} else {
currentPath = parentContainmentPropertyXPath +//
aSeparator +//
currentPath;
}
// recursive call
return getPathFromAncestorPrivate(aChangeSummary,//
currentPath,//
targetDO,//
(SDODataObject)parent,//
SDOConstants.SDO_XPATH_SEPARATOR_FRAGMENT);// we pass an empty separator so we have \a\b and not a\b\
}
/**
* INTERNAL:
* Get path for non-deleted DataObjects<br>
* ChangeSummary is not required and is set to null.<br>
* Assumptions:<br>
* target node is an ancestor of the source (this)
* @param sourceDO
* @param targetDO
* @return String xpath
*/
public String getPathFromAncestor(SDODataObject sourceDO, SDODataObject targetDO, SDOChangeSummary cs) {
// Implementors: SDOMarshalListener
// default to no changeSummary and xpath format
/*
* Algorithm:
* (1) Intact (non-deleted) objects:
* - recursively iterate up the container of each DataObject, recording property names as we go
* (2) Deleted objects:
* - use the changeSummary to get the deleted object with oldContainer state
* - recursively iterate up the oldContainer as in (1)
* Issues:
* - a deleted indexed object inside a ListWrapper will not retain its original index
*/
// Base Case: The internal node is actually the root
// Base Case: The source and target objects are equal
// checking if this and target are equal will handle both cases above
if (sourceDO == targetDO) {
// return "" empty string and handle at implementor
return SDOConstants.EMPTY_STRING;
} else {
// Recursive Case: call private recursive reverse O(logn) traversal
// function on current object
return getPathFromAncestorPrivate(cs,//
SDOConstants.EMPTY_STRING,//
targetDO,//
sourceDO,//
SDOConstants.EMPTY_STRING// we pass an empty separator so we have \a\b and not a\b\
);
}
}
private void marshalNilAttribute(SDOProperty property, DOMRecord row) {
//Marshal out xsi:nil=true
NamespaceResolver resolver = null;
if(property.getContainingType() != null) {
resolver = property.getContainingType().getXmlDescriptor().getNamespaceResolver();
}
if(null == resolver) {
resolver = typeHelper.getNamespaceResolver();
}
String xsiPrefix = resolver.resolveNamespaceURI(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);
if ((xsiPrefix == null) || xsiPrefix.equals(SDOConstants.EMPTY_STRING)) {
NamespaceResolver nsResolverWithXsi = new NamespaceResolver();
copyNamespaces(resolver, nsResolverWithXsi);
resolver = nsResolverWithXsi;
xsiPrefix = resolver.generatePrefix(XMLConstants.SCHEMA_INSTANCE_PREFIX);
resolver.put(xsiPrefix, javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);
}
String xPath = getXPathForProperty(property, true, resolver);
xPath = xPath + "/@" + xsiPrefix + XMLConstants.COLON + XMLConstants.SCHEMA_NIL_ATTRIBUTE;
XMLField field = new XMLField(xPath);
field.setNamespaceResolver(resolver);
row.put(field, XMLConstants.BOOLEAN_STRING_TRUE);
}
private void copyNamespaces(NamespaceResolver source, NamespaceResolver target) {
if (null != source && null != target) {
if(source.hasPrefixesToNamespaces()) {
target.getPrefixesToNamespaces().putAll(source.getPrefixesToNamespaces());
}
target.setDefaultNamespaceURI(source.getDefaultNamespaceURI());
}
}
}