org.eclipse.persistence.internal.oxm.XMLCompositeObjectMappingNodeValue Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 1998, 2024 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.internal.oxm;
import org.eclipse.persistence.core.queries.CoreAttributeGroup;
import org.eclipse.persistence.core.queries.CoreAttributeItem;
import org.eclipse.persistence.core.sessions.CoreSession;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.exceptions.XMLMarshalException;
import org.eclipse.persistence.internal.core.sessions.CoreAbstractSession;
import org.eclipse.persistence.internal.oxm.mappings.CompositeObjectMapping;
import org.eclipse.persistence.internal.oxm.mappings.Descriptor;
import org.eclipse.persistence.internal.oxm.mappings.DirectMapping;
import org.eclipse.persistence.internal.oxm.mappings.Field;
import org.eclipse.persistence.internal.oxm.mappings.InverseReferenceMapping;
import org.eclipse.persistence.internal.oxm.mappings.Mapping;
import org.eclipse.persistence.internal.oxm.mappings.UnmarshalKeepAsElementPolicy;
import org.eclipse.persistence.internal.oxm.record.MarshalContext;
import org.eclipse.persistence.internal.oxm.record.MarshalRecord;
import org.eclipse.persistence.internal.oxm.record.ObjectMarshalContext;
import org.eclipse.persistence.internal.oxm.record.UnmarshalRecord;
import org.eclipse.persistence.internal.oxm.record.XMLReader;
import org.eclipse.persistence.internal.oxm.record.XMLRecord;
import org.eclipse.persistence.internal.oxm.record.deferred.CompositeObjectMappingContentHandler;
import org.eclipse.persistence.oxm.XMLRoot;
import org.eclipse.persistence.oxm.mappings.nullpolicy.AbstractNullPolicy;
import org.eclipse.persistence.platform.xml.XMLPlatformFactory;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import javax.xml.namespace.QName;
import java.lang.reflect.Modifier;
import java.util.List;
/**
* INTERNAL:
* Purpose: This is how the XML Composite Object Mapping is handled
* when used with the TreeObjectBuilder.
*/
public class XMLCompositeObjectMappingNodeValue extends XMLRelationshipMappingNodeValue implements NullCapableValue {
private CompositeObjectMapping xmlCompositeObjectMapping;
private boolean isInverseReference;
public XMLCompositeObjectMappingNodeValue(CompositeObjectMapping xmlCompositeObjectMapping) {
this.xmlCompositeObjectMapping = xmlCompositeObjectMapping;
}
public XMLCompositeObjectMappingNodeValue(CompositeObjectMapping xmlCompositeObjectMapping, boolean isInverse) {
this(xmlCompositeObjectMapping);
isInverseReference = isInverse;
}
@Override
public void attribute(UnmarshalRecord unmarshalRecord, String namespaceURI, String localName, String value) {
unmarshalRecord.removeNullCapableValue(this);
Descriptor referenceDescriptor = (Descriptor) getMapping().getReferenceDescriptor();
ObjectBuilder treeObjectBuilder = (ObjectBuilder) referenceDescriptor.getObjectBuilder();
MappingNodeValue textMappingNodeValue = (MappingNodeValue) treeObjectBuilder.getRootXPathNode().getTextNode().getNodeValue();
Mapping textMapping = textMappingNodeValue.getMapping();
Object childObject = referenceDescriptor.getInstantiationPolicy().buildNewInstance();
if(textMapping.isAbstractDirectMapping()) {
DirectMapping xmlDirectMapping = (DirectMapping) textMappingNodeValue.getMapping();
Field xmlField = (Field) xmlDirectMapping.getField();
Object realValue = unmarshalRecord.getXMLReader().convertValueBasedOnSchemaType(xmlField, value, (ConversionManager) unmarshalRecord.getSession().getDatasourcePlatform().getConversionManager(), unmarshalRecord);
Object convertedValue = xmlDirectMapping.getAttributeValue(realValue, unmarshalRecord.getSession(), unmarshalRecord);
xmlDirectMapping.setAttributeValueInObject(childObject, convertedValue);
} else {
Object oldChildObject = unmarshalRecord.getCurrentObject();
CompositeObjectMapping nestedXMLCompositeObjectMapping = (CompositeObjectMapping) textMappingNodeValue.getMapping();
unmarshalRecord.setCurrentObject(childObject);
textMappingNodeValue.attribute(unmarshalRecord, namespaceURI, localName, value);
unmarshalRecord.setCurrentObject(oldChildObject);
}
setAttributeValue(childObject, unmarshalRecord);
}
/**
* Marshal any 'self' mapped attributes.
*
*/
@Override
public boolean marshalSelfAttributes(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, CoreAbstractSession session, NamespaceResolver namespaceResolver, Marshaller marshaller) {
Object objectValue = xmlCompositeObjectMapping.getAttributeValueFromObject(object);
objectValue = xmlCompositeObjectMapping.convertObjectValueToDataValue(objectValue, session, marshaller);
Descriptor descriptor = (Descriptor)session.getDescriptor(objectValue);
if(descriptor != null){
ObjectBuilder objectBuilder = (ObjectBuilder)descriptor.getObjectBuilder();
return objectBuilder.marshalAttributes(marshalRecord, objectValue, session);
} else {
UnmarshalKeepAsElementPolicy keepAsElementPolicy = getMapping().getKeepAsElementPolicy();
if(null != keepAsElementPolicy && (keepAsElementPolicy.isKeepAllAsElement() || keepAsElementPolicy.isKeepUnknownAsElement())) {
if(objectValue instanceof Node rootNode) {
NamedNodeMap attributes = rootNode.getAttributes();
for(int i = 0; i < attributes.getLength(); i++) {
Attr next = (Attr)attributes.item(i);
if(!(javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(next.getNamespaceURI()))) {
marshalRecord.node(next, namespaceResolver);
}
}
}
}
}
return false;
}
@Override
public boolean marshal(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, CoreAbstractSession session, NamespaceResolver namespaceResolver) {
return marshal(xPathFragment, marshalRecord, object, session, namespaceResolver, ObjectMarshalContext.getInstance());
}
@Override
public boolean marshal(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, CoreAbstractSession session, NamespaceResolver namespaceResolver, MarshalContext marshalContext) {
if (xmlCompositeObjectMapping.isReadOnly()) {
return false;
}
int size =marshalRecord.getCycleDetectionStack().size();
Object objectValue = marshalContext.getAttributeValue(object, xmlCompositeObjectMapping);
if ((isInverseReference || xmlCompositeObjectMapping.getInverseReferenceMapping() != null) && objectValue != null && size >= 2) {
Object owner = marshalRecord.getCycleDetectionStack().get(size - 2);
if (objectValue.equals(owner)) {
return false;
}
}
return this.marshalSingleValue(xPathFragment, marshalRecord, object, objectValue, session, namespaceResolver, marshalContext);
}
private boolean isNil(Object value) {
if (value instanceof XMLRoot) {
return ((XMLRoot)value).isNil();
}
return false;
}
@Override
public boolean marshalSingleValue(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, Object objectValue, CoreAbstractSession session, NamespaceResolver namespaceResolver, MarshalContext marshalContext) {
boolean isNilFlag = isNil(objectValue);
objectValue = xmlCompositeObjectMapping.convertObjectValueToDataValue(objectValue, session, marshalRecord.getMarshaller());
if (null == objectValue) {
return xmlCompositeObjectMapping.getNullPolicy().compositeObjectMarshal(xPathFragment, marshalRecord, object, session, namespaceResolver);
}
XPathFragment groupingFragment = marshalRecord.openStartGroupingElements(namespaceResolver);
if(xPathFragment.hasAttribute) {
ObjectBuilder tob = (ObjectBuilder) xmlCompositeObjectMapping.getReferenceDescriptor().getObjectBuilder();
MappingNodeValue textMappingNodeValue = (MappingNodeValue) tob.getRootXPathNode().getTextNode().getMarshalNodeValue();
Mapping textMapping = textMappingNodeValue.getMapping();
if(textMapping.isAbstractDirectMapping()) {
DirectMapping xmlDirectMapping = (DirectMapping) textMapping;
Object fieldValue = xmlDirectMapping.getFieldValue(xmlDirectMapping.valueFromObject(objectValue, xmlDirectMapping.getField(), session), session, marshalRecord);
QName schemaType = ((Field) xmlDirectMapping.getField()).getSchemaTypeForValue(fieldValue, session);
if(fieldValue != null) {
marshalRecord.attribute(xPathFragment, namespaceResolver, fieldValue, schemaType);
} else {
XMLMarshalException ex = XMLMarshalException.nullValueNotAllowed(this.xmlCompositeObjectMapping.getAttributeName(), this.xmlCompositeObjectMapping.getDescriptor().getJavaClass().getName());
try {
marshalRecord.getMarshaller().getErrorHandler().warning(new SAXParseException(null, null, ex));
} catch(Exception saxException) {
throw ex;
}
}
marshalRecord.closeStartGroupingElements(groupingFragment);
return true;
} else {
return textMappingNodeValue.marshalSingleValue(xPathFragment, marshalRecord, objectValue, textMapping.getAttributeValueFromObject(objectValue), session, namespaceResolver, marshalContext);
}
}
boolean isSelfFragment = xPathFragment.isSelfFragment;
marshalRecord.closeStartGroupingElements(groupingFragment);
UnmarshalKeepAsElementPolicy keepAsElementPolicy = xmlCompositeObjectMapping.getKeepAsElementPolicy();
if (null != keepAsElementPolicy && (keepAsElementPolicy.isKeepUnknownAsElement() || keepAsElementPolicy.isKeepAllAsElement()) && objectValue instanceof Node) {
if (isSelfFragment) {
NodeList children = ((org.w3c.dom.Element) objectValue).getChildNodes();
for (int i = 0, childrenLength = children.getLength(); i < childrenLength ; i++) {
Node next = children.item(i);
short nodeType = next.getNodeType();
if (nodeType == Node.ELEMENT_NODE) {
marshalRecord.node(next, marshalRecord.getNamespaceResolver());
return true;
} else if (nodeType == Node.TEXT_NODE) {
marshalRecord.characters(next.getNodeValue());
return true;
}
}
return false;
} else {
marshalRecord.node((Node) objectValue, marshalRecord.getNamespaceResolver());
return true;
}
}
Descriptor descriptor = (Descriptor)xmlCompositeObjectMapping.getReferenceDescriptor();
if(descriptor == null){
descriptor = (Descriptor) session.getDescriptor(objectValue.getClass());
}else if(descriptor.hasInheritance()){
Class objectValueClass = objectValue.getClass();
if(!(objectValueClass == descriptor.getJavaClass())){
descriptor = (Descriptor) session.getDescriptor(objectValueClass);
}
}
if(descriptor != null){
marshalRecord.beforeContainmentMarshal(objectValue);
ObjectBuilder objectBuilder = (ObjectBuilder)descriptor.getObjectBuilder();
CoreAttributeGroup group = marshalRecord.getCurrentAttributeGroup();
CoreAttributeItem item = group.getItem(getMapping().getAttributeName());
CoreAttributeGroup nestedGroup = XMLRecord.DEFAULT_ATTRIBUTE_GROUP;
if(item != null) {
if(item.getGroups() != null) {
nestedGroup = item.getGroup(descriptor.getJavaClass());
}
if(nestedGroup == null) {
nestedGroup = item.getGroup() == null?XMLRecord.DEFAULT_ATTRIBUTE_GROUP:item.getGroup();
}
}
marshalRecord.pushAttributeGroup(nestedGroup);
if (!(isSelfFragment || xPathFragment.nameIsText)) {
xPathNode.startElement(marshalRecord, xPathFragment, object, session, namespaceResolver, objectBuilder, objectValue);
if (isNilFlag) {
marshalRecord.nilSimple(namespaceResolver);
}
}
List extraNamespaces = null;
if (marshalRecord.getNamespaceResolver() != null && descriptor.getNamespaceResolver() != null) {
for (String prefix: descriptor.getNamespaceResolver().getPrefixesToNamespaces().keySet()) {
if (!marshalRecord.getNamespaceResolver().hasPrefix(prefix)) {
marshalRecord.setEqualNamespaceResolvers(false);
break;
}
}
}
if (!marshalRecord.hasEqualNamespaceResolvers()) {
extraNamespaces = objectBuilder.addExtraNamespacesToNamespaceResolver(descriptor, marshalRecord, session, true, false);
writeExtraNamespaces(extraNamespaces, marshalRecord, session);
}
if(!isSelfFragment) {
marshalRecord.addXsiTypeAndClassIndicatorIfRequired(descriptor, (Descriptor) xmlCompositeObjectMapping.getReferenceDescriptor(), (Field)xmlCompositeObjectMapping.getField(), false);
}
objectBuilder.buildRow(marshalRecord, objectValue, session, marshalRecord.getMarshaller(), xPathFragment);
marshalRecord.afterContainmentMarshal(object, objectValue);
marshalRecord.popAttributeGroup();
if (!(isSelfFragment || xPathFragment.nameIsText())) {
marshalRecord.endElement(xPathFragment, namespaceResolver);
}
marshalRecord.removeExtraNamespacesFromNamespaceResolver(extraNamespaces, session);
} else {
if(Constants.UNKNOWN_OR_TRANSIENT_CLASS.equals(xmlCompositeObjectMapping.getReferenceClassName())){
throw XMLMarshalException.descriptorNotFoundInProject(objectValue.getClass().getName());
}
if (!(isSelfFragment || xPathFragment.nameIsText())) {
xPathNode.startElement(marshalRecord, xPathFragment, object, session, namespaceResolver, null, objectValue);
}
QName schemaType = ((Field) xmlCompositeObjectMapping.getField()).getSchemaTypeForValue(objectValue,session);
updateNamespaces(schemaType, marshalRecord,((Field)xmlCompositeObjectMapping.getField()));
marshalRecord.characters(schemaType, objectValue, null, false);
if (!(isSelfFragment || xPathFragment.nameIsText())) {
marshalRecord.endElement(xPathFragment, namespaceResolver);
}
}
return true;
}
@Override
public boolean startElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord, Attributes atts) {
try {
unmarshalRecord.removeNullCapableValue(this);
Descriptor xmlDescriptor = (Descriptor)xmlCompositeObjectMapping.getReferenceDescriptor();
if (null == xmlDescriptor) {
xmlDescriptor = findReferenceDescriptor(xPathFragment, unmarshalRecord, atts, xmlCompositeObjectMapping,xmlCompositeObjectMapping.getKeepAsElementPolicy());
if(xmlDescriptor == null){
if(xmlCompositeObjectMapping.getField() != null){
//try leaf element type
QName leafType = ((Field)xmlCompositeObjectMapping.getField()).getLastXPathFragment().getLeafElementType();
if (leafType != null) {
XPathFragment frag = new XPathFragment();
frag.setNamespaceAware(unmarshalRecord.isNamespaceAware());
String xpath = leafType.getLocalPart();
String uri = leafType.getNamespaceURI();
if (uri != null && !uri.isEmpty()) {
frag.setNamespaceURI(uri);
String prefix = ((Descriptor)xmlCompositeObjectMapping.getDescriptor()).getNonNullNamespaceResolver().resolveNamespaceURI(uri);
if (prefix != null && !prefix.isEmpty()) {
xpath = prefix + Constants.COLON + xpath;
}
}
frag.setXPath(xpath);
Context xmlContext = unmarshalRecord.getUnmarshaller().getContext();
xmlDescriptor = xmlContext.getDescriptorByGlobalType(frag);
}
}
}
UnmarshalKeepAsElementPolicy policy = xmlCompositeObjectMapping.getKeepAsElementPolicy();
if (null != policy && ((xmlDescriptor == null && policy.isKeepUnknownAsElement()) || policy.isKeepAllAsElement())) {
QName schemaType = unmarshalRecord.getTypeQName();
if(schemaType == null){
schemaType = ((Field)xmlCompositeObjectMapping.getField()).getSchemaType();
unmarshalRecord.setTypeQName(schemaType);
}
if(schemaType != null){
Class