org.eclipse.persistence.internal.oxm.XMLCompositeCollectionMappingNodeValue 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, 2019 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 java.util.List;
import javax.xml.namespace.QName;
import org.eclipse.persistence.core.queries.CoreAttributeGroup;
import org.eclipse.persistence.core.queries.CoreAttributeItem;
import org.eclipse.persistence.exceptions.XMLMarshalException;
import org.eclipse.persistence.internal.core.queries.CoreContainerPolicy;
import org.eclipse.persistence.internal.core.sessions.CoreAbstractSession;
import org.eclipse.persistence.internal.oxm.mappings.CompositeCollectionMapping;
import org.eclipse.persistence.internal.oxm.mappings.Descriptor;
import org.eclipse.persistence.internal.oxm.mappings.Field;
import org.eclipse.persistence.internal.oxm.mappings.InverseReferenceMapping;
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.CompositeCollectionMappingContentHandler;
import org.eclipse.persistence.oxm.mappings.nullpolicy.AbstractNullPolicy;
import org.eclipse.persistence.oxm.mappings.nullpolicy.XMLNullRepresentationType;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
/**
* INTERNAL:
* Purpose: This is how the XML Composite Collection Mapping is handled when used with the TreeObjectBuilder.
*
*/
public class XMLCompositeCollectionMappingNodeValue extends XMLRelationshipMappingNodeValue implements ContainerValue {
private CompositeCollectionMapping xmlCompositeCollectionMapping;
private int index = -1;
private boolean isInverseReference;
public XMLCompositeCollectionMappingNodeValue(CompositeCollectionMapping xmlCompositeCollectionMapping) {
super();
this.xmlCompositeCollectionMapping = xmlCompositeCollectionMapping;
}
public XMLCompositeCollectionMappingNodeValue(CompositeCollectionMapping xmlCompositeCollectionMapping, boolean isInverse) {
this(xmlCompositeCollectionMapping);
this.isInverseReference = isInverse;
}
@Override
public boolean marshal(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, CoreAbstractSession session, NamespaceResolver namespaceResolver) {
if (xmlCompositeCollectionMapping.isReadOnly()) {
return false;
}
Object collection = xmlCompositeCollectionMapping.getAttributeAccessor().getAttributeValueFromObject(object);
if (null == collection) {
AbstractNullPolicy wrapperNP = xmlCompositeCollectionMapping.getWrapperNullPolicy();
if (wrapperNP != null && wrapperNP.getMarshalNullRepresentation() == XMLNullRepresentationType.XSI_NIL) {
marshalRecord.nilSimple(namespaceResolver);
return true;
} else {
return false;
}
}
CoreContainerPolicy cp = getContainerPolicy();
int size = marshalRecord.getCycleDetectionStack().size();
// when writing the collection need to see if any of the objects we are
// writing are in the parent collection inverse ref
if ((isInverseReference || xmlCompositeCollectionMapping.getInverseReferenceMapping() != null) && size >= 2) {
Object owner = marshalRecord.getCycleDetectionStack().get(size - 2);
try {
if (cp.contains(owner, collection, session)) {
return false;
}
} catch (ClassCastException e) {
// For Bug #416875
}
}
Object iterator = cp.iteratorFor(collection);
if (null != iterator && cp.hasNext(iterator)) {
XPathFragment groupingFragment = marshalRecord.openStartGroupingElements(namespaceResolver);
marshalRecord.closeStartGroupingElements(groupingFragment);
int valueSize = cp.sizeFor(collection);
if(marshalRecord.getMarshaller().isApplicationJSON() && (valueSize > 1 || !marshalRecord.getMarshaller().isReduceAnyArrays())) {
marshalRecord.startCollection();
}
do {
Object objectValue = cp.next(iterator, session);
marshalSingleValue(xPathFragment, marshalRecord, object, objectValue, session, namespaceResolver, ObjectMarshalContext.getInstance());
} while (cp.hasNext(iterator));
if(marshalRecord.getMarshaller().isApplicationJSON() && (valueSize > 1 || !marshalRecord.getMarshaller().isReduceAnyArrays())) {
marshalRecord.endCollection();
}
return true;
}
return marshalRecord.emptyCollection(xPathFragment, namespaceResolver, xmlCompositeCollectionMapping.getWrapperNullPolicy() != null);
}
@Override
public boolean startElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord, Attributes atts) {
try {
Descriptor xmlDescriptor = (Descriptor) xmlCompositeCollectionMapping.getReferenceDescriptor();
if (xmlDescriptor == null) {
xmlDescriptor = findReferenceDescriptor(xPathFragment, unmarshalRecord, atts, xmlCompositeCollectionMapping, xmlCompositeCollectionMapping.getKeepAsElementPolicy());
if (xmlDescriptor == null) {
if (unmarshalRecord.getXMLReader().isNullRepresentedByXsiNil(xmlCompositeCollectionMapping.getNullPolicy())) {
if (unmarshalRecord.isNil()) {
return true;
}
} else if (xmlCompositeCollectionMapping.getNullPolicy().valueIsNull(atts)) {
getContainerPolicy().addInto(null, unmarshalRecord.getContainerInstance(this), unmarshalRecord.getSession());
return true;
}
if (xmlCompositeCollectionMapping.getField() != null) {
//try leaf element type
QName leafType = ((Field) xmlCompositeCollectionMapping.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.length() > 0) {
frag.setNamespaceURI(uri);
String prefix = ((Descriptor) xmlCompositeCollectionMapping.getDescriptor()).getNonNullNamespaceResolver().resolveNamespaceURI(uri);
if (prefix != null && prefix.length() > 0) {
xpath = prefix + Constants.COLON + xpath;
}
}
frag.setXPath(xpath);
Context xmlContext = unmarshalRecord.getUnmarshaller().getContext();
xmlDescriptor = xmlContext.getDescriptorByGlobalType(frag);
}
}
}
UnmarshalKeepAsElementPolicy policy = xmlCompositeCollectionMapping.getKeepAsElementPolicy();
if (policy != null && ((xmlDescriptor == null && policy.isKeepUnknownAsElement()) || policy.isKeepAllAsElement())) {
if (unmarshalRecord.getTypeQName() != null) {
Class theClass = unmarshalRecord.getConversionManager().javaType(unmarshalRecord.getTypeQName());
if (theClass == null) {
setupHandlerForKeepAsElementPolicy(unmarshalRecord, xPathFragment, atts);
return true;
}
} else {
setupHandlerForKeepAsElementPolicy(unmarshalRecord, xPathFragment, atts);
return true;
}
}
}
AbstractNullPolicy nullPolicy = xmlCompositeCollectionMapping.getNullPolicy();
if (nullPolicy.isNullRepresentedByEmptyNode()) {
String qnameString = xPathFragment.getLocalName();
if (xPathFragment.getPrefix() != null) {
qnameString = xPathFragment.getPrefix() + Constants.COLON + qnameString;
}
if (null != xmlDescriptor) {
// Process null capable value
CompositeCollectionMappingContentHandler aHandler = new CompositeCollectionMappingContentHandler(//
unmarshalRecord, this, xmlCompositeCollectionMapping, atts, xPathFragment, xmlDescriptor);
// Send control to the handler
aHandler.startElement(xPathFragment.getNamespaceURI(), xPathFragment.getLocalName(), qnameString, atts);
XMLReader xmlReader = unmarshalRecord.getXMLReader();
xmlReader.setContentHandler(aHandler);
xmlReader.setLexicalHandler(aHandler);
}
} else if (!(unmarshalRecord.getXMLReader().isNullRecord(nullPolicy, atts, unmarshalRecord))) {
Field xmlFld = (Field) this.xmlCompositeCollectionMapping.getField();
if (xmlFld.hasLastXPathFragment()) {
unmarshalRecord.setLeafElementType(xmlFld.getLastXPathFragment().getLeafElementType());
}
processChild(xPathFragment, unmarshalRecord, atts, xmlDescriptor, xmlCompositeCollectionMapping);
}
} catch (SAXException e) {
throw XMLMarshalException.unmarshalException(e);
}
return true;
}
@Override
public void endElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord) {
Object collection = unmarshalRecord.getContainerInstance(this);
endElement(xPathFragment, unmarshalRecord, collection);
}
@Override
public void endElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord, Object collection) {
if(unmarshalRecord.isNil() && unmarshalRecord.getXMLReader().isNullRepresentedByXsiNil(xmlCompositeCollectionMapping.getNullPolicy()) &&
(unmarshalRecord.getChildRecord() == null)){
if(unmarshalRecord.getXMLReader().isInCollection()){
unmarshalRecord.addAttributeValue(this, null);
}else{
unmarshalRecord.setAttributeValueNull(this);
}
unmarshalRecord.resetStringBuffer();
return;
}
if (null == unmarshalRecord.getChildRecord()) {
SAXFragmentBuilder builder = unmarshalRecord.getFragmentBuilder();
UnmarshalKeepAsElementPolicy keepAsElementPolicy = xmlCompositeCollectionMapping.getKeepAsElementPolicy();
if (null != keepAsElementPolicy && (keepAsElementPolicy.isKeepUnknownAsElement() || keepAsElementPolicy.isKeepAllAsElement()) && builder.getNodes().size() > 1) {
if(unmarshalRecord.getTypeQName() != null){
Class theClass = unmarshalRecord.getConversionManager().javaType(unmarshalRecord.getTypeQName());
if(theClass != null){
//handle simple text
endElementProcessText(unmarshalRecord, xmlCompositeCollectionMapping, xPathFragment, collection);
return;
}
}
if(builder.getNodes().size() > 1) {
setOrAddAttributeValueForKeepAsElement(builder, xmlCompositeCollectionMapping, xmlCompositeCollectionMapping, unmarshalRecord, true, collection);
return;
}
}else{
//handle simple text
endElementProcessText(unmarshalRecord, xmlCompositeCollectionMapping, xPathFragment, collection);
return;
}
return;
}
Object objectValue = unmarshalRecord.getChildRecord().getCurrentObject();
InverseReferenceMapping inverseReferenceMapping = xmlCompositeCollectionMapping.getInverseReferenceMapping();
if(null != inverseReferenceMapping) {
if(inverseReferenceMapping.getContainerPolicy() == null) {
Object currentValue = inverseReferenceMapping.getAttributeAccessor().getAttributeValueFromObject(objectValue);
if( !isInverseReference || (currentValue == null && isInverseReference)) {
inverseReferenceMapping.getAttributeAccessor().setAttributeValueInObject(objectValue, unmarshalRecord.getCurrentObject());
}
} else {
Object backpointerContainer = inverseReferenceMapping.getAttributeAccessor().getAttributeValueFromObject(objectValue);
if(backpointerContainer == null) {
backpointerContainer = inverseReferenceMapping.getContainerPolicy().containerInstance();
inverseReferenceMapping.getAttributeAccessor().setAttributeValueInObject(objectValue, backpointerContainer);
}
inverseReferenceMapping.getContainerPolicy().addInto(unmarshalRecord.getCurrentObject(), backpointerContainer, unmarshalRecord.getSession());
}
}
// convert the value - if necessary
objectValue = xmlCompositeCollectionMapping.convertDataValueToObjectValue(objectValue, unmarshalRecord.getSession(), unmarshalRecord.getUnmarshaller());
unmarshalRecord.addAttributeValue(this, objectValue, collection);
unmarshalRecord.setChildRecord(null);
}
@Override
public Object getContainerInstance() {
return getContainerPolicy().containerInstance();
}
@Override
public void setContainerInstance(Object object, Object containerInstance) {
xmlCompositeCollectionMapping.setAttributeValueInObject(object, containerInstance);
}
@Override
public CoreContainerPolicy getContainerPolicy() {
return xmlCompositeCollectionMapping.getContainerPolicy();
}
@Override
public boolean isContainerValue() {
return true;
}
@Override
public boolean marshalSingleValue(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, Object value, CoreAbstractSession session, NamespaceResolver namespaceResolver, MarshalContext marshalContext) {
Marshaller marshaller = marshalRecord.getMarshaller();
// convert the value - if necessary
boolean isNil = false;
if (value instanceof Root) {
isNil = ((Root) value).nil;
value = ((Root) value).getObject();
}
value = xmlCompositeCollectionMapping.convertObjectValueToDataValue(value, session, marshaller);
if (null == value) {
return xmlCompositeCollectionMapping.getNullPolicy().compositeObjectMarshal(xPathFragment, marshalRecord, object, session, namespaceResolver);
}
Descriptor descriptor = (Descriptor)xmlCompositeCollectionMapping.getReferenceDescriptor();
if(descriptor == null){
descriptor = (Descriptor) session.getDescriptor(value.getClass());
}else if(descriptor.hasInheritance()){
Class objectValueClass = value.getClass();
if(!(objectValueClass == descriptor.getJavaClass())){
descriptor = (Descriptor) session.getDescriptor(objectValueClass);
}
}
UnmarshalKeepAsElementPolicy keepAsElementPolicy = xmlCompositeCollectionMapping.getKeepAsElementPolicy();
if (null != keepAsElementPolicy && (keepAsElementPolicy.isKeepUnknownAsElement() || keepAsElementPolicy.isKeepAllAsElement()) && value instanceof org.w3c.dom.Node) {
marshalRecord.node((org.w3c.dom.Node) value, marshalRecord.getNamespaceResolver());
return true;
}
if(descriptor != null){
marshalRecord.beforeContainmentMarshal(value);
ObjectBuilder objectBuilder = (ObjectBuilder)descriptor.getObjectBuilder();
CoreAttributeGroup group = marshalRecord.getCurrentAttributeGroup();
CoreAttributeGroup nestedGroup = XMLRecord.DEFAULT_ATTRIBUTE_GROUP;
CoreAttributeItem item = group.getItem(getMapping().getAttributeName());
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);
xPathNode.startElement(marshalRecord, xPathFragment, object, session, namespaceResolver, objectBuilder, value);
if (isNil) {
marshalRecord.nilSimple(namespaceResolver);
}
List extraNamespaces = objectBuilder.addExtraNamespacesToNamespaceResolver(descriptor, marshalRecord, session,true, false);
writeExtraNamespaces(extraNamespaces, marshalRecord, session);
marshalRecord.addXsiTypeAndClassIndicatorIfRequired(descriptor, (Descriptor) xmlCompositeCollectionMapping.getReferenceDescriptor(), (Field)xmlCompositeCollectionMapping.getField(), false);
objectBuilder.buildRow(marshalRecord, value, session, marshaller, xPathFragment);
marshalRecord.afterContainmentMarshal(object, value);
marshalRecord.popAttributeGroup();
marshalRecord.endElement(xPathFragment, namespaceResolver);
marshalRecord.removeExtraNamespacesFromNamespaceResolver(extraNamespaces, session);
} else {
if(Constants.UNKNOWN_OR_TRANSIENT_CLASS.equals(xmlCompositeCollectionMapping.getReferenceClassName())){
throw XMLMarshalException.descriptorNotFoundInProject(value.getClass().getName());
}
xPathNode.startElement(marshalRecord, xPathFragment, object, session, namespaceResolver, null, value);
QName schemaType = ((Field) xmlCompositeCollectionMapping.getField()).getSchemaTypeForValue(value, session);
updateNamespaces(schemaType, marshalRecord,((Field)xmlCompositeCollectionMapping.getField()));
marshalRecord.characters(schemaType, value, null, false);
marshalRecord.endElement(xPathFragment, namespaceResolver);
}
return true;
}
@Override
public CompositeCollectionMapping getMapping() {
return xmlCompositeCollectionMapping;
}
@Override
protected void setOrAddAttributeValue(UnmarshalRecord unmarshalRecord, Object value, XPathFragment xPathFragment, Object collection){
unmarshalRecord.addAttributeValue(this, value, collection);
}
@Override
public boolean getReuseContainer() {
return xmlCompositeCollectionMapping.getReuseContainer();
}
/**
* INTERNAL:
* Used to track the index of the corresponding containerInstance in the containerInstances Object[] on UnmarshalRecord
*/
@Override
public void setIndex(int index){
this.index = index;
}
/**
* INTERNAL:
* Set to track the index of the corresponding containerInstance in the containerInstances Object[] on UnmarshalRecord
* Set during TreeObjectBuilder initialization
*/
@Override
public int getIndex(){
return index;
}
/**
* INTERNAL
* Return true if an empty container should be set on the object if there
* is no presence of the collection in the XML document.
* @since EclipseLink 2.3.3
*/
@Override
public boolean isDefaultEmptyContainer() {
return xmlCompositeCollectionMapping.isDefaultEmptyContainer();
}
@Override
public boolean isWrapperAllowedAsCollectionName() {
return true;
}
}