| /* |
| * 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.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(); |
| } |
| } |