blob: e1b44fd58a55e9deffaa6e1e84e7d8742429acfa [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:
// bdoughan - Jan 27/2009 - 1.1 - Initial implementation
// bdoughan - Mar 31/2009 - 2.0 - Added ChangeSummary Support
package org.eclipse.persistence.sdo.helper.jaxb;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import org.eclipse.persistence.core.mappings.CoreAttributeAccessor;
import org.eclipse.persistence.exceptions.SDOException;
import org.eclipse.persistence.internal.core.helper.CoreField;
import org.eclipse.persistence.internal.core.queries.CoreContainerPolicy;
import org.eclipse.persistence.internal.oxm.MappingNodeValue;
import org.eclipse.persistence.internal.oxm.TreeObjectBuilder;
import org.eclipse.persistence.internal.oxm.XPathFragment;
import org.eclipse.persistence.internal.oxm.XPathNode;
import org.eclipse.persistence.internal.oxm.mappings.Mapping;
import org.eclipse.persistence.internal.queries.ContainerPolicy;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.jaxb.JAXBContext;
import org.eclipse.persistence.mappings.ContainerMapping;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.oxm.XMLDescriptor;
import org.eclipse.persistence.oxm.XMLField;
import org.eclipse.persistence.oxm.mappings.XMLCompositeCollectionMapping;
import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping;
import org.eclipse.persistence.oxm.mappings.XMLDirectMapping;
import org.eclipse.persistence.oxm.mappings.XMLInverseReferenceMapping;
import org.eclipse.persistence.oxm.mappings.XMLObjectReferenceMapping;
import org.eclipse.persistence.sdo.SDOChangeSummary;
import org.eclipse.persistence.sdo.SDODataObject;
import org.eclipse.persistence.sdo.SDOProperty;
import org.eclipse.persistence.sdo.SDOType;
import org.eclipse.persistence.sdo.ValueStore;
import org.eclipse.persistence.sdo.helper.ListWrapper;
import commonj.sdo.DataObject;
import commonj.sdo.Property;
/**
* The JAXBValueStore enables a DataObject to access data from a POJO.
* The link between an SDO property and a POJO property is through their
* XML representation. For the POJO property this corresponds to its
* JAXB mapping.
*/
public class JAXBValueStore implements ValueStore {
private JAXBHelperContext jaxbHelperContext;
private Object entity;
private XMLDescriptor descriptor;
private SDODataObject dataObject;
private Map<Property, JAXBListWrapper> listWrappers;
public JAXBValueStore(JAXBHelperContext aJAXBHelperContext, SDOType sdoType) {
this.jaxbHelperContext = aJAXBHelperContext;
this.listWrappers = new WeakHashMap<Property, JAXBListWrapper>();
this.descriptor = jaxbHelperContext.getObjectDescriptor(sdoType);
this.entity = this.descriptor.getInstantiationPolicy().buildNewInstance();
}
public JAXBValueStore(JAXBHelperContext aJAXBHelperContext, Object anEntity) {
this.jaxbHelperContext = aJAXBHelperContext;
this.listWrappers = new WeakHashMap<Property, JAXBListWrapper>();
JAXBContext jaxbContext = (JAXBContext) jaxbHelperContext.getJAXBContext();
this.descriptor = (XMLDescriptor) jaxbContext.getXMLContext().getSession(anEntity).getDescriptor(anEntity);
this.entity = anEntity;
}
private JAXBValueStore(JAXBHelperContext aJAXBHelperContext, Object anEntity, XMLDescriptor aDescriptor, SDODataObject aDataObject, Map<Property, JAXBListWrapper> aMap) {
this.jaxbHelperContext = aJAXBHelperContext;
this.entity = anEntity;
this.descriptor = aDescriptor;
this.dataObject = aDataObject;
this.listWrappers = aMap;
for(JAXBListWrapper jaxbListWrapper : listWrappers.values()) {
jaxbListWrapper.getCurrentElements().setValueStore(this);
}
}
/**
* Return the DataObject associated with this value store.
*/
SDODataObject getDataObject() {
return dataObject;
}
/**
* Return the POJO associated with this value store.
*/
Object getEntity() {
return entity;
}
/**
* Return the XMLDescriptor associated with this value store.
* This is the XMLDescriptor for the associated POJO.
*/
XMLDescriptor getEntityDescriptor() {
return descriptor;
}
/**
* Return the JAXBHelperContext. This is the JAXBHelperContext
* used to create the DataObject.
*/
JAXBHelperContext getJAXBHelperContext() {
return jaxbHelperContext;
}
/**
* Initialize the value store with its associated DataObject.
*/
@Override
public void initialize(DataObject aDataObject) {
this.dataObject = (SDODataObject) aDataObject;
}
/**
* Get the value from the wrapped POJO, wrapping in DataObjects as
* necessary.
*/
@Override
public Object getDeclaredProperty(int propertyIndex) {
SDOProperty declaredProperty = (SDOProperty) dataObject.getType().getDeclaredProperties().get(propertyIndex);
if(declaredProperty.getType().isChangeSummaryType()) {
return dataObject.getChangeSummary();
}
Mapping mapping = this.getJAXBMappingForProperty(declaredProperty);
Object value = mapping.getAttributeAccessor().getAttributeValueFromObject(entity);
if (declaredProperty.isMany()) {
JAXBListWrapper listWrapper = listWrappers.get(declaredProperty);
if (null != listWrapper) {
return listWrapper;
}
listWrapper = new JAXBListWrapper(this, declaredProperty);
listWrappers.put(declaredProperty, listWrapper);
return listWrapper;
} else if(null == value || declaredProperty.getType().isDataType()) {
return value;
} else {
if(declaredProperty.isContainment()) {
return jaxbHelperContext.wrap(value, declaredProperty, dataObject);
} else {
return jaxbHelperContext.wrap(value);
}
}
}
/**
* Set the value on the underlying POJO, unwrapping values as necessary.
*/
@Override
public void setDeclaredProperty(int propertyIndex, Object value) {
SDOProperty declaredProperty = (SDOProperty) dataObject.getType().getDeclaredProperties().get(propertyIndex);
if(declaredProperty.getType().isChangeSummaryType()) {
return;
}
Mapping mapping = this.getJAXBMappingForProperty(declaredProperty);
Object newValue = value;
Object oldValue = mapping.getAttributeAccessor().getAttributeValueFromObject(entity);
if (declaredProperty.getType().isDataType()) {
if (!declaredProperty.isMany()) {
AbstractSession session = ((JAXBContext) jaxbHelperContext.getJAXBContext()).getXMLContext().getSession(entity);
XMLDirectMapping directMapping = (XMLDirectMapping) mapping;
if (directMapping.hasConverter()) {
newValue = directMapping.getConverter().convertDataValueToObjectValue(newValue, session);
} else {
CoreField field = mapping.getField();
newValue = session.getDatasourcePlatform().getConversionManager().convertObject(newValue, descriptor.getObjectBuilder().getFieldClassification((XMLField) field));
}
}
mapping.setAttributeValueInObject(entity, newValue);
} else if (declaredProperty.isMany()) {
// Get a ListWrapper and set it's current elements
ListWrapper listWrapper = (ListWrapper) getDeclaredProperty(propertyIndex);
listWrapper.addAll((List) newValue);
} else {
// OLD VALUE
if (mapping.isAbstractCompositeObjectMapping()) {
XMLCompositeObjectMapping compositeMapping = (XMLCompositeObjectMapping) mapping;
XMLInverseReferenceMapping inverseReferenceMapping = compositeMapping.getInverseReferenceMapping();
if (oldValue != null && inverseReferenceMapping != null && inverseReferenceMapping.getAttributeAccessor() != null) {
inverseReferenceMapping.getAttributeAccessor().setAttributeValueInObject(oldValue, null);
}
}
// NEW VALUE
newValue = jaxbHelperContext.unwrap((DataObject) value);
mapping.getAttributeAccessor().setAttributeValueInObject(entity, newValue);
if (mapping.isAbstractCompositeObjectMapping()) {
XMLCompositeObjectMapping compositeMapping = (XMLCompositeObjectMapping) mapping;
XMLInverseReferenceMapping inverseReferenceMapping = compositeMapping.getInverseReferenceMapping();
if (value != null && inverseReferenceMapping != null && inverseReferenceMapping.getAttributeAccessor() != null) {
inverseReferenceMapping.getAttributeAccessor().setAttributeValueInObject(newValue, entity);
}
}
}
}
/**
* For isMany=false properties return true if not null. For collection properties
* return true if the collection is not empty.
*/
@Override
public boolean isSetDeclaredProperty(int propertyIndex) {
SDOProperty declaredProperty = (SDOProperty) dataObject.getType().getDeclaredProperties().get(propertyIndex);
if(declaredProperty.getType().isChangeSummaryType()) {
return true;
}
Mapping mapping = this.getJAXBMappingForProperty(declaredProperty);
if (declaredProperty.isMany()) {
Collection collection = (Collection) mapping.getAttributeAccessor().getAttributeValueFromObject(entity);
if (null == collection) {
return false;
}
return !collection.isEmpty();
} else {
return null != mapping.getAttributeAccessor().getAttributeValueFromObject(entity);
}
}
/**
* For isMany=false properties set the value to null. For isMany=true set
* the value to an empty container of the appropriate type.
*/
@Override
public void unsetDeclaredProperty(int propertyIndex) {
SDOProperty declaredProperty = (SDOProperty) dataObject.getType().getDeclaredProperties().get(propertyIndex);
Mapping mapping = this.getJAXBMappingForProperty(declaredProperty);
if (declaredProperty.isMany()) {
ContainerMapping containerMapping = (ContainerMapping) mapping;
ContainerPolicy containerPolicy = containerMapping.getContainerPolicy();
// OLD VALUE
if (mapping.isAbstractCompositeCollectionMapping()) {
XMLCompositeCollectionMapping compositeMapping = (XMLCompositeCollectionMapping) mapping;
XMLInverseReferenceMapping inverseReferenceMapping = compositeMapping.getInverseReferenceMapping();
if (inverseReferenceMapping != null && inverseReferenceMapping.getAttributeAccessor() != null) {
Object oldContainer = mapping.getAttributeValueFromObject(entity);
if (oldContainer != null) {
AbstractSession session = ((JAXBContext) jaxbHelperContext.getJAXBContext()).getXMLContext().getSession(entity);
Object iterator = containerPolicy.iteratorFor(oldContainer);
while (containerPolicy.hasNext(iterator)) {
Object oldValue = containerPolicy.next(iterator, session);
inverseReferenceMapping.getAttributeAccessor().setAttributeValueInObject(oldValue, null);
}
}
}
}
// NEW VALUE
Object container = containerPolicy.containerInstance();
mapping.getAttributeAccessor().setAttributeValueInObject(entity, container);
} else {
// OLD VALUE
Object oldValue = mapping.getAttributeAccessor().getAttributeValueFromObject(entity);
if (mapping.isAbstractCompositeObjectMapping()) {
XMLCompositeObjectMapping compositeMapping = (XMLCompositeObjectMapping) mapping;
final XMLInverseReferenceMapping inverseReferenceMapping = compositeMapping.getInverseReferenceMapping();
if (inverseReferenceMapping != null && inverseReferenceMapping.getAttributeAccessor() != null) {
if (oldValue != null) {
inverseReferenceMapping.getAttributeAccessor().setAttributeValueInObject(oldValue, null);
}
}
}
// NEW VALUE
mapping.getAttributeAccessor().setAttributeValueInObject(entity, null);
}
}
@Override
public Object getOpenContentProperty(Property property) {
throw new UnsupportedOperationException();
}
@Override
public void setOpenContentProperty(Property property, Object value) {
throw new UnsupportedOperationException();
}
@Override
public boolean isSetOpenContentProperty(Property property) {
throw new UnsupportedOperationException();
}
@Override
public void unsetOpenContentProperty(Property property) {
throw new UnsupportedOperationException();
}
@Override
public void setManyProperty(Property property, Object value) {
}
@Override
public ValueStore copy() {
AbstractSession session = ((JAXBContext) jaxbHelperContext.getJAXBContext()).getXMLContext().getSession(entity);
Object originalEntity = entity;
entity = descriptor.getInstantiationPolicy().buildNewInstance();
for(SDOProperty sdoProperty : (List<SDOProperty>) dataObject.getType().getProperties()) {
if(!sdoProperty.getType().isChangeSummaryType()) {
Mapping mapping = getJAXBMappingForProperty(sdoProperty);
CoreAttributeAccessor attributeAccessor = mapping.getAttributeAccessor();
Object attributeValue = attributeAccessor.getAttributeValueFromObject(originalEntity);
if(mapping.isCollectionMapping()) {
Object containerCopy = null;
SDOChangeSummary sdoChangeSummary = dataObject.getChangeSummary();
if(null != sdoChangeSummary) {
List list = listWrappers.get(sdoProperty);
containerCopy = sdoChangeSummary.getOriginalElements().get(list);
}
if(null == containerCopy) {
CoreContainerPolicy containerPolicy = mapping.getContainerPolicy();
containerCopy = containerPolicy.containerInstance();
if(null != attributeValue) {
Object attributeValueIterator = containerPolicy.iteratorFor(attributeValue);
while(containerPolicy.hasNext(attributeValueIterator)) {
containerPolicy.addInto(containerPolicy.nextEntry(attributeValueIterator), containerCopy, session);
}
}
}
attributeAccessor.setAttributeValueInObject(entity, containerCopy);
} else {
attributeAccessor.setAttributeValueInObject(entity, attributeValue);
}
}
}
return new JAXBValueStore(jaxbHelperContext, originalEntity, descriptor, dataObject, listWrappers);
}
/**
* Return the JAXB mapping for the SDO property. They are matched
* on their XML schema representation.
*/
Mapping getJAXBMappingForProperty(SDOProperty sdoProperty) {
DatabaseMapping sdoMapping = sdoProperty.getXmlMapping();
XMLField field;
if (sdoMapping instanceof XMLObjectReferenceMapping) {
XMLObjectReferenceMapping referenceMapping = (XMLObjectReferenceMapping) sdoMapping;
field = (XMLField) referenceMapping.getFields().get(0);
} else {
field = (XMLField) sdoMapping.getField();
}
TreeObjectBuilder treeObjectBuilder = (TreeObjectBuilder) descriptor.getObjectBuilder();
XPathNode xPathNode = treeObjectBuilder.getRootXPathNode();
XPathFragment xPathFragment = field.getXPathFragment();
while (xPathNode != null && xPathFragment != null) {
if (xPathFragment.isAttribute()) {
if (sdoProperty.isMany() && !sdoProperty.isContainment() && !sdoProperty.getType().isDataType()) {
xPathFragment = null;
break;
}
Map attributeChildrenMap = xPathNode.getAttributeChildrenMap();
if (null == attributeChildrenMap) {
xPathNode = null;
} else {
xPathNode = (XPathNode) attributeChildrenMap.get(xPathFragment);
}
} else if (xPathFragment.nameIsText()) {
xPathNode = xPathNode.getTextNode();
} else {
Map nonAttributeChildrenMap = xPathNode.getNonAttributeChildrenMap();
if (null == nonAttributeChildrenMap) {
xPathNode = null;
} else {
xPathNode = (XPathNode) nonAttributeChildrenMap.get(xPathFragment);
}
}
xPathFragment = xPathFragment.getNextFragment();
if (xPathFragment != null && xPathFragment.nameIsText()) {
if (sdoProperty.isMany() && !sdoProperty.isContainment()) {
xPathFragment = null;
break;
}
}
}
if (null == xPathFragment && xPathNode != null) {
if (xPathNode.getNodeValue().isMappingNodeValue()) {
MappingNodeValue mappingNodeValue = (MappingNodeValue) xPathNode.getNodeValue();
return mappingNodeValue.getMapping();
}
}
throw SDOException.sdoJaxbNoMappingForProperty(sdoProperty.getName(), field.getXPath());
}
}