org.eclipse.persistence.sdo.SDOSequence Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.sdo;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import commonj.sdo.Property;
import commonj.sdo.Sequence;
import javax.xml.namespace.QName;
import org.eclipse.persistence.sdo.SDODataObject;
import org.eclipse.persistence.sdo.helper.ListWrapper;
import org.eclipse.persistence.sdo.helper.SDOTypeHelper;
import org.eclipse.persistence.core.mappings.CoreMapping;
import org.eclipse.persistence.exceptions.SDOException;
import org.eclipse.persistence.internal.oxm.XPathFragment;
import org.eclipse.persistence.oxm.NamespaceResolver;
import org.eclipse.persistence.oxm.XMLConstants;
import org.eclipse.persistence.oxm.XMLDescriptor;
import org.eclipse.persistence.oxm.XMLField;
import org.eclipse.persistence.oxm.XMLRoot;
import org.eclipse.persistence.oxm.mappings.XMLCollectionReferenceMapping;
import org.eclipse.persistence.oxm.mappings.XMLObjectReferenceMapping;
import org.eclipse.persistence.oxm.sequenced.Setting;
import org.eclipse.persistence.mappings.DatabaseMapping;
public class SDOSequence implements Sequence {
private static final String TEXT_XPATH = "text()";
private SDODataObject dataObject;
private List settings;
private Map valuesToSettings;
public SDOSequence(SDODataObject dataObject) {
// catch a null dataObject early before we get NPE on any update
// operations during add/remove
if (null == dataObject) {
throw SDOException.sequenceDataObjectInstanceFieldIsNull();
}
this.dataObject = dataObject;
this.settings = new ArrayList();
this.valuesToSettings = new HashMap();
}
public SDODataObject getDataObject() {
return dataObject;
}
public List getSettings() {
return settings;
}
protected Map getValuesToSettings() {
return valuesToSettings;
}
@Override
public void add(int index, int propertyIndex, Object value) {
Property property = dataObject.getInstanceProperty(propertyIndex);
add(index, property, value);
}
@Override
public boolean add(int propertyIndex, Object value) {
Property property = dataObject.getInstanceProperty(propertyIndex);
return add(property, value);
}
@Override
public void add(int index, Property property, Object value) {
if (!isAllowedInSequence(property)) {
return;
}
if (property != null && property.isOpenContent() && dataObject.getType().isOpen()) {
dataObject.addOpenContentProperty(property);
}
// Update the data object
if (property.isMany()) {
ListWrapper listWrapper = (ListWrapper) dataObject.getList(property);
if (value instanceof List) {
// iterate list
for (Iterator i = ((List) value).iterator(); i.hasNext();) {
// add a setting to the end of the sequence
Object aValue = i.next();
Setting setting = convertToSetting(property, aValue);
valuesToSettings.put(new Key(property, aValue), setting);
settings.add(index++, setting);
// no need to check updateContainment flag -
// ListWrapper.add() will not pass an entire List here
listWrapper.add(aValue, false);
}
} else {
// set individual list item
// add a setting to the end of the sequence
Setting setting = convertToSetting(property, value);
valuesToSettings.put(new Key(property, value), setting);
settings.add(index, setting);
int listIdx = getIndexInList(property, value);
if (listIdx != -1) {
listWrapper.add(listIdx, value, false);
} else {
listWrapper.add(value, false);
}
}
} else {
dataObject.setPropertyInternal((SDOProperty) property, value, false);
// Update the settings
Setting setting = convertToSetting(property, value);
valuesToSettings.put(new Key(property, value), setting);
settings.add(index, setting);
}
}
private boolean isAllowedInSequence(Property property) {
// Disallow the addition of a null Property
if (null == property) {
return false;
}
// Disallow the addition of a read only Property
if (property.isReadOnly()) {
return false;
}
// Disallow the addition of a Properties representing an XML attribute
if (dataObject.getType().getHelperContext().getXSDHelper().isAttribute(property)) {
throw SDOException.sequenceAttributePropertyNotSupported(property.getName());
}
// Disallow an open Property on a closed Type
if (property.isOpenContent() && !dataObject.getType().isOpen()) {
return false;
}
// Disallow the addition of an isMany==false Property that is already
// set
if (property.isMany()) {
return true;
}
if (dataObject.isSet(property)) {
throw SDOException.sequenceDuplicateSettingNotSupportedForComplexSingleObject(getIndexForProperty(property), property.getName());
}
return true;
}
@Override
public void add(int index, String propertyName, Object value) {
Property property = dataObject.getInstanceProperty(propertyName);
if (property == null) {
// Property with given name does not exist - create an open content
// property
property = dataObject.defineOpenContentProperty(propertyName, value);
((SDOProperty) property).setMany(true);
}
add(index, property, value);
}
@Override
public void add(int index, String text) {
addText(index, text);
}
@Override
public boolean add(Property property, Object value) {
if (addSettingWithoutModifyingDataObject(property, value)) {
if (property != null && property.isOpenContent() && dataObject.getType().isOpen()) {
dataObject.addOpenContentProperty(property);
}
if (value instanceof XMLRoot) {
value = ((XMLRoot) value).getObject();
}
if (property.isMany()) {
ListWrapper listWrapper = (ListWrapper) dataObject.getList(property);
listWrapper.add(value, false);
} else {
dataObject.setPropertyInternal((SDOProperty) property, value, false);
}
return true;
}
return false;
}
@Override
public boolean add(String propertyName, Object value) {
Property property = dataObject.getInstanceProperty(propertyName);
if (property == null) {
// Property with given name does not exist - create an open content
// property
property = dataObject.defineOpenContentProperty(propertyName, value);
((SDOProperty) property).setMany(true);
}
return add(property, value);
}
@Override
public void add(String text) {
addText(text);
}
@Override
public void addText(int index, String text) {
// Trigger store of original sequence
dataObject._setModified(true);
Setting textSetting = new Setting(null, TEXT_XPATH);
textSetting.setObject(dataObject);
textSetting.setValue(text, false);
settings.add(index, textSetting);
}
@Override
public void addText(String text) {
// Trigger store of original sequence
dataObject._setModified(true);
Setting textSetting = new Setting(null, TEXT_XPATH);
textSetting.setObject(dataObject);
textSetting.setValue(text, false);
settings.add(textSetting);
}
@Override
public SDOProperty getProperty(int index) {
try {
return getProperty(settings.get(index));
} catch (IndexOutOfBoundsException iobex) {
throw SDOException.invalidIndex(iobex, index);
}
}
public SDOProperty getProperty(Setting setting) {
CoreMapping mapping = setting.getMapping();
if (null == mapping) {
List children = setting.getChildren();
if (null != children && children.size() > 0) {
return getProperty(children.get(0));
}
} else {
SDOProperty property = null;
if (null == setting.getName()) {
Object value = setting.getValue();
if (value instanceof SDODataObject) {
SDOProperty containmentProp = ((SDODataObject) value).getContainmentProperty();
if (containmentProp != null) {
property = dataObject.getInstanceProperty(containmentProp.getName());
}
if (property == null) {
XMLDescriptor desc = ((SDODataObject) value).getType().getXmlDescriptor();
String qualifiedName = desc.getDefaultRootElement();
int index = qualifiedName.indexOf(':');
String localName = null;
if (index > -1) {
localName = qualifiedName.substring(index + 1, qualifiedName.length());
} else {
localName = qualifiedName;
}
property = dataObject.getInstanceProperty(localName);
}
} else {
XMLRoot xmlRoot = (XMLRoot) value;
if (null != xmlRoot) {
property = dataObject.getInstanceProperty(xmlRoot.getLocalName());
}
}
} else {
property = dataObject.getInstanceProperty(mapping.getAttributeName());
}
return property;
}
return null;
}
@Override
public Object getValue(int index) {
try {
return getValue(settings.get(index));
} catch (IndexOutOfBoundsException iobex) {
throw SDOException.invalidIndex(iobex, index);
}
}
private Object getValue(Setting setting) {
if (null != setting.getMapping() || (setting.getName() != null && setting.getName().equals(TEXT_XPATH))) {
Object value = setting.getValue();
if (value instanceof XMLRoot) {
value = ((XMLRoot) value).getObject();
}
return value;
}
if (null == setting.getChildren() || setting.getChildren().size() == 0) {
return null;
}
return getValue(setting.getChildren().get(0));
}
@Override
public void move(int toIndex, int fromIndex) {
if (toIndex == fromIndex) {
return;
}
// verify indexes are in range
int size = settings.size();
if (fromIndex < 0 || fromIndex >= size) {
throw SDOException.invalidIndex(null, fromIndex);
}
if (toIndex < 0 || toIndex >= size) {
throw SDOException.invalidIndex(null, toIndex);
}
// trigger deep copy of sequence (if applicable)
dataObject._setModified(true);
Setting setting = settings.remove(fromIndex);
settings.add(toIndex, setting);
SDOProperty prop = getProperty(setting);
if (prop != null && prop.isMany()) {
ListWrapper lw = (ListWrapper) dataObject.getList(prop);
Object value = getValue(setting);
int currentIndexInLw = lw.indexOf(value);
lw.remove(currentIndexInLw, false);
int newIndexInLw = getIndexInList(prop, value);
lw.add(newIndexInLw, value, false);
}
}
@Override
public void remove(int index) {
Setting setting = settings.get(index);
remove(setting);
settings.remove(setting);
}
private void remove(Setting setting) {
CoreMapping mapping = setting.getMapping();
if (null != mapping) {
Property property = null;
if (null == setting.getName()) {
XMLRoot xmlRoot = (XMLRoot) setting.getValue();
if (null != xmlRoot) {
property = dataObject.getInstanceProperty(xmlRoot.getLocalName());
valuesToSettings.remove(new Key(property, setting.getValue()));
}
} else {
property = dataObject.getInstanceProperty(mapping.getAttributeName());
valuesToSettings.remove(new Key(property, setting.getValue()));
}
if (property.isMany()) {
ListWrapper listWrapper = (ListWrapper) dataObject.getList(property);
listWrapper.remove(setting.getValue(), false, false);
} else {
dataObject.unset(property, false, false);
}
} else if (setting.getName() != null && setting.getName().equals(TEXT_XPATH)) {
// Handle "text()"
dataObject._setModified(true);
}
List children = setting.getChildren();
if (null != children) {
int childrenSize = children.size();
for (int x = 0; x < childrenSize; x++) {
remove(children.get(x));
}
}
}
/**
* INTERNAL:
*
* @param setting
*/
public void addValueToSettings(Setting setting) {
valuesToSettings.put(new Key(getProperty(setting), getValue(setting)), setting);
}
/**
* INTERNAL:
*
* @param setting
*/
public void removeValueToSettings(Setting setting) {
valuesToSettings.remove(new Key(getProperty(setting), getValue(setting)));
}
@Override
public Object setValue(int index, Object value) {
return setValue(settings.get(index), value);
}
private Object setValue(Setting setting, Object value) {
if (null == setting.getMapping()) {
if (setting.getName() != null && setting.getName().equals(TEXT_XPATH)) {
dataObject._setModified(true);
Object oldValue = setting.getValue();
setting.setValue(value, false);
return oldValue;
}
List children = setting.getChildren();
if (null != children && children.size() > 0) {
return setValue(children.get(0), value);
}
return null;
}
SDOProperty property = getProperty(setting);
Object oldValue = setting.getValue();
if (property.isMany()) {
ListWrapper listValue = (ListWrapper) dataObject.getList(property);
int valueIndex = listValue.indexOf(oldValue);
listValue.remove(oldValue, property.isContainment(), false);
listValue.add(valueIndex, value, false);
setting.setValue(value, false);
} else {
if (dataObject.isSet(property)) {
updateSettingWithoutModifyingDataObject(property, dataObject.get(property), value);
setting.setValue(value);
} else {
addSettingWithoutModifyingDataObject(property, value);
}
dataObject.setPropertyInternal(property, value, false);
}
return oldValue;
}
@Override
public int size() {
return settings.size();
}
private Setting convertToSetting(Property property, Object value) {
SDOProperty sdoProperty = (SDOProperty) property;
DatabaseMapping mapping = sdoProperty.getXmlMapping();
Setting setting = new Setting();
SDOType sdoType = dataObject.getType();
XMLDescriptor xmlDescriptor = sdoType.getXmlDescriptor();
if (null == mapping) {
setting.setObject(dataObject);
mapping = xmlDescriptor.getMappingForAttributeName("openContentProperties");
setting.setMapping(mapping);
XMLRoot xmlRoot = new XMLRoot();
if (value instanceof XMLRoot) {
xmlRoot.setLocalName(((XMLRoot) value).getLocalName());
xmlRoot.setNamespaceURI(((XMLRoot) value).getNamespaceURI());
xmlRoot.setObject(((XMLRoot) value).getObject());
} else {
xmlRoot.setLocalName(sdoProperty.getName());
xmlRoot.setNamespaceURI(sdoProperty.getUri());
xmlRoot.setObject(value);
// do not set schema type for global properties
SDOTypeHelper hlpr = (SDOTypeHelper) dataObject.getType().getHelperContext().getTypeHelper();
if (hlpr.getOpenContentProperty(sdoProperty.getUri(), sdoProperty.getName()) == null) {
QName schemaTypeQName = hlpr.getXSDTypeFromSDOType(property.getType());
if (schemaTypeQName != null && schemaTypeQName != XMLConstants.STRING_QNAME) {
xmlRoot.setSchemaType(schemaTypeQName);
}
}
}
setting.setValue(xmlRoot, false);
} else {
setting = convertToSetting(mapping, value);
}
return setting;
}
private Setting convertToSetting(DatabaseMapping mapping, Object value) {
XMLDescriptor xmlDescriptor = (XMLDescriptor) mapping.getDescriptor();
NamespaceResolver nsResolver = xmlDescriptor.getNamespaceResolver();
Setting rootSetting = new Setting();
XMLField xmlField = (XMLField) mapping.getField();
if (xmlField == null) {
if (mapping instanceof XMLObjectReferenceMapping) {
xmlField = (XMLField) ((XMLObjectReferenceMapping) mapping).getFields().get(0);
} else if (mapping instanceof XMLCollectionReferenceMapping) {
xmlField = (XMLField) ((XMLCollectionReferenceMapping) mapping).getFields().get(0);
}
}
Setting setting = rootSetting;
if (xmlField != null) {
XPathFragment xPathFragment = xmlField.getXPathFragment();
rootSetting = convertToSetting(xPathFragment, nsResolver);
setting = rootSetting;
while (xPathFragment.getNextFragment() != null) {
xPathFragment = xPathFragment.getNextFragment();
Setting childSetting = convertToSetting(xPathFragment, nsResolver);
setting.addChild(childSetting);
setting = childSetting;
}
}
setting.setObject(dataObject);
setting.setMapping(mapping);
setting.setValue(value, false);
return rootSetting;
}
private Setting convertToSetting(XPathFragment xPathFragment, NamespaceResolver nsResolver) {
Setting setting = new Setting();
String name = xPathFragment.getLocalName();
if (null == name) {
name = xPathFragment.getShortName();
}
setting.setName(name);
if (xPathFragment.hasNamespace()) {
setting.setNamespaceURI(nsResolver.resolveNamespacePrefix(xPathFragment.getPrefix()));
}
return setting;
}
public SDOSequence copy() {
SDOSequence copy = new SDOSequence(dataObject);
for (int index = 0, size = settings.size(); index < size; index++) {
Setting settingCopy = settings.get(index).copy();
copy.getSettings().add(settingCopy);
copy.getValuesToSettings().put(new Key(getProperty(settingCopy), getValue(settingCopy)), settingCopy);
}
return copy;
}
/**
* INTERNAL: Add a setting to the list at the specified index. The owning
* DataObject will not be made aware of this addition.
*
* @param index
* the index at which to add the new Setting in the Settings list
* @param property
* @param value
* @return true if the a Setting was successfully added to the list,
* otherwise false
*/
public boolean addSettingWithoutModifyingDataObject(int index, Property property, Object value) {
Setting setting = convertToSetting(property, value);
valuesToSettings.put(new Key(property, value), setting);
if (index >= 0) {
settings.add(index, setting);
} else {
settings.add(setting);
}
return true;
}
/**
* INTERNAL:
*/
public boolean addSettingWithoutModifyingDataObject(Property property, Object value) {
return addSettingWithoutModifyingDataObject(property, value, true);
}
/**
* INTERNAL:
*/
public boolean addSettingWithoutModifyingDataObject(Property property, Object value, boolean checkAllowed) {
if (checkAllowed && !isAllowedInSequence(property)) {
return false;
}
Setting setting = convertToSetting(property, value);
valuesToSettings.put(new Key(property, value), setting);
settings.add(setting);
return true;
}
/**
* INTERNAL:
*/
public void updateSettingWithoutModifyingDataObject(Property property, Object oldValue, Object newValue) {
Key key = new Key(property, oldValue);
Setting setting = valuesToSettings.get(key);
valuesToSettings.remove(key);
valuesToSettings.put(new Key(property, newValue), setting);
// set the new value on the appropriate setting
while (setting.getMapping() == null) {
List children = setting.getChildren();
if (children != null && children.size() > 0) {
setting = children.get(0);
}
}
setting.setValue(newValue, false);
}
/**
* INTERNAL:
*/
public void removeSettingWithoutModifyingDataObject(Property property, Object value) {
settings.remove(valuesToSettings.remove(new Key(property, value)));
}
/**
* INTERNAL:
*/
public void removeSettingWithoutModifyingDataObject(Property property) {
List keys = new ArrayList(valuesToSettings.keySet());
int size = valuesToSettings.keySet().size();
for (int i = size - 1; i >= 0; i--) {
Key nextKey = keys.get(i);
if (nextKey.getProperty() == property) {
settings.remove(valuesToSettings.remove(nextKey));
}
}
}
/**
* INTERNAL: Convenience method that returns the index of the Setting
* associated with a given property in the Settings list
*
* @param property
* @return index of the Setting associated with a given property in the
* Settings list or -1 if not found
*/
public int getIndexForProperty(Property property) {
List keys = new ArrayList(valuesToSettings.keySet());
for (int i = 0; i < keys.size(); i++) {
Key nextKey = keys.get(i);
if (nextKey.getProperty() == property) {
return settings.indexOf(valuesToSettings.get(nextKey));
}
}
return -1;
}
/**
* INTERNAL: Convenience method that, given a many property and a value,
* returns the associated Setting's index in the Settings list. For example,
* if a sequence contains many properties "letters" and "numbers", such as
* [A, 1, C, 2, B, D], and we are looking for the letter B, this method will
* return 2. Although B is at index 4 of the Settings list, it is at index 2
* of the list of "letters" - [A, C, B, D].
*
* @param property
* @return index of the value's Setting in the list relative to a given
* property or -1 if not found.
*/
private int getIndexInList(Property manyProp, Object value) {
int returnIndex = -1;
for (int i = 0; i < settings.size(); i++) {
Setting nextSetting = settings.get(i);
SDOProperty prop = getProperty(nextSetting);
if (prop.equals(manyProp)) {
returnIndex++;
if (value.equals(getValue(nextSetting))) {
return returnIndex;
}
}
}
return returnIndex;
}
/**
* INTERNAL: Get the root Setting for a given Setting.
*
* @param setting
* @return the root Setting or this Setting if it is a root
*/
public static Setting getRootSetting(Setting setting) {
Setting rootSetting = setting;
while (rootSetting.getParent() != null) {
rootSetting = rootSetting.getParent();
}
return rootSetting;
}
/**
* INTERNAL: Ensure that each Setting in the settings list is also present
* in the valuesToSettings map
*/
public void afterUnmarshal() {
for (Iterator setIt = getSettings().iterator(); setIt.hasNext();) {
addValueToSettings(setIt.next());
}
}
private static class Key {
private Property property;
private Object value;
public Key(Property property, Object value) {
this.property = property;
this.value = value;
}
protected Property getProperty() {
return this.property;
}
protected Object getValue() {
return this.value;
}
@Override
public boolean equals(Object object) {
try {
Key key = (Key) object;
return property == key.getProperty() && value == key.getValue();
} catch (ClassCastException e) {
return false;
}
}
@Override
public int hashCode() {
if (value == null) {
return 0;
}
return value.hashCode();
}
}
}