blob: 21f56b806bed9b9f96c5496fc37bbcaff80e6a13 [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;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import org.eclipse.persistence.exceptions.SDOException;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.oxm.XMLConversionManager;
import org.eclipse.persistence.oxm.XMLConstants;
import org.eclipse.persistence.oxm.XMLRoot;
import org.eclipse.persistence.oxm.sequenced.SequencedObject;
import org.eclipse.persistence.oxm.sequenced.Setting;
import org.eclipse.persistence.sdo.helper.ListWrapper;
import org.eclipse.persistence.sdo.helper.SDODataHelper;
import org.eclipse.persistence.sdo.helper.SDOTypeHelper;
import org.eclipse.persistence.sdo.helper.XPathEngine;
import commonj.sdo.ChangeSummary;
import commonj.sdo.DataGraph;
import commonj.sdo.DataObject;
import commonj.sdo.Property;
import commonj.sdo.Sequence;
import commonj.sdo.Type;
import commonj.sdo.helper.DataHelper;
import commonj.sdo.helper.HelperContext;
public class SDODataObject implements DataObject, SequencedObject {
/**
* Development Guidelines:
* (1) All no-argument get and single argument set functions that are outside the DataObject interface
* must be proceeded with a _ underscore in the form _getValue()/_setValue(value).
* The reason for this is to avoid naming collisions with generated classes that extend SDODataObject
* where the generated get/set methods for POJO fields would collide with the same function here.
*/
/** The Type that this DataObject represents */
private SDOType type;
// t20070714_bc4j: Add pluggable DO support for BC4J using currentValueStore
private SDODataObject container;
/**
* The (currentValueStore) will maintain the current state of our model
* after logged changes - it is a shallow copy of the original, progressively becoming deeper with changes.
*/
private ValueStore currentValueStore;
private List openContentProperties;
private List openContentPropertiesAttributes;
private Map openContentAliasNames;
private String containmentPropertyName;
private SDOChangeSummary changeSummary;
private List<SDOProperty> instanceProperties;
private String sdoRef;
private SDOSequence sequence;
private DataGraph dataGraph;
/** hold the current context containing all helpers so that we can preserve inter-helper relationships */
private HelperContext aHelperContext;
/**Unique hash ID of this Externalizable class - not required at this point because we serialize the xml representation */
@SuppressWarnings("unused")
private String text;
//static final long serialVersionUID = 5930094058760264198L;
/**
* INTERNAL:
* Private constructor.
* Use {@link org.eclipse.persistence.sdo.helper.delegates.SDODataFactoryDelegate#create(commonj.sdo.Type) SDODataFactoryDelegate.create(Type)} instead
*/
public SDODataObject() {
// Implementors should not be calling this constructor directly - please use SDODataFactory
// however if it is called we will initialize the default implementation of the currentValueStore Map
// initialize Map Implementation
// set currentValueStore Map implementation (replace any that was set in the constructor in newInstance() above)
_setCurrentValueStore(new DefaultValueStore());
}
/**
* INTERNAL:
* Set the HelperContext that will be associated with this DataObject.
* @param aContext
*/
public void _setHelperContext(HelperContext aContext) {
aHelperContext = aContext;
// reset the HelperContext on ChangeSummary
if (changeSummary != null) {
changeSummary.setHelperContext(aHelperContext);
}
}
@Override
public Object get(String path) {// path like "a/b/c"
try {
return XPathEngine.getInstance().get(path, this);
} catch (Exception e) {
// Swallow exception and return null, as per SDO 2.1 spec
return null;
}
}
@Override
public void set(String path, Object value) throws ClassCastException, UnsupportedOperationException, IllegalArgumentException {
XPathEngine.getInstance().set(path, value, this, false);
}
@Override
public boolean isSet(String path) {
return XPathEngine.getInstance().isSet(path, this);
}
@Override
public void unset(String path) {
XPathEngine.getInstance().unset(path, this);
}
@Override
public boolean getBoolean(String path) throws ClassCastException {
try {
Boolean value = (Boolean)XPathEngine.getInstance().convertObjectToValueByPath(path, Boolean.class, this);
if (value == null) {
return false;
}
return value.booleanValue();
} catch (Exception e) {
// Swallow exception and return null, as per SDO 2.1 spec
return false;
}
}
@Override
public byte getByte(String path) {
try {
Byte value = (Byte)XPathEngine.getInstance().convertObjectToValueByPath(path, Byte.class, this);
if (value == null) {
return 0;
}
return value.byteValue();
} catch (Exception e) {
// Swallow exception and return null, as per SDO 2.1 spec
return 0;
}
}
@Override
public char getChar(String path) {
try {
Character value = (Character)XPathEngine.getInstance().convertObjectToValueByPath(path, Character.class, this);
if (value == null) {
return '\u0000';
}
return value.charValue();
} catch (Exception e) {
// Swallow exception and return null, as per SDO 2.1 spec
return '\u0000';
}
}
@Override
public double getDouble(String path) {
try {
Double value = (Double)XPathEngine.getInstance().convertObjectToValueByPath(path, Double.class, this);
if (value == null) {
return 0.0d;
}
return value.doubleValue();
} catch (Exception e) {
// Swallow exception and return null, as per SDO 2.1 spec
return 0.0d;
}
}
@Override
public float getFloat(String path) {
try {
Float value = (Float)XPathEngine.getInstance().convertObjectToValueByPath(path, Float.class, this);
if (value == null) {
return 0.0f;
}
return value.floatValue();
} catch (Exception e) {
// Swallow exception and return null, as per SDO 2.1 spec
return 0.0f;
}
}
@Override
public int getInt(String path) {
try {
Integer value = (Integer)XPathEngine.getInstance().convertObjectToValueByPath(path, Integer.class, this);
if (value == null) {
return 0;
}
return value.intValue();
} catch (Exception e) {
// Swallow exception and return null, as per SDO 2.1 spec
return 0;
}
}
@Override
public long getLong(String path) {
try {
Long value = (Long)XPathEngine.getInstance().convertObjectToValueByPath(path, Long.class, this);
if (value == null) {
return 0L;
}
return value.longValue();
} catch (Exception e) {
// Swallow exception and return null, as per SDO 2.1 spec
return 0L;
}
}
@Override
public short getShort(String path) {
try {
Short value = (Short)XPathEngine.getInstance().convertObjectToValueByPath(path, Short.class, this);
if (value == null) {
return 0;
}
return value.shortValue();
} catch (Exception e) {
// Swallow exception and return null, as per SDO 2.1 spec
return 0;
}
}
@Override
public byte[] getBytes(String path) {
try {
byte[] value = (byte[])XPathEngine.getInstance().convertObjectToValueByPath(path, byte[].class, this);
return value;
} catch (Exception e) {
// Swallow exception and return null, as per SDO 2.1 spec
return null;
}
}
@Override
public BigDecimal getBigDecimal(String path) {
try {
BigDecimal value = (BigDecimal)XPathEngine.getInstance().convertObjectToValueByPath(path, BigDecimal.class, this);
return value;
} catch (Exception e) {
// Swallow exception and return null, as per SDO 2.1 spec
return null;
}
}
@Override
public BigInteger getBigInteger(String path) {
try {
BigInteger value = (BigInteger)XPathEngine.getInstance().convertObjectToValueByPath(path, BigInteger.class, this);
return value;
} catch (Exception e) {
// Swallow exception and return null, as per SDO 2.1 spec
return null;
}
}
@Override
public SDODataObject getDataObject(String path) throws ClassCastException {
Object value = get(path);
if(value instanceof ListWrapper) {
return (SDODataObject)((ListWrapper)value).get(0);
}
return (SDODataObject)value;
}
@Override
public Date getDate(String path) {
try {
Date value = (Date)XPathEngine.getInstance().convertObjectToValueByPath(path, Date.class, this);
return value;
} catch (Exception e) {
// Swallow exception and return null, as per SDO 2.1 spec
return null;
}
}
@Override
public String getString(String path) {
try {
String value = (String)XPathEngine.getInstance().convertObjectToValueByPath(path, String.class, this);
return value;
} catch (Exception e) {
// Swallow exception and return null, as per SDO 2.1 spec
return null;
}
}
@Override
public List getList(String path) {
try {
return (List)XPathEngine.getInstance().convertObjectToValueByPath(path, List.class, this);
} catch (Exception e) {
// Swallow exception and return null, as per SDO 2.1 spec
return null;
}
}
@Override
public void setBoolean(String path, boolean value) {
convertValueAndSet(path, value);
}
@Override
public void setByte(String path, byte value) {
convertValueAndSet(path, value);
}
@Override
public void setChar(String path, char value) {
convertValueAndSet(path, value);
}
@Override
public void setDouble(String path, double value) {
convertValueAndSet(path, value);
}
@Override
public void setFloat(String path, float value) {
convertValueAndSet(path, value);
}
@Override
public void setInt(String path, int value) {
convertValueAndSet(path, value);
}
@Override
public void setLong(String path, long value) {
convertValueAndSet(path, value);
}
@Override
public void setShort(String path, short value) {
convertValueAndSet(path, value);
}
@Override
public void setBytes(String path, byte[] value) {
convertValueAndSet(path, value);
}
@Override
public void setBigDecimal(String path, BigDecimal value) {
convertValueAndSet(path, value);
}
@Override
public void setBigInteger(String path, BigInteger value) {
convertValueAndSet(path, value);
}
@Override
public void setDataObject(String path, DataObject value) {
set(path, value);
}
@Override
public void setDate(String path, Date value) {
convertValueAndSet(path, value);
}
@Override
public void setString(String path, String value) {
convertValueAndSet(path, value);
}
@Override
public void setList(String path, List value) {
convertValueAndSet(path, value);
}
@Override
public Object get(int propertyIndex) throws IllegalArgumentException {
Property p = getInstanceProperty(propertyIndex);
return get(p);
}
@Override
public void set(int propertyIndex, Object value) {
try {
Property p = getInstanceProperty(propertyIndex);
set(p, value);
} catch (IndexOutOfBoundsException e) {
throw new IllegalArgumentException("PropertyIndex invalid.");
}
}
@Override
public boolean isSet(int propertyIndex) {
Property p = getInstanceProperty(propertyIndex);
return isSet(p);
}
@Override
public void unset(int propertyIndex) {
Property p = getInstanceProperty(propertyIndex);
unset(p);
}
@Override
public boolean getBoolean(int propertyIndex) throws IllegalArgumentException, ClassCastException {
Property property = getInstanceProperty(propertyIndex);
return getBoolean(property);
}
@Override
public byte getByte(int propertyIndex) throws IllegalArgumentException, ClassCastException {
Property property = getInstanceProperty(propertyIndex);
return getByte(property);
}
@Override
public char getChar(int propertyIndex) throws IllegalArgumentException, ClassCastException {
Property property = getInstanceProperty(propertyIndex);
return getChar(property);
}
@Override
public double getDouble(int propertyIndex) throws IllegalArgumentException, ClassCastException {
Property property = getInstanceProperty(propertyIndex);
return getDouble(property);
}
@Override
public float getFloat(int propertyIndex) throws IllegalArgumentException, ClassCastException {
Property property = getInstanceProperty(propertyIndex);
return getFloat(property);
}
@Override
public int getInt(int propertyIndex) throws IllegalArgumentException, ClassCastException {
Property property = getInstanceProperty(propertyIndex);
return getInt(property);
}
@Override
public long getLong(int propertyIndex) throws IllegalArgumentException, ClassCastException {
Property property = getInstanceProperty(propertyIndex);
return getLong(property);
}
@Override
public short getShort(int propertyIndex) throws IllegalArgumentException, ClassCastException {
Property property = getInstanceProperty(propertyIndex);
return getShort(property);
}
@Override
public byte[] getBytes(int propertyIndex) throws IllegalArgumentException, ClassCastException {
Property property = getInstanceProperty(propertyIndex);
return getBytes(property);
}
@Override
public BigDecimal getBigDecimal(int propertyIndex) throws IllegalArgumentException, ClassCastException {
Property property = getInstanceProperty(propertyIndex);
return getBigDecimal(property);
}
@Override
public BigInteger getBigInteger(int propertyIndex) throws IllegalArgumentException, ClassCastException {
Property property = getInstanceProperty(propertyIndex);
return getBigInteger(property);
}
@Override
public SDODataObject getDataObject(int propertyIndex) {
Property property = getInstanceProperty(propertyIndex);
return getDataObject(property);
}
@Override
public Date getDate(int propertyIndex) throws IllegalArgumentException, ClassCastException {
Property property = getInstanceProperty(propertyIndex);
return getDate(property);
}
@Override
public String getString(int propertyIndex) throws IllegalArgumentException, ClassCastException {
Property property = getInstanceProperty(propertyIndex);
return getString(property);
}
@Override
public List getList(int propertyIndex) {
Property property = getInstanceProperty(propertyIndex);
return getList(property);
}
/**
* @deprecated in SDO 2.1.0.
*/
@Deprecated
@Override
public Sequence getSequence(String path) {
// get property from path
Object anObject = get(path);
if ((null == anObject) || !(anObject instanceof DataObject)) {
// throw exception because there is no way to get the property from a null object in this context
throw SDOException.sequenceNotFoundForPath(path);
} else {
// we cannot use containmentProperty in the many case as the index is missing - use the DO
return ((DataObject)anObject).getSequence();
}
}
/**
* @deprecated in SDO 2.1.0.
*/
@Deprecated
@Override
public Sequence getSequence(int propertyIndex) {
// get property
Property aProperty = getInstanceProperty(propertyIndex);
return getSequencePrivate(aProperty);
}
/**
* @deprecated in SDO 2.1.0.
*/
@Deprecated
@Override
public Sequence getSequence(Property property) {
return getSequencePrivate(property);
}
/**
* INTERNAL:
* Return a Sequence object when the conditions of many=false and dataType=false are met.
* Throw an UnsupportedOperationException in all other cases.
*/
private Sequence getSequencePrivate(Property aProperty) {
// we process only complex types that are non-many
if ((aProperty != null) && ((SDOProperty)aProperty).getType().isSequenced() && isSet(aProperty) &&//
!((SDOProperty)aProperty).getType().isDataType() && !aProperty.isMany()) {
return ((DataObject)get(aProperty)).getSequence();
} else {
throw SDOException.sequenceNotSupportedForProperty(aProperty.getName());
}
}
@Override
public SDOSequence getSequence() {
// sequence object should be null if !sequenced
return sequence;
}
@Override
public void setBoolean(int propertyIndex, boolean value) {
convertValueAndSet(propertyIndex, value);
}
@Override
public void setByte(int propertyIndex, byte value) {
convertValueAndSet(propertyIndex, value);
}
@Override
public void setChar(int propertyIndex, char value) {
convertValueAndSet(propertyIndex, value);
}
@Override
public void setDouble(int propertyIndex, double value) {
convertValueAndSet(propertyIndex, value);
}
@Override
public void setFloat(int propertyIndex, float value) {
convertValueAndSet(propertyIndex, value);
}
@Override
public void setInt(int propertyIndex, int value) {
convertValueAndSet(propertyIndex, value);
}
@Override
public void setLong(int propertyIndex, long value) {
convertValueAndSet(propertyIndex, value);
}
@Override
public void setShort(int propertyIndex, short value) {
convertValueAndSet(propertyIndex, value);
}
@Override
public void setBytes(int propertyIndex, byte[] value) {
convertValueAndSet(propertyIndex, value);
}
@Override
public void setBigDecimal(int propertyIndex, BigDecimal value) {
convertValueAndSet(propertyIndex, value);
}
@Override
public void setBigInteger(int propertyIndex, BigInteger value) {
convertValueAndSet(propertyIndex, value);
}
@Override
public void setDataObject(int propertyIndex, DataObject value) {
set(propertyIndex, value);
}
@Override
public void setDate(int propertyIndex, Date value) {
convertValueAndSet(propertyIndex, value);
}
@Override
public void setString(int propertyIndex, String value) {
convertValueAndSet(propertyIndex, value);
}
@Override
public void setList(int propertyIndex, List value) {
convertValueAndSet(propertyIndex, value);
}
@Override
public Object get(Property property) throws IllegalArgumentException {
if (null == property) {// check null property before null type
throw new IllegalArgumentException("Argument not Supported.");
}
if ((null != type) && !type.isOpen() && property.isOpenContent()) {
throw new IllegalArgumentException("Argument not Supported.");
}
if (property.isMany()) {
return getList(property);
}
if (isSet(property)) {
return getPropertyInternal(property);
}
// return the default or a cached pseudo default Object for an unset property
// JIRA-253: We are wrapping primitive numerics with a wrapper Object
return property.getDefault();
}
/**
* INTERNAL:
* Create a dynamic open content property if no property exists for (name).
* @param name
* @param value
* @return Property
* @throws UnsupportedOperationException
* @throws IllegalArgumentException
*/
public Property defineOpenContentProperty(String name, Object value) throws UnsupportedOperationException, IllegalArgumentException {
if (value == null) {
return null;
}
DataObject propertyDO = aHelperContext.getDataFactory().create(SDOConstants.SDO_URL, SDOConstants.PROPERTY);
propertyDO.set("name", name);
Type sdotype = null;
boolean isMany = false;
boolean isContainment = false;
Class valueClass = value.getClass();
if (value instanceof Collection) {
if (((Collection)value).size() > 0) {
Object firstObject = ((Collection)value).iterator().next();
if (firstObject != null) {
valueClass = firstObject.getClass();
if (firstObject instanceof DataObject) {
if (((DataObject)firstObject).getContainer() == null) {
isContainment = true;
}
sdotype = ((DataObject)firstObject).getType();
} else {
sdotype = ((SDOTypeHelper)aHelperContext.getTypeHelper()).getTypeForSimpleJavaType(valueClass);
}
} else {
return null;
}
} else {
return null;
}
isMany = true;
} else if (value instanceof DataObject) {
if (((DataObject)value).getContainer() == null) {
isContainment = true;
}
sdotype = ((DataObject)value).getType();
} else {
Property xmlElementProperty = aHelperContext.getTypeHelper().getOpenContentProperty(SDOConstants.SDOXML_URL, SDOConstants.XMLELEMENT_PROPERTY_NAME);
propertyDO.set(xmlElementProperty, Boolean.TRUE);
sdotype = ((SDOTypeHelper)aHelperContext.getTypeHelper()).getTypeForSimpleJavaType(valueClass);
}
propertyDO.set("type", sdotype);
propertyDO.set("many", isMany);
propertyDO.set("containment", isContainment);
return aHelperContext.getTypeHelper().defineOpenContentProperty(null, propertyDO);
}
public Property defineOpenContentProperty(String name, Object value, Type sdotype) throws UnsupportedOperationException, IllegalArgumentException {
if (value == null) {
return null;
}
if(sdotype == null){
return defineOpenContentProperty(name, value);
}
DataObject propertyDO = aHelperContext.getDataFactory().create(SDOConstants.SDO_URL, SDOConstants.PROPERTY);
propertyDO.set("name", name);
boolean isMany = false;
boolean isContainment = false;
Class valueClass = value.getClass();
if (value instanceof Collection) {
if (((Collection)value).size() > 0) {
Object firstObject = ((Collection)value).iterator().next();
if (firstObject != null) {
valueClass = firstObject.getClass();
if (firstObject instanceof DataObject) {
if (((DataObject)firstObject).getContainer() == null) {
isContainment = true;
}
sdotype = ((DataObject)firstObject).getType();
} else {
sdotype = ((SDOTypeHelper)aHelperContext.getTypeHelper()).getTypeForSimpleJavaType(valueClass);
}
}
}
isMany = true;
} else if (value instanceof DataObject) {
if (((DataObject)value).getContainer() == null) {
isContainment = true;
}
sdotype = ((DataObject)value).getType();
} else {
sdotype = ((SDOTypeHelper)aHelperContext.getTypeHelper()).getTypeForSimpleJavaType(valueClass);
}
propertyDO.set("type", sdotype);
propertyDO.set("many", isMany);
propertyDO.set("containment", isContainment);
return aHelperContext.getTypeHelper().defineOpenContentProperty(null, propertyDO);
}
@Override
public void set(Property property, Object value) throws UnsupportedOperationException, IllegalArgumentException {
set((SDOProperty) property, value, true);
}
public void setInternal(SDOProperty property, Object value, boolean updateSequence) throws UnsupportedOperationException, IllegalArgumentException {
if (null == getType()) {
throw new UnsupportedOperationException("Type is null");
}
if (property.isOpenContent() && !getType().isOpen()) {// not open and not in types currentValueStore
throw new IllegalArgumentException("DataObject " + this + " is not Open for property " + property.getName());
}
// Note: get() will call setPropertyInternal() if the list is null = not set yet - we need to propagate the updateSequence flag
Object oldValue = get(property);
boolean wasSet = isSet(property);
if (wasSet && (oldValue == value)) {
return;
} else {
_setModified(true);
}
if (property.isMany()) {
List listValue;
if (null == value) {
listValue = new ListWrapper(this, property);
} else {
try {
listValue = (List) value;
} catch(ClassCastException e) {
throw new IllegalArgumentException("Properties with isMany = true can only be set on list values.");
}
}
if (property.isContainment() || isContainedByDataGraph(property)) {
for(Object next: listValue) {
if(next instanceof SDODataObject) {
if (parentContains(next)) {
throw new IllegalArgumentException("Circular reference.");
}
}
}
}
ListWrapper oldValueListWrapper = (ListWrapper) oldValue;
// 20060529: v33: Move clear() out of ListWrapper.addAll()
// handle clearing of elements which also calls removeContainment(prop) outside of addAll()
oldValueListWrapper.clear(updateSequence);
// handle updateContainment and sequences inside addAll()
oldValueListWrapper.addAll((Collection)value, updateSequence);// for non-default Pluggable impl this function is not required
} else {
if (property.isContainment() || isContainedByDataGraph(property)) {
if (parentContains(value)) {
throw new IllegalArgumentException("Circular reference.");
}
// detach the oldValue from this dataObject
if(null != oldValue) {
detach(property, oldValue);
}
// sets the new value's container and containment prop to this dataobject, detaches from other owner...not right
/**
* Case: set(do) is actually a move(do) between two CS - detach required
* Case: set(do) is actually an add(do) on a single CS - detach not required
*/
SDODataObject dataObjectValue = (SDODataObject) value;
if (dataObjectValue != null) {
updateContainment(property, dataObjectValue);
}
}
// process pluggable currentValueStore and set [value] as a property of [this] as well as sequences
setPropertyInternal(property, value, updateSequence);
}
if (getType().isOpen() && property.isOpenContent()) {
addOpenContentProperty(property);
}
}
/**
* INTERNAL:
* Sets the value of the given property of the object to the new value.
* <p>
* The use of a false updateSequence flag is internally implemented during an SDOSequence.add() call.
* Refactor: we need to abstract this function using a type of Command pattern to handle the sequence context.
* @param property
* @param value
* @param updateSequence
* @throws UnsupportedOperationException
* @throws IllegalArgumentException
*/
public void set(SDOProperty property, Object value, boolean updateSequence) throws UnsupportedOperationException, IllegalArgumentException {
if (null == property) {
throw new IllegalArgumentException("Illegal Argument.");
}
if (property.isReadOnly()) {
throw new UnsupportedOperationException("Property is Readonly." + property.getName() + " " + getType().getName());
}
// Can't set null on a non-nullable property - implementation specific handling, one of:
// 1) perform an unset operation
// 2) throw new UnsupportedOperationException("Property ["+ property.getName() +"] is non-nullable");
// We will perform an unset op...
if (value == null && !property.isNullable()) {
unset(property, false, updateSequence);
} else {
setInternal(property, value, updateSequence);
}
}
@Override
public boolean isSet(Property property) {
if (null == property) {
throw SDOException.cannotPerformOperationOnNullArgument("isSet");
}
if (property.isMany()) {
List value = getList(property);
return !value.isEmpty();
} else {
return isSetInternal(property);
}
}
@Override
public void unset(Property property) {
if (null == property) {
throw SDOException.cannotPerformOperationOnNullArgument("unset");
}
unset(property, false);
}
/**
* INTERNAL:
* This function is implemented internally to unset the specified property on this DataObject
* @param property
* @param fromDelete
*/
private void unset(Property property, boolean fromDelete) {
unset(property, fromDelete, true);
}
/**
* INTERNAL:
* Unset the specified property on this DataObject.
* The fromDelete parameter specifies whether we are from a delete or unset/detach operation.
* The updateSequence parameter is used internally to stop a bidirectional update in the SDOSequence
* when originally called from this Sequence.
* @param property
* @param fromDelete
* @param updateSequence
*/
public void unset(Property property, boolean fromDelete, boolean updateSequence) {
if (null == property) {
throw SDOException.cannotPerformOperationOnNullArgument("unset");
}
if (property.isReadOnly()) {
throw new UnsupportedOperationException("Property is Readonly." + property.getName() + " " + getType().getName());
}
boolean wasSet = isSet(property);
if (wasSet) {
if(!fromDelete){
_setModified(true);
}
} else {
return;
}
if (property.isContainment() || isContainedByDataGraph(property)) {
Object oldValue = get(property);
if (oldValue != null) {
if (property.isMany()) {
for (int itemIndex = 0, size = ((List)oldValue).size(); itemIndex < size;
itemIndex++) {
SDODataObject manyItem = (SDODataObject)((List)oldValue).get(itemIndex);
if (manyItem != null) {
manyItem.detachOrDelete(fromDelete);
}
}
} else {
((SDODataObject)oldValue).detachOrDelete(fromDelete);
}
}
}
if (wasSet) {
unsetInternal(property, updateSequence);
}
}
@Override
public boolean getBoolean(Property property) throws IllegalArgumentException, ClassCastException {
Boolean propertyBooleanValue = (Boolean)convertObjectToValue(property, Boolean.class);
if (propertyBooleanValue == null) {
return false;
}
return propertyBooleanValue.booleanValue();
}
@Override
public byte getByte(Property property) throws IllegalArgumentException, ClassCastException {
Byte propertyByteValue = (Byte)convertObjectToValue(property, Byte.class);
if (propertyByteValue == null) {
return 0;
}
return propertyByteValue.byteValue();
}
@Override
public char getChar(Property property) throws IllegalArgumentException {
Character propertyCharValue = (Character)convertObjectToValue(property, Character.class);
if (propertyCharValue == null) {
return '\u0000';
}
return propertyCharValue.charValue();
}
@Override
public double getDouble(Property property) throws IllegalArgumentException {
Double propertyDoubleValue = (Double)convertObjectToValue(property, Double.class);
if (propertyDoubleValue == null) {
return 0.0d;
}
return propertyDoubleValue.doubleValue();
}
@Override
public float getFloat(Property property) throws IllegalArgumentException {
Float propertyFloatValue = (Float)convertObjectToValue(property, Float.class);
if (propertyFloatValue == null) {
return 0.0f;
}
return propertyFloatValue.floatValue();
}
@Override
public int getInt(Property property) throws IllegalArgumentException {
Integer propertyIntegerValue = (Integer)convertObjectToValue(property, Integer.class);
if (propertyIntegerValue == null) {
return 0;
}
return propertyIntegerValue.intValue();
}
@Override
public long getLong(Property property) throws IllegalArgumentException {
Long propertyLongValue = (Long)convertObjectToValue(property, Long.class);
if (propertyLongValue == null) {
return 0L;
}
return propertyLongValue.longValue();
}
@Override
public short getShort(Property property) throws IllegalArgumentException {
Short propertyShortValue = (Short)convertObjectToValue(property, Short.class);
if (propertyShortValue == null) {
return 0;
}
return propertyShortValue.shortValue();
}
@Override
public byte[] getBytes(Property property) throws IllegalArgumentException {
byte[] propertyByteValue = (byte[])convertObjectToValue(property, byte[].class);
return propertyByteValue;
}
@Override
public BigDecimal getBigDecimal(Property property) throws IllegalArgumentException {
BigDecimal propertyDecimalValue = (BigDecimal)convertObjectToValue(property, BigDecimal.class);
return propertyDecimalValue;
}
@Override
public BigInteger getBigInteger(Property property) throws IllegalArgumentException {
BigInteger propertyBigIntegerValue = (BigInteger)convertObjectToValue(property, BigInteger.class);
return propertyBigIntegerValue;
}
@Override
public SDODataObject getDataObject(Property property) throws IllegalArgumentException, ClassCastException {
if(property != null && property.isMany()) {
List value = (List)get(property);
if(value.size() > 0) {
return (SDODataObject)value.get(0);
} else {
return null;
}
}
return (SDODataObject)get(property);
}
@Override
public Date getDate(Property property) {
if (null == property) {
throw SDOException.cannotPerformOperationOnNullArgument("getDate");
}
if (property.getType().equals(SDOConstants.SDO_STRING)) {
DataHelper dHelper = aHelperContext.getDataHelper();
String dateString = (String)get(property);
return dHelper.toDate(dateString);
}
Date propertyDateValue = (Date)convertObjectToValue(property, Date.class);
return propertyDateValue;
}
@Override
public String getString(Property property) {
String propertyStringValue = (String)convertObjectToValue(property, String.class);
return propertyStringValue;
}
@Override
public List getList(Property property) {
if (null == property) {
throw SDOException.cannotPerformOperationOnNullArgument("getList");
}
if (((type != null) && !type.isOpen() && property.isOpenContent())) {
throw new IllegalArgumentException("Argument not Supported.");
}
if (!property.isMany()) {
throw new ClassCastException("can not call getList for a property that has isMany false.");
}
/*
* Check the currentValueStore map
* (1) If the property is stored
* (1a) return the ListWrapper or
* (1b) return and store an empty default value ListWrapper
* (2) if the property is not set
* (2a) return and store an empty default value ListWrapper
*/
Object value = getPropertyInternal(property);
if ((value != null) && value instanceof List) {
return (List)value;
}
// get a default empty list
List theList = new ListWrapper(this, property);
if (getType().isOpen() && property.isOpenContent()) {
addOpenContentProperty(property);
}
// set the property to a default empty list
setPropertyInternal((SDOProperty) property, theList, false);
return theList;
}
@Override
public void setBoolean(Property property, boolean value) {
convertValueAndSet(property, value);
}
@Override
public void setByte(Property property, byte value) {
convertValueAndSet(property, value);
}
@Override
public void setChar(Property property, char value) {
convertValueAndSet(property, value);
}
@Override
public void setDouble(Property property, double value) {
convertValueAndSet(property, value);
}
@Override
public void setFloat(Property property, float value) {
convertValueAndSet(property, value);
}
@Override
public void setInt(Property property, int value) {
convertValueAndSet(property, value);
}
@Override
public void setLong(Property property, long value) {
convertValueAndSet(property, value);
}
@Override
public void setShort(Property property, short value) {
convertValueAndSet(property, value);
}
@Override
public void setBytes(Property property, byte[] value) {
convertValueAndSet(property, value);
}
@Override
public void setBigDecimal(Property property, BigDecimal value) {
convertValueAndSet(property, value);
}
@Override
public void setBigInteger(Property property, BigInteger value) {
convertValueAndSet(property, value);
}
@Override
public void setDataObject(Property property, DataObject value) {
set(property, value);
}
@Override
public void setDate(Property property, Date value) {
convertValueAndSet(property, value);
}
@Override
public void setString(Property property, String value) {
convertValueAndSet(property, value);
}
@Override
public void setList(Property property, List value) {
convertValueAndSet(property, value);
}
@Override
public DataObject createDataObject(String propertyName) {
Property aProperty = getInstanceProperty(propertyName);
return createDataObject(aProperty);
}
@Override
public SDODataObject createDataObject(int propertyIndex) {
Property aProperty = getInstanceProperty(propertyIndex);
return createDataObject(aProperty);
}
@Override
public SDODataObject createDataObject(Property aProperty) {
if (aProperty.isContainment()) {
Type aType = aProperty.getType();
if (aType != null) {
return createDataObject(aProperty, aType);
}
}
return null;
}
@Override
public SDODataObject createDataObject(String propertyName, String namespaceURI, String typeName) {
Property aProperty = getInstanceProperty(propertyName);
Type aType = aHelperContext.getTypeHelper().getType(namespaceURI, typeName);
return createDataObject(aProperty, aType);
}
@Override
public SDODataObject createDataObject(int propertyIndex, String namespaceURI, String typeName) {
Property aProperty = getInstanceProperty(propertyIndex);
Type aType = aHelperContext.getTypeHelper().getType(namespaceURI, typeName);
return createDataObject(aProperty, aType);
}
@Override
public SDODataObject createDataObject(Property property, Type aType) {
SDODataObject created = (SDODataObject) aHelperContext.getDataFactory().create(aType);
if (property.isMany()) {
//getList.add will call updateContainment
((ListWrapper)getList(property)).add(created, true);
} else {
set(property, created);
}
_setModified(true);
created._setCreated(true);
return created;
}
/**
* INTERNAL:
* Perform a detach on a DataObject or List of DataObjects
* @param property
* @param oldValue (List or Object)
*/
private void detach(Property property, Object oldValue) {
// This function is called internally from set(property, object, boolean) when a detach of the existing object is required
if (property.isMany()) {
List oldListValue = (List) oldValue;
for (int i = 0, size = oldListValue.size(); i < size; i++) {
Object nextValue = oldListValue.get(i);
if ((nextValue != null) && (nextValue instanceof SDODataObject)) {
((SDODataObject)nextValue).detachOrDelete(false);
}
}
} else {
if ((oldValue != null) && (oldValue instanceof SDODataObject)) {
((SDODataObject)oldValue).detachOrDelete(false);
}
}
}
/**
* INTERNAL:
* Recursively walk the tree and set oldSettings for a detached/deleted object.
* This function performs a single preOrder traversal of the tree.
* An unset is done for each property if the action = delete
* Implementors: detach() and delete() via detach
* @param fromDelete (flag the action true = delete, false = detach)
*/
public void detachOrDelete(boolean fromDelete) {
// If we are detaching the root - perform a no-operation
if ((null == container) && !fromDelete) {
return;
}
boolean isCSRoot = (null != changeSummary) && (changeSummary.getRootObject() == this);
// Detaching the dataObject that is the root of the CS doesn't need to do anything - truncate the recursive traversal
if(!fromDelete && isCSRoot){
return;
}
// This is the root of the recursive loop
detachDeleteRecursivePrivate(fromDelete, !isCSRoot, true);
}
/**
* INTERNAL:
* Recursively walk the tree and set oldSettings for a detached/deleted object.
* @param fromDelete
* @param clearCS (true = clear the cs field) = !(is root of the detach/delete subtree the CS root?)
* @param isRootOfRecursiveLoop (are we at the root of the detach/delete or inside the subtree)
*/
private void detachDeleteRecursivePrivate(boolean fromDelete, boolean clearCS, boolean isRootOfRecursiveLoop) {
// Store flag for (is root of the detach/delete subtree the CS root?) before we modify it when this object has no container
boolean subTreeRootHasCStoClear = clearCS;
if (null == getContainer()) {
clearCS = false;
}
if (isRootOfRecursiveLoop || fromDelete) {
if (null != getContainer()) {
getContainer()._setModified(true);
_setContainer(null);
_setContainmentPropertyName(null);
}
}
_setDeleted(true);
/** Order here is important - the dataGraph pointer should be cleared preorder before we enter the recursive loop to clear child DO's */
DataGraph previousDataGraph = getDataGraph();
// Remove back pointer to containing DataGraph
setDataGraph(null);
List instancePropertiesList = getInstanceProperties();
for (int i = 0, psize = instancePropertiesList.size(); i < psize; i++) {
SDOProperty nextProperty = (SDOProperty)instancePropertiesList.get(i);
Object oldValue = get(nextProperty);
if (!nextProperty.getType().isChangeSummaryType()) {
// Non-containment nodes have changeSummary and dataGraph pointers if they were in a dataGraph
if (nextProperty.isContainment() || isContainedByDataGraph(previousDataGraph, nextProperty)) {
if (nextProperty.isMany()) {
Object manyItem;
for (int j = 0, lsize = ((List)oldValue).size(); j < lsize; j++) {
manyItem = ((List)oldValue).get(j);
detachDeleteRecursivePrivateHelper((SDODataObject)manyItem, fromDelete, clearCS);
}
} else {
detachDeleteRecursivePrivateHelper((SDODataObject)oldValue, fromDelete, clearCS);
}
}
if (fromDelete && !nextProperty.isReadOnly()) {
unset(nextProperty, fromDelete);
}
}
}
/**
* Bug # 6202793
* We delete the changeSummary field in the following use cases
* in addition to when the clearCS field was passed in as or transformed to true.
* Case 0: detach subtree root from root (with cs) = false
* Case 1: detach subtree internal from root (with cs) = false
* Case 2: delete subtree root from root (with cs) = false
* Case 3: delete subtree internal from root (with cs) = true
* Case 4: detach subtree root from root (without cs) = false
* Case 5: detach subtree internal from root (without cs) = false
* Case 6: delete subtree root from root (without cs) = true
* Case 7: delete subtree internal from root (without cs) = true
*/
if (clearCS || subTreeRootHasCStoClear) {
_setChangeSummary(null);
}
}
/**
* INTERNAL:
* Call detach or delete recursively on aDataObject after possibly changing the flag whether to clear the ChangeSummary pointer at this level
* @param aDataObject
* @param fromDelete
* @param clearCS
*/
private void detachDeleteRecursivePrivateHelper(SDODataObject aDataObject, boolean fromDelete, boolean clearCS) {
if (aDataObject != null) {
// Return whether (aDataObject) is the changeSummary root.
boolean isCSRoot = (aDataObject.getChangeSummary() != null) &&//
(aDataObject.getChangeSummary().getRootObject() == aDataObject);
// If we are at the CS root - we do not clear the changeSummary
if (isCSRoot) {
clearCS = false;
} else {
if (aDataObject.getContainer() != null) {
ChangeSummary containerCS = aDataObject.getContainer().getChangeSummary();
// If there is no CS field set above this object then clear any ChangeSummary pointer at this level
if (containerCS == null) {
clearCS = true;
}
}
}
aDataObject.detachDeleteRecursivePrivate(fromDelete, clearCS, false);
}
}
@Override
public void detach() {
detachWithSequenceUpdate();
}
/**
* INTERNAL:
* Perform a detach action that originated from a detach and update the sequence.
* Case 01
*/
private void detachWithSequenceUpdate() {
detach(false, true);
}
/**
* INTERNAL:
* Perform a detach action that originated from a delete and update the sequence.
* Case 11
*/
private void deleteWithSequenceUpdate() {
detach(true, true);
}
/**
* INTERNAL:
* Removes this DataObject from its container, if any.
* Same as
* getContainer().getList(getContainmentProperty()).remove(this) or
* getContainer().unset(getContainmentProperty())
* depending on getContainmentProperty().isMany() respectively.
* @param fromDelete (true = delete action, false = detach/unset)
* @param updateSequence
*/
private void detach(boolean fromDelete, boolean updateSequence) {
// Note: there is no case10 where fromDelete=true and updateSequence=false
SDOProperty containmentProperty = getContainmentProperty();
if ((containmentProperty != null) && containmentProperty.isReadOnly()) {
throw new UnsupportedOperationException("Property is Readonly." + containmentProperty.getName() + " " + getType().getName());
}
if (containmentProperty != null) {
if (getContainmentProperty().isMany()) {
List oldList = getContainer().getList(containmentProperty);
// pass remove containment flag instead of calling remove(this) and detachOrDelete(fromDelete) separately
// This will invoke creation of an intact list copy before removing its containment and clearing its changeSummary
((ListWrapper)oldList).remove(this, fromDelete, updateSequence);
} else {
getContainer().unset(containmentProperty, fromDelete, updateSequence);
}
} else {
_setDeleted(true);
detachOrDelete(fromDelete);
}
}
@Override
public void delete() {
deleteWithSequenceUpdate();
}
@Override
public SDODataObject getContainer() {
return container;
}
@Override
public SDOProperty getContainmentProperty() {
if ((container != null) && (containmentPropertyName != null)) {
return container.getInstanceProperty(containmentPropertyName);
} else {
return null;
}
}
@Override
public DataGraph getDataGraph() {
return dataGraph;
}
public void setDataGraph(DataGraph dataGraph) {
this.dataGraph = dataGraph;
}
@Override
public SDOType getType() {
return type;
}
@Override
public List getInstanceProperties() {
if (null == instanceProperties) {
instanceProperties = new ArrayList();
}
return instanceProperties;
}
@Override
public SDOProperty getProperty(String propertyName) {
return getInstanceProperty(propertyName);
}
@Override
public SDOProperty getInstanceProperty(String propertyName) {
if (getType() == null) {
throw new UnsupportedOperationException("Type is null");
}
SDOProperty property = getType().getProperty(propertyName);
if (null == property) {
property = (SDOProperty)_getOpenContentAliasNamesMap().get(propertyName);
if (null == property) {
for (int i = 0; i < _getOpenContentProperties().size(); i++) {
SDOProperty nextProp = (SDOProperty)_getOpenContentProperties().get(i);
if (nextProp.getName().equals(propertyName)) {
return nextProp;
}
}
if (null == property) {
for (int i = 0; i < _getOpenContentPropertiesAttributes().size(); i++) {
SDOProperty nextProp = (SDOProperty)_getOpenContentPropertiesAttributes().get(i);
if (nextProp.getName().equals(propertyName)) {
return nextProp;
}
}
}
}
}
return property;
}
SDOProperty getInstanceProperty(String propertyName, Object value) {
SDOProperty sdoProperty = getInstanceProperty(propertyName);
if(null == sdoProperty) {
sdoProperty = (SDOProperty) defineOpenContentProperty(propertyName, value);
}
return sdoProperty;
}
/**
* INTERNAL:
* @param propertyIndex
* @return
* @throws SDOException
*/
public SDOProperty getInstanceProperty(int propertyIndex) throws IllegalArgumentException {
try {
SDOProperty property = getInstancePropertiesArray()[propertyIndex];
return property;
} catch (IndexOutOfBoundsException e) {
throw SDOException.propertyNotFoundAtIndex(e, propertyIndex);
}
}
@Override
public SDODataObject getRootObject() {
if (getContainer() != null) {
return getContainer().getRootObject();
}
return this;
}
@Override
public SDOChangeSummary getChangeSummary() {
return changeSummary;
}
/**
* INTERNAL:
* Set flag created value.
* @param created flag created's new value.
*/
public void _setCreated(boolean created) {
if (changeSummary != null) {
changeSummary.setCreated(this, created);
}
}
/**
* INTERNAL:
* Set flag modified value.
* @param modified flag modified's new value.
*/
public void _setModified(boolean modified) {
if (changeSummary != null) {
if (isLogging()) {
updateChangeSummaryWithOriginalValues();
}
}
}
/**
* INTERNAL:
* Set flag deleted value.
* @param deleted flag deleted's new value.
*/
private void _setDeleted(boolean deleted) {
// reduced scope from public to private 17 May 2007 - use the public deprecated public function until we remove it
if (changeSummary != null) {
boolean wasDeleted = changeSummary.setDeleted(this, deleted);
if (wasDeleted && isLogging()) {
updateChangeSummaryWithOriginalValues();
}
}
}
/**
* INTERNAL:
* @param csm
*/
private void setChangeSummaryNonRecursive(ChangeSummary csm) {
// set changeSummary instance using property
changeSummary = (SDOChangeSummary) csm;
if (getType() != null) {
SDOProperty changeSummaryProperty = getType().getChangeSummaryProperty();
if (changeSummaryProperty != null) {
setChangeSummaryProperty(changeSummaryProperty, csm);
}
}
}
/**
* INTERNAL:
* Recursively Set this DataObject's ChangeSummary as passed in value.
* @param aChangeSummary the ChangeSummary taking this DataObject as root.
*/
public void _setChangeSummary(SDOChangeSummary aChangeSummary) {
updateChangeSummaryAndDataGraph(aChangeSummary, getDataGraph());
}
/**
* INTERNAL:
* Recursively set this DataObject's changeSummary and dataGraph.
* The parent changeSummary and dataGraph may be null.
* Preconditions: No changeSummary must exist on the child tree.
* Callers: Typically updateContainment (during a set) or delete/detach
* of an object without its own changeSummary property.
* @param aDataGraph
*/
private void updateChangeSummaryAndDataGraph(ChangeSummary aChangeSummary, DataGraph aDataGraph) {
Iterator iterProperties = getInstanceProperties().iterator();
Property property;
Object object;
Object listContainedObject;
// Add back pointer to containing DataGraph - in preOrder sequence before recursing
setDataGraph(aDataGraph);
// Recurse currentValueStore
while (iterProperties.hasNext()) {
property = (Property)iterProperties.next();
// Note: Both opposite currentValueStore cannot be isContainment=true
// special handling for bidirectional currentValueStore, do a shallow set
// do not recurse bidirectional currentValueStore that are non-containment
if (property.isContainment() || isContainedByDataGraph(property)) {
object = get(property);
if (object instanceof SDODataObject) {// DataObject child of this DataObject
((SDODataObject)object).updateChangeSummaryAndDataGraph(aChangeSummary, aDataGraph);
}
if (object instanceof ListWrapper) {// ListWrapper child of this DataObject
Iterator anIterator = ((ListWrapper)object).iterator();
while (anIterator.hasNext()) {
listContainedObject = anIterator.next();
if (listContainedObject instanceof SDODataObject) {
((SDODataObject)listContainedObject).updateChangeSummaryAndDataGraph(aChangeSummary, aDataGraph);
}
}
}
}
}
// Set/Clear changeSummary in postOrder sequence
setChangeSummaryNonRecursive(aChangeSummary);
}
/**
* INTERNAL:
* Recursively set this DataObject's DataGraph
* This function serves as a copy of updateChangeSummaryAndDataGraph() to recursively walk and set the dataGraph.
* that will be run when no recursion occurs in the case that an object (with a changeSummary)
* is set internally to a tree (without a changeSummary).
* Callers: Typically updateContainment (during a set) or delete/detach
* when the current object is internal with its own changeSummary property.
* @param aDataGraph
*/
private void updateDataGraph(DataGraph aDataGraph) {
Iterator iterProperties = getInstanceProperties().iterator();
Property property;
Object object;
Object listContainedObject;
// Add back pointer to containing DataGraph - in preOrder sequence before recursing
setDataGraph(aDataGraph);
// Recurse currentValueStore
while (iterProperties.hasNext()) {
property = (Property)iterProperties.next();
// Note: Both opposite currentValueStore cannot be isContainment=true
// special handling for bidirectional currentValueStore, do a shallow set
// do not recurse bidirectional currentValueStore that are non-containment
if (property.isContainment() || isContainedByDataGraph(property)) {
object = get(property);
if (object instanceof SDODataObject) {// DataObject child of this DataObject
((SDODataObject)object).updateDataGraph(aDataGraph);
}
if (object instanceof ListWrapper) {// ListWrapper child of this DataObject
Iterator anIterator = ((ListWrapper)object).iterator();
while (anIterator.hasNext()) {
listContainedObject = anIterator.next();
if (listContainedObject instanceof SDODataObject) {
((SDODataObject)listContainedObject).updateDataGraph(aDataGraph);
}
}
}
}
}
}
/**
* INTERNAL:
*
* @param property
* @param value
*/
private void setChangeSummaryProperty(SDOProperty property, ChangeSummary value) {
if (property.isOpenContent()) {
throw new IllegalArgumentException("ChangeSummary can not be on an open content property.");
}
if (property.isMany()) {
throw new IllegalArgumentException("ChangeSummary can not be on a property with many set to true.");
}
if (isLogging()) {
_setModified(true);
}
// process pluggable currentValueStore
setPropertyInternal(property, value, true);
}
/**
* INTERNAL:
* This function reverses any operations that were performed on this object since change tracking
* was turned on. The object is returned to the state when logging was first started.<br>
* @param isCSRoot
* @param cs
* @param origContainer
* @param origContainmentPropName
*/
public void undoChanges(boolean isCSRoot, ChangeSummary cs,//
SDODataObject origContainer, String origContainmentPropName) {
// dont do anything if the changeSummary is null
if (null == cs) {
return;
}
/**
* Logic: 4 steps - we defer undo responsibilty to each of DataObject, ListWrapper and Sequence.
* 1) swap ValueStores (for DataObject)
* 2) swap ListWrapper (if it exists)
* 3) swap Sequence (if it exists)
* 4) #5928954 restore openContent properties to the 3 data structures on SDODataObject -
* openContentPropertiesMap<String,Property>, openContentProperties(List<Property>). instanceProperties (List<Property>)
*
* Use Cases:
* UC1: no change
* doDirty=false seqDirty=false
* UC2: change non-sequenced do
* doDirty=false seqDirty=false
* UC3: change sequenced do
* doDirty=true. SeqDirty=true
* UC4: change unstructured text on sequence only
* [doDirty=false seqDirty=true] - special case - verify that we do not do it twice (here and in UC3)
* UC5: unset(oc property), undoChanges(), get("open property")
*
*/
// base step: swap valueStores at the current node
if (((SDOChangeSummary)cs).isDirty(this)) {
// reset only if changes were done
if (!isCSRoot) {
// reset changeSummary
if (null == changeSummary) {
// we are not recursively setting cs - so don't use the set function as child nodes will be replaced later
changeSummary = (SDOChangeSummary) cs;
}
// reset container
if (null == container) {
_setContainer(origContainer);
}
// reset containmentProperty name
if (null == containmentPropertyName) {
_setContainmentPropertyName(origContainmentPropName);
}
}
// swap valueStores
_setCurrentValueStore((ValueStore)((SDOChangeSummary)cs).getOriginalValueStores().get(this));
// return original to null (no changes)
((SDOChangeSummary)cs).getOriginalValueStores().remove(this);
}
// swap sequences (may be UC2(DataObject and sequence change) or UC4 (sequence change only)
if (getType().isSequenced()) {// assume sequence is !null
// perform sequence.isDirty check independent of a dataObject.isDirty check
if (((SDOChangeSummary)cs).isDirty(sequence)) {
Sequence currentSequence = sequence;
SDOSequence originalSequence = (SDOSequence)((SDOChangeSummary)cs).getOriginalSequences().get(this);
// both sequences are either null or set
if ((null == originalSequence) && (null != currentSequence)) {
throw SDOException.oldSequenceNotFound();
} else {
sequence = originalSequence;
}
// reset the cache map
((SDOChangeSummary)cs).getOldSequences().remove(this);
// rest the primary map
((SDOChangeSummary)cs).getOriginalSequences().remove(this);
}
}
// restore any open content properties to the 3 data structures on SDODataObject, and remove from cs.unsetOCPropsMap
// see openContentPropertiesMap<String,Property>, openContentProperties(List<Property>). instanceProperties (List<Property>)
List oldUnsetOCList = ((SDOChangeSummary)cs).getUnsetOCProperties(this);
for (int i = 0, size = oldUnsetOCList.size(); i < size; i++) {
// it is essential that the oc property is removed from the cs or we will get an unsupported op during resetChanges()
// the property will be added only when logging=true, we reference the first item in the list as it reduces in size
addOpenContentProperty((Property)oldUnsetOCList.get(0));
}
// recursive step: swap valueStores on every contained subtree
for (Iterator iterProperties = getInstanceProperties().iterator();
iterProperties.hasNext();) {
SDOProperty property = (SDOProperty)iterProperties.next();
if (!property.getType().isChangeSummaryType()) {
Object value = get(property);
if ((null == value) && (null != getChangeSummary())) {
// no changes for null properties
} else {
if (property.isMany()) {
// check for modified ListWrapper
// assumption that we never have a null ListWrapper should still avoid a possible NPE
if (null != value) {
((ListWrapper)value).undoChanges(getChangeSummary());
if (!property.getType().isDataType()) {
for (Iterator iterMany = ((List)value).iterator();
iterMany.hasNext();) {
Object valueMany = iterMany.next();
if (null != valueMany) {// handle micro-corner case ListWrapper is null
((SDODataObject)valueMany).undoChanges(false, changeSummary, this, property.getName());
}
}
}
}
} else {
if (!property.getType().isDataType() && (null != value)) {
((SDODataObject)value).undoChanges(false, changeSummary, this, property.getName());
}
}
}
}
}
}
/**
* INTERNAL:
* Process ValueStore changes on any set/delete/detach/unset
* when logging is on.
*/
private void updateChangeSummaryWithOriginalValues() {
/**
* ValueStore processing:
* We switch the current VS to original VS.
* The current VS will be a shallow copy of the original for this dataobject at its first change
* If the VS is already copied don't reswap them again
*
* Case: Move/Reset
* a detach of a child modifies its parent - trigering a copy of parent's ValueStore
* a set on the parent will trigger another copy - but we will skip this one as we already
* have a copy of the original.
* Issue: if we reset the child back the same place (in effect doing our own undo)
* do we optimize this and remove the copy - for now a real undoChanges() will do the same.
*
*/
// dont store an original sequence if there are already is one in the map
if (isLogging() && (!getChangeSummary().isDirty(this)) && (!getChangeSummary().isCreated(this))) {
// dont copy containers of many props
// original valuestore becomes current one (from null)
getChangeSummary().getOriginalValueStores().put(this, _getCurrentValueStore());
// we make a shallow copy of the current valuestore arrays at this level in the tree
currentValueStore = _getCurrentValueStore().copy();
// handle Sequences only in UC2 where we have modified the container object - not when only the sequence is dirty
if (getType().isSequenced()) {
// deep copy the list of settings so a modification of the current sequence will not affect a setting in the originalSequences map
SDOSequence copySequence = getSequence().copy();
// store original sequence on ChangeSummary
getChangeSummary().getOriginalSequences().put(this, copySequence);
}
}
}
/**
* INTERNAL:
* Initialize all old settings related to ChangeSummary and recursively go down
* the tree to initialize all DataObjects rooted at this DataObject.
*/
public void resetChanges() {
// fill all oldSettings when logging is turned on
if ((container != null) && (containmentPropertyName != null) && (changeSummary != null)) {
changeSummary.setOldContainer(this, container);
changeSummary.setOldContainmentProperty(this, container.getInstanceProperty(containmentPropertyName));
}
// Note: valueStores will not be switched and originalValueStore will continue to be null until the first modification
// initialize empty list for current dataObject
for(int x=0, instancePropertiesSize=instanceProperties.size(); x<instancePropertiesSize; x++) {
SDOProperty property = instanceProperties.get(x);
Object value = get(property);
// #5878436 12-FEB-07 do not recurse into a non-containment relationship unless inside a dataGraph
if ((property.isContainment() || isContainedByDataGraph(property)) && //
!property.isMany() && (value != null) && !property.getType().isChangeSummaryType()) {
((SDODataObject)value).resetChanges();
} else {
// Handle isMany objects and store the oldCont* values for each item in the list
if (property.isMany()) {
if (null != value) {// secondary NPE check
List listValue = (List) value;
for(int y=0, listValueSize=listValue.size(); y<listValueSize; y++) {
Object valueMany = listValue.get(y);
// do not recurse into a non-containment relationship unless inside a dataGraph
if ((property.isContainment() || isContainedByDataGraph(property)) && //
(valueMany != null) && !property.getType().isChangeSummaryType()) {
((SDODataObject)valueMany).resetChanges();
}
}
}
}
}
}
}
/**
* INTERNAL:
* @param aType
*/
public void _setType(Type aType) {
type = (SDOType) aType;
if (getInstanceProperties().isEmpty()) {
if (type != null) {
getInstanceProperties().addAll(type.getProperties());
}
getInstanceProperties().addAll(_getOpenContentProperties());
}
if (type != null) {
_getCurrentValueStore().initialize(this);
// sequence is not initialized unless type.isSequenced=true, null otherwise
if (type.isSequenced()) {
sequence = new SDOSequence(this);
}
Property csmProperty = type.getChangeSummaryProperty();
if (csmProperty != null) {
SDOChangeSummary aChangeSummary = new SDOChangeSummary(this, aHelperContext);
aChangeSummary.endLogging();
_setChangeSummary(aChangeSummary);
}
}
}
/**
* INTERNAL:
* Pluggable Interface for substituting the default Map with a custom Map Class
* @param aValueStore
*/
public void _setCurrentValueStore(ValueStore aValueStore) {
currentValueStore = aValueStore;
}
/**
* INTERNAL:
* Map interface into the currentValueStore of this DataObject.<br>
* Note: Implementers of the {@link ValueStore ValueStore} interface require this accessor.
* @return
*/
public ValueStore _getCurrentValueStore() {
// Note: do not remove this function as it is the accessor into the pluggable ValueStore
// Notification of any change to this API is required
return currentValueStore;
}
/**
* INTERNAL:
* Sets the DataObject which contains this DataObject.
* @param aContainer the DataObject which is the container of this DataObject.
*/
public void _setContainer(DataObject aContainer) {
container = (SDODataObject) aContainer;
}
public Map _getOpenContentPropertiesAttributesMap() {
Map openContentPropertiesAttrs = new HashMap();
for (int i = 0, size = _getOpenContentPropertiesAttributes().size(); i < size; i++) {
Property next = (Property)_getOpenContentPropertiesAttributes().get(i);
//if uri is null then attributes won't be prefix qualified
QName qname = new QName(((SDOProperty)next).getUri(), next.getName());
openContentPropertiesAttrs.put(qname, get(next));
}
return openContentPropertiesAttrs;
}
public void _setOpenContentPropertiesAttributesMap(Map openAttributeProperties) {
Iterator iter = openAttributeProperties.keySet().iterator();
while (iter.hasNext()) {
QName nextKey = (QName)iter.next();
if ((!nextKey.getNamespaceURI().equals(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) && (!((nextKey.getNamespaceURI().equals(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI)) && nextKey.getLocalPart().equals(XMLConstants.SCHEMA_TYPE_ATTRIBUTE)))) {
Object value = openAttributeProperties.get(nextKey);
SDOProperty prop = (SDOProperty)aHelperContext.getXSDHelper().getGlobalProperty(nextKey.getNamespaceURI(), nextKey.getLocalPart(), false);
if (prop == null) {
DataObject propDo = aHelperContext.getDataFactory().create(SDOConstants.SDO_URL, SDOConstants.PROPERTY);
propDo.set("name", nextKey.getLocalPart());
propDo.set("type", SDOConstants.SDO_STRING);
propDo.set("many", false);
prop = (SDOProperty)aHelperContext.getTypeHelper().defineOpenContentProperty(null, propDo);
prop.setInstanceProperty(SDOConstants.XMLELEMENT_PROPERTY, Boolean.FALSE);
prop.setUri(nextKey.getNamespaceURI());
set(prop, value);
} else {
set(prop, value);
}
}
}
}
/**
* INTERNAL:
* This function is implemented by SDOType.setOpen() in a mapping setGet/SetMethodName call
* @param openContentPropertiesWithXMLRoots
*/
public void _setOpenContentPropertiesWithXMLRoots(List openContentPropertiesWithXMLRoots) {
for (int i = 0, size = openContentPropertiesWithXMLRoots.size(); i < size; i++) {
Object next = openContentPropertiesWithXMLRoots.get(i);
String propertyName = null;
String propertyUri = null;
Object value = null;
Type theType = null;
if (next instanceof XMLRoot) {
XMLRoot nextXMLRoot = (XMLRoot)next;
value = nextXMLRoot.getObject();
propertyName = nextXMLRoot.getLocalName();
propertyUri = nextXMLRoot.getNamespaceURI();
if (value instanceof DataObject) {
theType = ((DataObject)value).getType();
} else {
theType = ((SDOTypeHelper)aHelperContext.getTypeHelper()).getTypeForSimpleJavaType(value.getClass());
}
} else if (next instanceof DataObject) {
value = next;
String qualifiedName = ((SDOType)((DataObject)next).getType()).getXmlDescriptor().getDefaultRootElement();
int colonIndex = qualifiedName.indexOf(':');
if (colonIndex > -1) {
String prefix = qualifiedName.substring(0, colonIndex);
if ((prefix != null) && !prefix.equals("")) {
propertyUri = ((SDOType)((DataObject)next).getType()).getXmlDescriptor().getNonNullNamespaceResolver().resolveNamespacePrefix(prefix);
}
propertyName = qualifiedName.substring(colonIndex + 1, qualifiedName.length());
} else {
propertyName = qualifiedName;
}
theType = ((DataObject)next).getType();
}
else{
theType = ((SDOTypeHelper)aHelperContext.getTypeHelper()).getTypeForSimpleJavaType(next.getClass());
}
if (propertyName != null) {
SDOProperty prop = (SDOProperty)aHelperContext.getXSDHelper().getGlobalProperty(propertyUri, propertyName, true);
if (prop == null) {
prop = getInstanceProperty(propertyName);
if(prop != null){
if(prop.getUri() == null && propertyUri != null){
prop = createNewProperty(propertyName,propertyUri, theType);
}else{
if(prop.getUri() != null){
if(propertyUri == null){
prop = createNewProperty(propertyName,propertyUri, theType);
}else if(!prop.getUri().equals(propertyUri)){
prop = createNewProperty(propertyName,propertyUri, theType);
}
}
}
}else{
prop = createNewProperty(propertyName,propertyUri, theType);
}
}
if (prop.isMany()) {
((ListWrapper)getList(prop)).add(value, false);
} else {
set(prop, value, false);
}
}
}
}
private SDOProperty createNewProperty(String propertyName,String propertyUri, Type theType){
DataObject propDo = aHelperContext.getDataFactory().create(SDOConstants.SDO_URL, SDOConstants.PROPERTY);
propDo.set("name", propertyName);
propDo.set("type", theType);
propDo.set("many", true);
SDOProperty prop = (SDOProperty)aHelperContext.getTypeHelper().defineOpenContentProperty(null, propDo);
prop.setUri(propertyUri);
return prop;
}
/**
* INTERNAL:
* This function is implemented by SDOType.setOpen() in a mapping setGet/SetMethodName call
* @return
*/
public List _getOpenContentPropertiesWithXMLRoots() {
List returnList = new ArrayList();
for (int i = 0, size = openContentProperties.size(); i < size; i++) {
SDOProperty next = (SDOProperty) openContentProperties.get(i);
XMLRoot root = new XMLRoot();
String localName = next.getXPath();
if (next.getType() != null) {
if (!next.getType().isDataType()) {
String uri = next.getUri();
root.setNamespaceURI(uri);
} else {
String uri = next.getUri();
root.setNamespaceURI(uri);
}
}
root.setLocalName(localName);
Object value = get(next);
if (next.isMany()) {
for (int j = 0, sizel = ((List)value).size(); j < sizel; j++) {
XMLRoot nextRoot = new XMLRoot();
nextRoot.setNamespaceURI(root.getNamespaceURI());
nextRoot.setLocalName(root.getLocalName());
Object nextItem = ((List)value).get(j);
if ((next.getType() != null) && (next.getType().getXmlDescriptor() == null)) {
nextItem = XMLConversionManager.getDefaultXMLManager().convertObject(nextItem, String.class);
}
nextRoot.setObject(nextItem);
returnList.add(nextRoot);
}
} else {
if ((next.getType() != null) && (next.getType().getXmlDescriptor() == null)) {
value = XMLConversionManager.getDefaultXMLManager().convertObject(value, String.class);
}
root.setObject(value);
returnList.add(root);
}
}
return returnList;
}
/**
* INTERNAL:
* Returns a list of the Properties currently used in this DataObject which are not
* included in getType().getProperties
* @return the List of open content Properties currently used in this DataObject.
*/
public List _getOpenContentProperties() {
if (openContentProperties == null) {
openContentProperties = new ArrayList();
}
return openContentProperties;
}
private Map<String, Property> _getOpenContentAliasNamesMap() {
if (openContentAliasNames == null) {
openContentAliasNames = new HashMap();
}
return openContentAliasNames;
}
/**
* INTERNAL:
*/
private void convertValueAndSet(Property property, Object originalValue) {
Object convertedValue;
if (property.isMany()) {
if (originalValue == null) {
convertedValue = new ArrayList();
} else if (originalValue instanceof List) {
convertedValue = aHelperContext.getDataHelper().convert(property, originalValue);
} else {
List l = new ArrayList();
l.add(originalValue);
convertedValue = aHelperContext.getDataHelper().convert(property, l);
}
} else {
convertedValue = aHelperContext.getDataHelper().convert(property, originalValue);
}
set(property, convertedValue);
}
/**
* INTERNAL:
*/
private void convertValueAndSet(int propertyIndex, Object originalValue) {
Property property = getInstanceProperty(propertyIndex);
Object convertedValue = aHelperContext.getDataHelper().convert(property, originalValue);
set(property, convertedValue);
}
/**
* INTERNAL:
*/
private void convertValueAndSet(String path, Object originalValue) {
XPathEngine.getInstance().set(path, originalValue, this, true);
}
/**
* INTERNAL:
*
* @param property
* @param cls
* @return
* @throws ClassCastException
* @throws IllegalArgumentException
*/
public Object convertObjectToValue(Property property, Class cls) throws ClassCastException, IllegalArgumentException {
//ie get String - convert(thisprop, String.class)
if (null == property) {
throw new IllegalArgumentException(SDOException.cannotPerformOperationOnNullArgument("convertObjectToValue"));
}
Object obj = unwrapListValue(property, get(property));
try {
return ((SDODataHelper)aHelperContext.getDataHelper()).convertValueToClass(property, obj, cls);
} catch (Exception e) {
throw new ClassCastException("An error occurred during converison or an unsupported conversion was attempted.");
}
}
/**
* INTERNAL:
*
* @param property
* @param position
* @param cls
* @return
* @throws ClassCastException
* @throws IllegalArgumentException
*/
public Object convertObjectToValue(Property property, int position, Class cls) throws ClassCastException, IllegalArgumentException {
if (null == property) {
throw new IllegalArgumentException(SDOException.cannotPerformOperationOnNullArgument("convertObjectToValue"));
}
if ((cls == ClassConstants.List_Class) && !property.isMany()) {
throw new ClassCastException("can not call getList for a property that has isMany false.");
}
Object obj;
if (position == -1) {
if (cls == ClassConstants.List_Class) {
obj = get(property);
} else {
obj = unwrapListValue(property, get(property));
}
} else {
obj = getList(property).get(position);
}
try {
return ((SDODataHelper)aHelperContext.getDataHelper()).convertValueToClass(property, obj, cls);
} catch (Exception e) {
throw new ClassCastException("An error occurred during conversion or an unsupported conversion was attempted.");
}
}
/**
* INTERNAL:
* Sets the name of the property on the containing DataObject which contains this DataObject as a value.
* @param aContainmentPropertyName the name of the property on the containing DataObject which has this DataObject as a value.
*/
public void _setContainmentPropertyName(String aContainmentPropertyName) {
containmentPropertyName = aContainmentPropertyName;
}
/**
* INTERNAL:
* Return the name of the Property of the DataObject containing this data object
* or null if there is no container.
* @return the property containing this data object.
*/
public String _getContainmentPropertyName() {
return containmentPropertyName;
}
/**
* INTERNAL:
*
* @param value
* @return
*/
public boolean parentContains(Object value) {
if ((value == null) || (!(value instanceof DataObject))) {
return false;
}
if ((getContainer() != null) && (getContainmentProperty() != null)) {
if (value.equals(getContainer())) {
return true;
}
return getContainer().parentContains(value);
}
return false;
}
/**
* INTERNAL:
* Return whether the current dataObject(this) is part of a dataGraph.
* Typically this is used to determine whether to treat !isContainment dataObjects as containment while inside a dataGraph.
* @param aProperty
* @return
*/
private boolean isContainedByDataGraph(Property aProperty) {
// The property is of type DataObject that points to a DataGraph
return isContainedByDataGraph(getDataGraph(), (SDOProperty) aProperty);
}
/**
* INTERNAL:
* Return whether the current dataObject(this) is was part of the passed in dataGraph.
* Typically this is used to determine whether to treat !isContainment dataObjects as containment while inside a dataGraph.
* The dataGraph input is used when the current DataGraph pointer has already been cleared
* @param aDataGraph
* @param aProperty
* @return
*/
private boolean isContainedByDataGraph(DataGraph aDataGraph, SDOProperty aProperty) {
// The property is of type DataObject that points to a DataGraph
return (null != aDataGraph) && (null != aProperty.getType()) && !aProperty.getType().isDataType();
}
/**
* INTERNAL:
* Update containment with flagged update sequence state
* @param property
* @param values
* @param updateSequence
*/
public void updateContainment(Property property, Collection values, boolean updateSequence) {
if (property.isContainment() || isContainedByDataGraph(property)) {//
Object[] valuesArray = values.toArray();
for(int i=0; i<valuesArray.length; i++){
Object next = valuesArray[i];
if (next instanceof SDODataObject) {
updateContainment(property, (SDODataObject)next, updateSequence);
}
}
}
}
/**
* INTERNAL:
* Update containment on the specified collection of values and default to true = update the sequence
* @param property
* @param values
*/
public void updateContainment(Property property, Collection values) {
updateContainment(property, values, true);
}
/**
* INTERNAL:
* Update containment on the dataObject with specified update sequence state
* @param property
* @param aDataObject
* @param updateSequence
*/
public void updateContainment(Property property, SDODataObject aDataObject, boolean updateSequence) {
if (property.isContainment() || isContainedByDataGraph(property)) {
boolean wasInNewCS = (getChangeSummary() != null) && //
(aDataObject.getChangeSummary() != null) && getChangeSummary().equals(aDataObject.getChangeSummary());
// Remove property or old changeSummary
if(aDataObject.getContainer() != null) {
aDataObject.detach(false, updateSequence);
}
// Need to know if the DO was deleted and being re-added later on in this method.
// Since getChangeSummary().isDeleted() will be affected before the code in question
// is hit, store the value now.
boolean isDeleted = false;
if (getChangeSummary() != null) {
isDeleted = getChangeSummary().isDeleted(aDataObject);
}
// Check that the target object is not a changeSummary root - an internal set of an internal changeSummary
/**
* The following 2 update functions are kept separate for performance reasons in #6473342..
* Alternate implementations could have used a Command pattern object or boolean states to enable a
* single function to perform multiple operations.
*
* The DataGraph pointer on all subtrees are always updated regardless of the dataGraph value.
* However, the ChangeSummary pointer is only updated if the dataObject(value) being updated is not
* itself an internal changeSummary root.
* For example: the following would not update the CS to null when we get to dataObject B - as it has its own changeSummary-B
* root
* -> B
* -> CS-B
* -> D (String)
* But, the following would update the CS to CS-root when we get to dataObject B because the changeSummary root is above it
* root
* -> B
* -> D (String)
* -> CS-root
*/
if ((getChangeSummary() != null) && (aDataObject.getType() != null) && //
(aDataObject.getType().getChangeSummaryProperty() == null)) {
// Recursively set the current changeSummary and dataGraph - the CS root is above or absent
aDataObject.updateChangeSummaryAndDataGraph(getChangeSummary(), getDataGraph());
} else {
// Recursively set the dataGraph when this level (value) is the CS root n the subtree
if(aDataObject.getDataGraph() != getDataGraph()) {
aDataObject.updateDataGraph(getDataGraph());
}
}
// add value as a property of (this)
aDataObject._setContainer(this);
aDataObject._setContainmentPropertyName(property.getName());
// We don't setCreated for objects that were previously deleted
if (!wasInNewCS && (getChangeSummary() != null) && !getChangeSummary().isDeleted(aDataObject)) {
aDataObject._setCreated(true);
}
// If we are adding a previously deleted object, we must cancel out the isDeleted with an isCreated
// so we end up with all isDeleted, isModified == false
if ((getChangeSummary() != null) && getChangeSummary().isDeleted(aDataObject)) {
// explicitly clear the oldSetting and clear the key:value pair in the deleted map
aDataObject._setDeleted(false);
// remove oldSetting from map only when we return to original position
// by comparing an undo() with the projected model after set() - for now keep oldSettings
}
// modify container object
_setModified(true);
// In the case of re-adding a previously deleted/detached data object, we may need to update
// the change summary's OriginalValueStores list to ensure isModified correctness
if (isDeleted) {
// May need to remove the corresponding entry in the originalValueStore map.
// Compare the value store to the corresponding entry in the originalValueStore map.
// Check to see if we're adding into the old parent (undo delete) or into a new parent (move)
Map originalValueStores = getChangeSummary().getOriginalValueStores();
ValueStore originalVS = ((ValueStore) originalValueStores.get(aDataObject));
if (originalVS.equals(aDataObject._getCurrentValueStore())) {
// Remove the old value store from the change summary
originalValueStores.remove(aDataObject);
}
DataObject oldParentDO = getChangeSummary().getOldContainer(aDataObject);
if (oldParentDO == this) {
// The data object was deleted and is now being re-added to same parent object.
// May need to remove the corresponding entry in the oldValueStore map.
// Compare the parent value store to the corresponding entry in the oldValueStore map.
ValueStore originalParentVS = ((ValueStore) originalValueStores.get(oldParentDO));
if (originalParentVS.equals(this._getCurrentValueStore())) {
// Value stores are equal, now make sure no ListWrappers have been modified
// For each typePropertyValue that is a ListWrapper in the currentValueStore, check
// the OriginalElements list in the CS to see if that wrapper exists in the list (indicating
// the wrapper's list was modified at some point) and if it does, compare the current list
// to that original list. If they are equal, and the value stores are equal, the entries
// can be removed from the change summary so isModified will work correctly
Object prop;
Object oldList;
Map originalElements = getChangeSummary().getOriginalElements();
List oldParentProps = oldParentDO.getInstanceProperties();
for (int i=0; i<oldParentProps.size(); i++) {
prop = originalParentVS.getDeclaredProperty(i);
oldList = originalElements.get(prop);
if (oldList != null) {
List oldElements = (List) oldList;
List currentElements = ((ListWrapper) prop).getCurrentElements();
if (oldElements.size() != currentElements.size()) {
// A ListWrapper has been modified - don't remove the old value store entry
return;
}
Iterator elementIt = currentElements.iterator();
Iterator oldelementIt = oldElements.iterator();
while (elementIt.hasNext()) {
if (elementIt.next() != oldelementIt.next()) {
// A ListWrapper has been modified - don't remove the old value store entry
return;
}
}
}
}
// Remove the old value store from the change summary
originalValueStores.remove(oldParentDO);
}
}
}
}
}
/**
* INTERNAL:
* update containment and the update the sequence value by default.
* @param property
* @param value
*/
public void updateContainment(Property property, SDODataObject value) {
updateContainment(property, value, true);
}
/**
* INTERNAL:
* Return the changeSummary logging state
* @return
*/
private boolean isLogging() {
return ((changeSummary != null) && changeSummary.isLogging());
}
/**
* INTERNAL:
* Defined in SDO 2.01 spec on page 65 Externalizable function is called by
* ObjectStream.writeObject() A replacement object for serialization can be
* called here.
* <p>Security Note:
* This public function exposes a data replacement vulnerability where an outside client
* can gain access and modify their non-final constants.
* We may need to wrap the GZIP streams in some sort of encryption when we are not
* using HTTPS or SSL/TLS on the wire.
*
* @see org.eclipse.persistence.sdo.SDOResolvable
*/
public Object writeReplace() {
// JIRA129: pass the helperContext to the constructor to enable non-static contexts
return new SDOExternalizableDelegator(this, aHelperContext);
}
/**
* INTERNAL:
* Return the SDO Path from the root to the current internal node
*
* Prereq: We know that the targetObject will always have a parent as called
* from getPath()
*
* Matching conditions:
* Iterate up the tree
* return a non-null string for the XPath at when we reach the root 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 targetObject
* @param aSeparator (XPath separator is written only between elements not for the first call)
* @return String (representing the XPath)
*/
private String getPathPrivate(String currentPath,//
SDODataObject targetObject,//
String aSeparator) {
// Base Case: check we are at the root (get parent first)
SDODataObject aParent = targetObject.getContainer();
if (aParent == null) {
// 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
String aContainmentPropertyName = targetObject._getContainmentPropertyName();
SDOProperty aChild = aParent.getInstanceProperty(aContainmentPropertyName);
// Handle ListWrapper contained DataObjects
if (aChild.isMany()) {
int index = (aParent.getList(aChild)).indexOf(targetObject);
return getPathPrivate(aContainmentPropertyName +//
SDOConstants.SDO_XPATH_LIST_INDEX_OPEN_BRACKET +//
(1 + index) +// [indexes] start at 1
SDOConstants.SDO_XPATH_LIST_INDEX_CLOSE_BRACKET +//
aSeparator +//
currentPath,//
aParent,//
SDOConstants.SDO_XPATH_SEPARATOR_FRAGMENT);
}
// recursive call to non ListWrappers
return getPathPrivate(aContainmentPropertyName +//
aSeparator +//
currentPath,//
aParent,//
SDOConstants.SDO_XPATH_SEPARATOR_FRAGMENT);
}
/**
* INTERNAL:
* Return an SDO Path string from root of the caller to itself
* @return String
*/
public String _getPath() {
// Base Case: The internal node is actually the root
if (getContainer() == null) {
return SDOConstants.SDO_XPATH_TO_ROOT;
} else {
// Recursive Case: call private recursive reverse O(logn) traversal
// function on root object
return getPathPrivate(SDOConstants.EMPTY_STRING,//
this,//
SDOConstants.EMPTY_STRING);
}
}
/**
* INTERNAL:
*
* @return
*/
private SDOProperty[] getInstancePropertiesArray() {
if ((openContentProperties == null) || openContentProperties.isEmpty()) {
return getType().getPropertiesArray();
}
SDOProperty[] props = getType().getPropertiesArray();
SDOProperty[] ret = new SDOProperty[openContentProperties.size() + props.length];
for (int i = 0; i < props.length; i++) {
ret[i] = props[i];
}
for (int i = props.length; i < ret.length; i++) {
ret[i] = (SDOProperty)openContentProperties.get(i - props.length);
}
return ret;
}
/**
* INTERNAL:
* Get the value of the property (open-content or declared)..
* @param property
* @return
*/
public Object getPropertyInternal(Property property) {
int index = ((SDOProperty)property).getIndexInType();
if (index == -1) {
return _getCurrentValueStore().getOpenContentProperty(property);
} else {
return _getCurrentValueStore().getDeclaredProperty(index);
}
}
/**
* INTERNAL:
* Update the ValueStore with the new property value and update any sequence if it exists.
* @param property
* @param value
* @param updateSequence (truncate call back from sequence when this function was called from sequence)
*/
public void setPropertyInternal(SDOProperty property, Object value, boolean updateSequence) {
/*
* Update sequence object if element and without invoking a back pointer from sequence.add() to this dataObject.
* The sequence != null required in case where non-public type.setSequenced(true) called after type define.
* Assume: aHelperContext is assumed not to be null.
* ChangeSummaries are not part of sequences.
*
* See JIRA-242, bug#6031657: A modify operation will need logic to either
* 1 - update the existing setting in place
* 2 - add a new setting and remove the previous one - TBD
*
* The following code needs to check for !attribute instead of !element because
* we have 3 states( !attribute, !element and (!attribute && !element))
* - two of which are valid for sequence setting creation.
*/
Object origValue = getPropertyInternal(property);
if (type.isSequenced() && updateSequence//
&&!property.getType().isChangeSummaryType() && !aHelperContext.getXSDHelper().isAttribute(property)) {
// As we do for ListWrappers and DataObjects we will need to remove the previous setting if this set is actually a modify
// keep sequence code here for backdoor sets
if(property.isMany()) {
getSequence().addSettingWithoutModifyingDataObject(property, value);
} else {
if(isSet(property)) {
getSequence().updateSettingWithoutModifyingDataObject(property, get(property), value);
} else {
getSequence().addSettingWithoutModifyingDataObject(property, value);
}
}
}
int index = property.getIndexInType();
if (index == -1) {
_getCurrentValueStore().setOpenContentProperty(property, value);
} else {
_getCurrentValueStore().setDeclaredProperty(index, value);
SDOProperty oppositeProperty = property.getOpposite();
if(null != oppositeProperty) {
if(null != value) {
if(property.isMany()) {
List<SDODataObject> valueList = (List<SDODataObject>) value;
for(SDODataObject valueDO : valueList) {
if(this != valueDO.get(oppositeProperty)) {
valueDO.setInternal(oppositeProperty, this, true);
}
}
} else {
SDODataObject valueDO = (SDODataObject) value;
if(oppositeProperty.isMany()) {
List oppositeList = valueDO.getList(oppositeProperty);
if(!oppositeList.contains(this)) {
oppositeList.add(this);
}
} else {
if(this != valueDO.get(oppositeProperty)) {
valueDO.setInternal(oppositeProperty, this, true);
}
}
}
}
if(null != origValue) {
if(property.isMany()) {
List<SDODataObject> origValueList = (List<SDODataObject>) origValue;
for(SDODataObject origValueDO : origValueList) {
if(null != origValueDO.get(oppositeProperty)) {
origValueDO.set(oppositeProperty, null);
}
}
} else {
DataObject origValueDO = (DataObject) origValue;
if(oppositeProperty.isMany()) {
List oppositeList = origValueDO.getList(oppositeProperty);
if(oppositeList.contains(this)) {
oppositeList.remove(this);
}
} else {
if(null != origValueDO.get(oppositeProperty)) {
origValueDO.set(oppositeProperty, null);
}
}
}
}
}
}
}
/**
* INTERNAL:
* Add the open content property into all 3 data structures.
* Remove the property from the unset map.
* @param property
*/
public void addOpenContentProperty(Property property) {
List theList = null;
if (aHelperContext.getXSDHelper().isAttribute(property)) {
theList = _getOpenContentPropertiesAttributes();
}else {
theList = _getOpenContentProperties();
}
if (!theList.contains(property)) {
if (isLogging()) {
getChangeSummary().removeUnsetOCProperty(this, property);
}
theList.add(property);
getInstanceProperties().add(property);
if (((SDOProperty)property).hasAliasNames()) {
for (int i = 0, size = property.getAliasNames().size(); i < size; i++) {
_getOpenContentAliasNamesMap().put((String) property.getAliasNames().get(i), property);
}
}
}
}
/**
* INTERNAL:
* Remove the open content property (property) from all 3 data structures.
* Add the property to the unset map.
* We restore this OC property in undoChanges
* @param property
*/
public void removeOpenContentProperty(Property property) {
// save OC property in changeSummary for use during cs.copy and undoChanges
if (isLogging()) {
getChangeSummary().setUnsetOCProperty(this, property);
}
// remove oc property
_getOpenContentProperties().remove(property);
_getOpenContentPropertiesAttributes().remove(property);
getInstanceProperties().remove(property);
if (((SDOProperty)property).hasAliasNames()) {
for (int i = 0, size = property.getAliasNames().size(); i < size; i++) {
_getOpenContentAliasNamesMap().remove(property.getAliasNames().get(i));
}
}
}
/**
* INTERNAL:
* Return whether the property (open-content or declared) is set?
* @param property
* @return true if set, false otherwise
*/
public boolean isSetInternal(Property property) {
int index = ((SDOProperty)property).getIndexInType();
if (index == -1) {
return _getCurrentValueStore().isSetOpenContentProperty(property);
} else {
return _getCurrentValueStore().isSetDeclaredProperty(index);
}
}
/**
* INTERNAL:
* Unset the property on the ValueStore interface and update the sequence if it exists.
* Used by implementers that require
* @param property
* @param updateSequence
*/
private void unsetInternal(Property property, boolean updateSequence) {
if (property.isMany()) {
getList(property).clear();
if (property.isOpenContent()) {
removeOpenContentProperty(property);
} else {
_getCurrentValueStore().unsetDeclaredProperty(((SDOProperty)property).getIndexInType());
}
} else {
if (property.isOpenContent()) {
_getCurrentValueStore().unsetOpenContentProperty(property);
removeOpenContentProperty(property);
} else {
_getCurrentValueStore().unsetDeclaredProperty(((SDOProperty)property).getIndexInType());
}
}
/*
* Update sequence object if element and without invoking a back pointer from sequence.add() to this dataObject.
* The sequence != null required in case where non-public type.setSequenced(true) called after type define
*/
if (type.isSequenced() && (sequence != null) && updateSequence && aHelperContext.getXSDHelper().isElement(property)) {
// remove all instances of the property from the sequence
sequence.removeSettingWithoutModifyingDataObject(property);
}
}
/**
* INTERNAL:
* Return the sdoref attribute value during unmarshaling
* @return
*/
public String _getSdoRef() {
return sdoRef;
}
public void _setOpenContentPropertiesAttributes(List openContentPropertiesAttributes) {
this.openContentPropertiesAttributes = openContentPropertiesAttributes;
}
public List _getOpenContentPropertiesAttributes() {
if (openContentPropertiesAttributes == null) {
openContentPropertiesAttributes = new ArrayList();
}
return openContentPropertiesAttributes;
}
@Override
public List<Setting> getSettings() {
if(null != sequence) {
return getSequence().getSettings();
} else {
return null;
}
}
public void _setSdoRef(String newRef) {
sdoRef = newRef;
}
/**
* Convenience method that unwraps a list and returns the first element,
* if necessary.
*
* @param prop
* @param val
* @return
*/
private Object unwrapListValue(Property prop, Object val) {
if (prop == null || val == null) {
return null;
}
if (! (prop.isMany() && val instanceof ListWrapper) ) {
return val;
}
List l = (ListWrapper) val;
if (l.size() == 0) {
return null;
}
return l.get(0);
}
}