| /* |
| * 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 = 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()); |
| } |
| } |
| } |