| /* |
| * 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<XPathFragment, XPathNode> attributeChildrenMap = xPathNode.getAttributeChildrenMap(); |
| if (null == attributeChildrenMap) { |
| xPathNode = null; |
| } else { |
| xPathNode = attributeChildrenMap.get(xPathFragment); |
| } |
| } else if (xPathFragment.nameIsText()) { |
| xPathNode = xPathNode.getTextNode(); |
| } else { |
| Map<XPathFragment, XPathNode> nonAttributeChildrenMap = xPathNode.getNonAttributeChildrenMap(); |
| if (null == nonAttributeChildrenMap) { |
| xPathNode = null; |
| } else { |
| 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()); |
| } |
| |
| } |