blob: b4a82e4526acf5d91ff4e276475dbea274a72f08 [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:
// Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.internal.oxm;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.persistence.internal.core.helper.CoreField;
import org.eclipse.persistence.internal.core.sessions.CoreAbstractSession;
import org.eclipse.persistence.internal.oxm.mappings.BinaryDataMapping;
import org.eclipse.persistence.internal.oxm.mappings.ChoiceObjectMapping;
import org.eclipse.persistence.internal.oxm.mappings.CompositeObjectMapping;
import org.eclipse.persistence.internal.oxm.mappings.DirectMapping;
import org.eclipse.persistence.internal.oxm.mappings.Field;
import org.eclipse.persistence.internal.oxm.mappings.Mapping;
import org.eclipse.persistence.internal.oxm.mappings.ObjectReferenceMapping;
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.UnmarshalContext;
import org.eclipse.persistence.internal.oxm.record.UnmarshalRecord;
import org.xml.sax.Attributes;
/**
* INTERNAL:
* <p><b>Purpose</b>: This is how the XML Choice Collection Mapping is
* handled when used with the TreeObjectBuilder.</p>
* @author mmacivor
*/
public class XMLChoiceObjectMappingNodeValue extends MappingNodeValue {
private NodeValue choiceElementNodeValue;
private Map<Class<?>, NodeValue> choiceElementNodeValues;
private ChoiceObjectMapping xmlChoiceMapping;
//The first node value of the choice will be registered as a null capable value. If any
//of the choice elements get hit, this needs to be removed as a null value.
private XMLChoiceObjectMappingNodeValue nullCapableNodeValue;
private Field xmlField;
public XMLChoiceObjectMappingNodeValue(ChoiceObjectMapping mapping, Field xmlField) {
this.xmlChoiceMapping = mapping;
this.xmlField = xmlField;
initializeNodeValue();
}
@Override
public boolean isOwningNode(XPathFragment xPathFragment) {
return choiceElementNodeValue.isOwningNode(xPathFragment);
}
public void initializeNodeValue() {
Mapping xmlMapping = (Mapping) xmlChoiceMapping.getChoiceElementMappings().get(xmlField);
choiceElementNodeValue = getNodeValueForMapping(xmlMapping);
//check for mappings to other classes with the same field
for(Entry<Class<?>, Mapping> entry: ((Map<Class<?>, Mapping>)xmlChoiceMapping.getChoiceElementMappingsByClass()).entrySet()) {
Field field = (Field) xmlChoiceMapping.getClassToFieldMappings().get(entry.getKey());
if(field != null && field.equals(this.xmlField)) {
Mapping mappingForClass = entry.getValue();
if(mappingForClass != xmlMapping) {
if(this.choiceElementNodeValues == null) {
choiceElementNodeValues = new HashMap<>();
}
choiceElementNodeValues.put(entry.getKey(), getNodeValueForMapping(mappingForClass));
}
}
}
}
private NodeValue getNodeValueForMapping(Mapping xmlMapping) {
if(xmlMapping instanceof BinaryDataMapping){
return new XMLBinaryDataMappingNodeValue((BinaryDataMapping)xmlMapping);
} else if(xmlMapping instanceof DirectMapping) {
return new XMLDirectMappingNodeValue((DirectMapping)xmlMapping);
} else if(xmlMapping instanceof ObjectReferenceMapping) {
return new XMLObjectReferenceMappingNodeValue((ObjectReferenceMapping)xmlMapping, xmlField);
} else {
return new XMLCompositeObjectMappingNodeValue((CompositeObjectMapping)xmlMapping);
}
}
public void setNullCapableNodeValue(XMLChoiceObjectMappingNodeValue nodeValue) {
this.nullCapableNodeValue = nodeValue;
}
@Override
public boolean marshal(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, CoreAbstractSession session, NamespaceResolver namespaceResolver) {
return this.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(xmlChoiceMapping.isReadOnly()) {
return false;
}
Object value = xmlChoiceMapping.getFieldValue(object, session, marshalRecord);
return this.marshalSingleValue(xPathFragment, marshalRecord, object, value, session, namespaceResolver, marshalContext);
}
@Override
public boolean marshalSingleValue(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, Object value, CoreAbstractSession session, NamespaceResolver namespaceResolver, MarshalContext marshalContext) {
Class<?> valueClass = null;
if (value instanceof Root) {
Root root = (Root)value;
for(CoreField next: (List<CoreField>) this.xmlChoiceMapping.getFields()) {
XPathFragment fragment = ((Field)next).getXPathFragment();
while(fragment != null && !fragment.nameIsText) {
if(fragment.getNextFragment() == null || fragment.getHasText()) {
if(fragment.getLocalName().equals(root.getLocalName())) {
String fragUri = fragment.getNamespaceURI();
String namespaceUri = root.getNamespaceURI();
if((namespaceUri == null && fragUri == null) || (namespaceUri != null && fragUri != null && namespaceUri.equals(fragUri))) {
if(next == this.xmlField) {
return this.choiceElementNodeValue.marshalSingleValue(xPathFragment, marshalRecord, object, value, session, namespaceResolver, marshalContext);
} else {
//If this root is associated with another field, then return and let that NodeValue handle it
return false;
}
}
}
}
fragment = fragment.getNextFragment();
}
}
valueClass = root.getObject().getClass();
}
if (value != null) {
if(valueClass == null) {
valueClass = value.getClass();
}
Field fieldForClass = null;
Class<?> theClass = valueClass;
while(theClass != null) {
fieldForClass = (Field) xmlChoiceMapping.getClassToFieldMappings().get(valueClass);
if(fieldForClass != null) {
break;
}
theClass = theClass.getSuperclass();
}
if (fieldForClass != null && fieldForClass.equals(this.xmlField)) {
if(this.choiceElementNodeValues != null) {
NodeValue nodeValue = this.choiceElementNodeValues.get(theClass);
if(nodeValue != null) {
return nodeValue.marshalSingleValue(xPathFragment, marshalRecord, object, value, session, namespaceResolver, marshalContext);
}
}
return this.choiceElementNodeValue.marshalSingleValue(xPathFragment, marshalRecord, object, value, session, namespaceResolver, marshalContext);
}
List<Field> sourceFields = null;
theClass = valueClass;
while(theClass != null) {
sourceFields = (List<Field>) xmlChoiceMapping.getClassToSourceFieldsMappings().get(theClass);
if(sourceFields != null) {
break;
}
theClass = theClass.getSuperclass();
}
if (sourceFields != null && sourceFields.contains(this.xmlField)) {
return this.choiceElementNodeValue.marshalSingleValue(xPathFragment, marshalRecord, object, value, session, namespaceResolver, marshalContext);
}
}
return false;
}
@Override
public void endElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord) {
if(null != xmlChoiceMapping.getConverter()) {
UnmarshalContext unmarshalContext = unmarshalRecord.getUnmarshalContext();
unmarshalRecord.setUnmarshalContext(new ChoiceUnmarshalContext(unmarshalContext, xmlChoiceMapping));
this.choiceElementNodeValue.endElement(xPathFragment, unmarshalRecord);
unmarshalRecord.setUnmarshalContext(unmarshalContext);
} else {
this.choiceElementNodeValue.endElement(xPathFragment, unmarshalRecord);
}
}
@Override
public boolean startElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord, Attributes atts) {
return this.choiceElementNodeValue.startElement(xPathFragment, unmarshalRecord, atts);
}
@Override
public void setXPathNode(XPathNode xPathNode) {
super.setXPathNode(xPathNode);
this.choiceElementNodeValue.setXPathNode(xPathNode);
if(this.choiceElementNodeValues != null) {
for(NodeValue next:choiceElementNodeValues.values()) {
next.setXPathNode(xPathNode);
}
}
}
/**
* The underlying choice element node value will handle attributes.
*
*/
@Override
public void attribute(UnmarshalRecord unmarshalRecord, String URI, String localName, String value) {
this.choiceElementNodeValue.attribute(unmarshalRecord, URI, localName, value);
}
@Override
public Mapping getMapping() {
return this.xmlChoiceMapping;
}
@Override
public boolean isWhitespaceAware() {
return choiceElementNodeValue.isWhitespaceAware();
}
}