org.eclipse.persistence.internal.oxm.XMLChoiceCollectionMappingMarshalNodeValue 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, 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.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.persistence.internal.core.helper.CoreClassConstants;
import org.eclipse.persistence.internal.core.queries.CoreContainerPolicy;
import org.eclipse.persistence.internal.core.sessions.CoreAbstractSession;
import org.eclipse.persistence.internal.oxm.mappings.BinaryDataCollectionMapping;
import org.eclipse.persistence.internal.oxm.mappings.ChoiceCollectionMapping;
import org.eclipse.persistence.internal.oxm.mappings.CollectionReferenceMapping;
import org.eclipse.persistence.internal.oxm.mappings.CompositeCollectionMapping;
import org.eclipse.persistence.internal.oxm.mappings.DirectCollectionMapping;
import org.eclipse.persistence.internal.oxm.mappings.Field;
import org.eclipse.persistence.internal.oxm.mappings.Mapping;
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.oxm.mappings.nullpolicy.AbstractNullPolicy;
import org.eclipse.persistence.oxm.mappings.nullpolicy.XMLNullRepresentationType;
public class XMLChoiceCollectionMappingMarshalNodeValue extends MappingNodeValue implements ContainerValue {
private ChoiceCollectionMapping xmlChoiceCollectionMapping;
private Map fieldToNodeValues;
private Map, List> classToNodeValues;
private NodeValue choiceElementNodeValue;
private Field xmlField;
private boolean isMixedNodeValue;
private boolean isAny;
private NodeValue anyNodeValue;
private int index = -1;
/**
* This class is needed to hold field and nodeValue relationship.
* If we have choice with different fields with the same (java class) type (e.g. via XmlAdapter),
* there is need to know to which field we are holding the nodeValue.
*
* It is used in getNodeValueForValue method. If we knew only class relationship to nodeValue,
* there is no way how to say that this nodeValue is related to the first or second field (or any other field with given java class).
*/
private static class FieldNodeValue {
//author Martin Vojtek ([email protected])
//see AdapterWithElementsTestCases
private final Field field;
private final NodeValue nodeValue;
public FieldNodeValue(Field field, NodeValue nodeValue) {
super();
this.field = field;
this.nodeValue = nodeValue;
}
public Field getField() {
return field;
}
public NodeValue getNodeValue() {
return nodeValue;
}
}
public XMLChoiceCollectionMappingMarshalNodeValue(ChoiceCollectionMapping mapping, Field xmlField) {
this.xmlChoiceCollectionMapping = mapping;
this.xmlField = xmlField;
isAny = mapping.isAny();
initializeNodeValue();
}
@Override
public boolean isOwningNode(XPathFragment xPathFragment) {
if(isMixedNodeValue) {
if(xPathFragment.nameIsText()) {
return true;
} else {
return false;
}
}
return choiceElementNodeValue.isOwningNode(xPathFragment);
}
public void setFieldToNodeValues(Map fieldToNodeValues) {
this.fieldToNodeValues = fieldToNodeValues;
this.classToNodeValues = new HashMap<>();
for(Field nextField:fieldToNodeValues.keySet()) {
Class> associatedClass = ((Map>)this.xmlChoiceCollectionMapping.getFieldToClassMappings()).get(nextField);
if (classToNodeValues.containsKey(associatedClass)) {
classToNodeValues.get(associatedClass).add(new FieldNodeValue(nextField, fieldToNodeValues.get(nextField)));
} else {
List newFieldToNodeValuesList = new ArrayList<>();
newFieldToNodeValuesList.add(new FieldNodeValue(nextField, fieldToNodeValues.get(nextField)));
this.classToNodeValues.put(associatedClass, newFieldToNodeValuesList);
}
}
Collection> classes = this.classToNodeValues.keySet();
for(Class> nextClass:((Map, Mapping>)this.xmlChoiceCollectionMapping.getChoiceElementMappingsByClass()).keySet()) {
//Create node values for any classes that aren't already processed
if(!(classes.contains(nextClass))) {
Field field = (Field) xmlChoiceCollectionMapping.getClassToFieldMappings().get(nextClass);
NodeValue nodeValue = new XMLChoiceCollectionMappingUnmarshalNodeValue(xmlChoiceCollectionMapping, xmlField, (Mapping) xmlChoiceCollectionMapping.getChoiceElementMappingsByClass().get(nextClass));
List newFieldToNodeValuesList = new ArrayList<>();
newFieldToNodeValuesList.add(new FieldNodeValue(field, nodeValue));
this.classToNodeValues.put(nextClass, newFieldToNodeValuesList);
NodeValue nodeValueForField = fieldToNodeValues.get(field);
nodeValue.setXPathNode(nodeValueForField.getXPathNode());
}
}
}
private void initializeNodeValue() {
Mapping xmlMapping = (Mapping) xmlChoiceCollectionMapping.getChoiceElementMappings().get(xmlField);
if(xmlMapping instanceof BinaryDataCollectionMapping) {
choiceElementNodeValue = new XMLBinaryDataCollectionMappingNodeValue((BinaryDataCollectionMapping)xmlMapping);
} else if(xmlMapping instanceof DirectCollectionMapping) {
choiceElementNodeValue = new XMLCompositeDirectCollectionMappingNodeValue((DirectCollectionMapping)xmlMapping);
} else if(xmlMapping instanceof CompositeCollectionMapping) {
choiceElementNodeValue = new XMLCompositeCollectionMappingNodeValue((CompositeCollectionMapping)xmlMapping);
} else {
CollectionReferenceMapping refMapping = ((CollectionReferenceMapping)xmlMapping);
if(refMapping.usesSingleNode() || refMapping.getFields().size() == 1) {
choiceElementNodeValue = new XMLCollectionReferenceMappingNodeValue(refMapping, xmlField);
} else {
choiceElementNodeValue = new XMLCollectionReferenceMappingMarshalNodeValue((CollectionReferenceMapping)xmlMapping);
}
}
if(isAny){
anyNodeValue = new XMLChoiceCollectionMappingUnmarshalNodeValue(xmlChoiceCollectionMapping, null, xmlChoiceCollectionMapping.getAnyMapping());
}
}
@Override
public boolean marshal(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, CoreAbstractSession session, NamespaceResolver namespaceResolver) {
if(xmlChoiceCollectionMapping.isReadOnly()) {
return false;
}
Object value = xmlChoiceCollectionMapping.getAttributeValueFromObject(object);
if(value == null) {
AbstractNullPolicy wrapperNP = xmlChoiceCollectionMapping.getWrapperNullPolicy();
if (wrapperNP != null && wrapperNP.getMarshalNullRepresentation() == XMLNullRepresentationType.XSI_NIL) {
marshalRecord.nilSimple(namespaceResolver);
return true;
} else {
return false;
}
}
CoreContainerPolicy cp = getContainerPolicy();
Object iterator = cp.iteratorFor(value);
if (null != iterator && cp.hasNext(iterator)) {
if(xPathFragment != null) {
XPathFragment groupingFragment = marshalRecord.openStartGroupingElements(namespaceResolver);
marshalRecord.closeStartGroupingElements(groupingFragment);
}
} else {
return marshalRecord.emptyCollection(xPathFragment, namespaceResolver, xmlChoiceCollectionMapping.getWrapperNullPolicy() != null);
}
if(marshalRecord.getMarshaller().isApplicationJSON()){
List nodeValues = new ArrayList();
List values = new ArrayList<>();
NodeValue mixedNodeValue = null;
List mixedValues = null;
//sort the elements. Results will be a list of nodevalues and a corresponding list of
//collections associated with those nodevalues
while(cp.hasNext(iterator)) {
Object nextValue = xmlChoiceCollectionMapping.convertObjectValueToDataValue(cp.next(iterator, session), session, marshalRecord.getMarshaller());
NodeValue nodeValue = getNodeValueForValue(nextValue);
if(nodeValue != null){
if(nodeValue == this){
mixedNodeValue = this;
if(mixedValues == null){
mixedValues = new ArrayList();
}
mixedValues.add(nextValue);
}else{
int index = nodeValues.indexOf(nodeValue);
if(index > -1){
values.get(index).add(nextValue);
}else{
nodeValues.add(nodeValue);
List valuesList = new ArrayList();
valuesList.add(nextValue);
values.add(valuesList);
}
}
}
}
//always write out mixed values last so we can determine if the textWrapper key needs to be written.
if(mixedNodeValue != null){
nodeValues.add(mixedNodeValue);
values.add(mixedValues);
}
for(int i =0;i < nodeValues.size(); i++){
NodeValue associatedNodeValue = nodeValues.get(i);
List listValue = values.get(i);
XPathFragment frag = null;
if(associatedNodeValue == this){
frag = marshalRecord.getTextWrapperFragment();
}else{
frag = associatedNodeValue.getXPathNode().getXPathFragment();
if(frag != null){
frag = getOwningFragment(associatedNodeValue, frag);
associatedNodeValue = ((XMLChoiceCollectionMappingUnmarshalNodeValue)associatedNodeValue).getChoiceElementMarshalNodeValue();
}
}
if(frag != null || associatedNodeValue.isAnyMappingNodeValue()){
int valueSize = listValue.size();
if(valueSize > 1 || !marshalRecord.getMarshaller().isReduceAnyArrays()) {
marshalRecord.startCollection();
}
for(int j=0;j 1 || !marshalRecord.getMarshaller().isReduceAnyArrays()) {
marshalRecord.endCollection();
}
}
}
}
else{
while(cp.hasNext(iterator)) {
Object nextValue = cp.next(iterator, session);
marshalSingleValue(xPathFragment, marshalRecord, object, nextValue, session, namespaceResolver, ObjectMarshalContext.getInstance());
}
}
return true;
}
@Override
public boolean marshalSingleValue(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, Object value, CoreAbstractSession session, NamespaceResolver namespaceResolver, MarshalContext marshalContext) {
value = xmlChoiceCollectionMapping.convertObjectValueToDataValue(value, session, marshalRecord.getMarshaller());
if(value !=null && value.getClass() == CoreClassConstants.STRING && this.xmlChoiceCollectionMapping.isMixedContent()) {
marshalMixedContent(marshalRecord, (String)value);
return true;
}
NodeValue associatedNodeValue = getNodeValueForValue(value);
if(associatedNodeValue != null) {
if(associatedNodeValue.isAnyMappingNodeValue()){
//NodeValue unwrappedNodeValue = ((XMLChoiceCollectionMappingUnmarshalNodeValue)associatedNodeValue).getChoiceElementMarshalNodeValue();
return marshalSingleValueWithNodeValue(null, marshalRecord, object, value, session, namespaceResolver, marshalContext, associatedNodeValue);
}
else{
//Find the correct fragment
XPathFragment frag = associatedNodeValue.getXPathNode().getXPathFragment();
if(frag != null){
frag = getOwningFragment(associatedNodeValue, frag);
NodeValue unwrappedNodeValue = ((XMLChoiceCollectionMappingUnmarshalNodeValue)associatedNodeValue).getChoiceElementMarshalNodeValue();
return marshalSingleValueWithNodeValue(frag, marshalRecord, object, value, session, namespaceResolver, marshalContext, unwrappedNodeValue);
}
}
}
return true;
}
private boolean marshalSingleValueWithNodeValue(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, Object value, CoreAbstractSession session, NamespaceResolver namespaceResolver, MarshalContext marshalContext, NodeValue unwrappedNodeValue) {
if(unwrappedNodeValue != null){
unwrappedNodeValue.marshalSingleValue(xPathFragment, marshalRecord, object, value, session, namespaceResolver, marshalContext);
}
return true;
}
private NodeValue getNodeValueForValue(Object value){
if(value == null){
Iterator nodeValues= fieldToNodeValues.values().iterator();
while(nodeValues.hasNext()) {
XMLChoiceCollectionMappingUnmarshalNodeValue unmarshalNodeValue = (XMLChoiceCollectionMappingUnmarshalNodeValue)nodeValues.next();
NodeValue nextNodeValue = unmarshalNodeValue.getChoiceElementMarshalNodeValue();
if(nextNodeValue instanceof MappingNodeValue){
Mapping nextMapping = ((MappingNodeValue)nextNodeValue).getMapping();
if(nextMapping.isAbstractCompositeCollectionMapping()){
if(((CompositeCollectionMapping)nextMapping).getNullPolicy().isNullRepresentedByXsiNil()){
return unmarshalNodeValue;
}
}else if(nextMapping.isAbstractCompositeDirectCollectionMapping()){
if(((DirectCollectionMapping)nextMapping).getNullPolicy().isNullRepresentedByXsiNil()){
return unmarshalNodeValue;
}
}else if(nextMapping instanceof BinaryDataCollectionMapping){
if(((BinaryDataCollectionMapping)nextMapping).getNullPolicy().isNullRepresentedByXsiNil()){
return unmarshalNodeValue;
}
}
}
}
return null;
}
Field associatedField = null;
NodeValue nodeValue = null;
if(value instanceof Root) {
Root rootValue = (Root)value;
String localName = rootValue.getLocalName();
String namespaceUri = rootValue.getNamespaceURI();
Object fieldValue = rootValue.getObject();
associatedField = getFieldForName(localName, namespaceUri);
if(associatedField == null) {
if(xmlChoiceCollectionMapping.isAny()) {
return this.anyNodeValue;
}
Class> theClass = fieldValue.getClass();
while(associatedField == null) {
associatedField = (Field) xmlChoiceCollectionMapping.getClassToFieldMappings().get(theClass);
if(theClass.getSuperclass() != null) {
theClass = theClass.getSuperclass();
} else {
break;
}
}
}
if(associatedField != null) {
nodeValue = this.fieldToNodeValues.get(associatedField);
}
} else {
Class> theClass = value.getClass();
while(associatedField == null) {
associatedField = (Field) xmlChoiceCollectionMapping.getClassToFieldMappings().get(theClass);
List fieldNodes = classToNodeValues.get(theClass);
nodeValue = null;
if (null != fieldNodes) {
//match also field
if (null != associatedField && fieldNodes.size() > 1) {
for (FieldNodeValue fieldNode : fieldNodes) {
if (fieldNode.getField().equals(associatedField)) {
nodeValue = fieldNode.getNodeValue();
}
}
}
if (null == nodeValue && fieldNodes.size() > 0) {
nodeValue = fieldNodes.get(0).getNodeValue();
}
}
if(theClass.getSuperclass() != null) {
theClass = theClass.getSuperclass();
} else {
break;
}
}
}
if(associatedField == null) {
//check the field associations
List sourceFields = null;
Class> theClass = value.getClass();
while(theClass != null) {
sourceFields = (List) xmlChoiceCollectionMapping.getClassToSourceFieldsMappings().get(theClass);
if(sourceFields != null) {
break;
}
theClass = theClass.getSuperclass();
}
if(sourceFields != null) {
associatedField = sourceFields.get(0);
nodeValue = fieldToNodeValues.get(associatedField);
}
}
if(nodeValue != null){
return nodeValue;
}
if(associatedField != null) {
return fieldToNodeValues.get(associatedField);
}
if (xmlChoiceCollectionMapping.isMixedContent() && value instanceof String){
//use this as a placeholder for the nodevalue for mixedcontent
return this;
}
if (xmlChoiceCollectionMapping.isAny()){
return anyNodeValue;
}
return null;
}
private XPathFragment getOwningFragment(NodeValue nodeValue, XPathFragment frag){
while(frag != null) {
if(nodeValue.isOwningNode(frag)) {
return frag;
}
frag = frag.getNextFragment();
}
return null;
}
private void marshalMixedContent(MarshalRecord record, String value) {
record.characters(value);
}
private Field getFieldForName(String localName, String namespaceUri) {
Iterator fields = fieldToNodeValues.keySet().iterator();
while(fields.hasNext()) {
Field nextField = fields.next();
if(nextField != null){
XPathFragment fragment = nextField.getXPathFragment();
while(fragment != null && (!fragment.nameIsText())) {
if(fragment.getNextFragment() == null || fragment.getHasText()) {
if(fragment.getLocalName().equals(localName)) {
String fragUri = fragment.getNamespaceURI();
if((namespaceUri == null && fragUri == null) || (namespaceUri != null && fragUri != null && namespaceUri.equals(fragUri))) {
return nextField;
}
}
}
fragment = fragment.getNextFragment();
}
}
}
return null;
}
public Collection getAllNodeValues() {
return this.fieldToNodeValues.values();
}
@Override
public boolean isMarshalNodeValue() {
return true;
}
@Override
public boolean isUnmarshalNodeValue() {
return false;
}
@Override
public boolean isWrapperAllowedAsCollectionName() {
return false;
}
@Override
public Object getContainerInstance() {
return getContainerPolicy().containerInstance();
}
@Override
public void setContainerInstance(Object object, Object containerInstance) {
xmlChoiceCollectionMapping.setAttributeValueInObject(object, containerInstance);
}
@Override
public CoreContainerPolicy getContainerPolicy() {
return xmlChoiceCollectionMapping.getContainerPolicy();
}
@Override
public boolean isContainerValue() {
return true;
}
@Override
public ChoiceCollectionMapping getMapping() {
return xmlChoiceCollectionMapping;
}
@Override
public boolean getReuseContainer() {
return getMapping().getReuseContainer();
}
/**
* INTERNAL:
* Indicates that this is the choice mapping node value that represents the mixed content.
*/
public void setIsMixedNodeValue(boolean b) {
this.isMixedNodeValue = b;
}
/**
* INTERNAL:
* Return true if this is the node value representing mixed content.
*/
@Override
public boolean isMixedContentNodeValue() {
return this.isMixedNodeValue;
}
/**
* 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 getMapping().isDefaultEmptyContainer();
}
@Override
public void setXPathNode(XPathNode xPathNode) {
super.setXPathNode(xPathNode);
if(this.anyNodeValue != null) {
this.anyNodeValue.setXPathNode(xPathNode);
}
}
}