| /* |
| * Copyright (c) 2013, 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: |
| // Blaise Doughan - 2.5 - initial implementation |
| package org.eclipse.persistence.internal.oxm; |
| |
| import java.lang.reflect.Method; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map.Entry; |
| |
| import javax.xml.namespace.QName; |
| |
| import org.eclipse.persistence.core.descriptors.CoreDescriptor; |
| import org.eclipse.persistence.core.mappings.CoreMapping; |
| import org.eclipse.persistence.core.mappings.transformers.CoreFieldTransformer; |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| import org.eclipse.persistence.exceptions.XMLMarshalException; |
| import org.eclipse.persistence.internal.core.descriptors.CoreObjectBuilder; |
| import org.eclipse.persistence.internal.core.helper.CoreField; |
| import org.eclipse.persistence.internal.core.sessions.CoreAbstractRecord; |
| import org.eclipse.persistence.internal.core.sessions.CoreAbstractSession; |
| import org.eclipse.persistence.internal.helper.DatabaseField; |
| import org.eclipse.persistence.internal.oxm.mappings.AnyAttributeMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.AnyCollectionMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.AnyObjectMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.BinaryDataCollectionMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.BinaryDataMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.ChoiceCollectionMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.ChoiceObjectMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.CollectionReferenceMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.CompositeCollectionMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.CompositeObjectMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.Descriptor; |
| import org.eclipse.persistence.internal.oxm.mappings.DirectCollectionMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.DirectMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.Field; |
| import org.eclipse.persistence.internal.oxm.mappings.FragmentCollectionMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.FragmentMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.InverseReferenceMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.Mapping; |
| import org.eclipse.persistence.internal.oxm.mappings.ObjectReferenceMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.TransformationMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.VariableXPathCollectionMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.VariableXPathObjectMapping; |
| import org.eclipse.persistence.internal.oxm.record.AbstractMarshalRecord; |
| 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.SequencedMarshalContext; |
| import org.eclipse.persistence.internal.oxm.record.UnmarshalRecord; |
| import org.eclipse.persistence.internal.oxm.record.UnmarshalRecordImpl; |
| import org.eclipse.persistence.internal.oxm.record.XMLRecord; |
| import org.eclipse.persistence.internal.queries.ContainerPolicy; |
| import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| import org.eclipse.persistence.mappings.AttributeAccessor; |
| import org.eclipse.persistence.mappings.converters.Converter; |
| import org.eclipse.persistence.oxm.XMLContext; |
| import org.eclipse.persistence.oxm.XMLMarshaller; |
| import org.eclipse.persistence.oxm.XMLUnmarshaller; |
| import org.eclipse.persistence.oxm.mappings.UnmarshalKeepAsElementPolicy; |
| import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping; |
| import org.eclipse.persistence.oxm.sequenced.SequencedObject; |
| import org.eclipse.persistence.sessions.Session; |
| |
| public class XPathObjectBuilder extends CoreObjectBuilder<CoreAbstractRecord, CoreAbstractSession, CoreField, CoreMapping> implements ObjectBuilder { |
| |
| public static final String CYCLE_RECOVERABLE = "org.glassfish.jaxb.runtime.CycleRecoverable"; |
| public static final String CYCLE_RECOVERABLE_CONTEXT = "org.glassfish.jaxb.runtime.CycleRecoverable$Context"; |
| private static final String CYCLE_RECOVERABLE_OLD = "com.sun.xml.bind.CycleRecoverable"; |
| private static final String CYCLE_RECOVERABLE_CONTEXT_OLD = "com.sun.xml.bind.CycleRecoverable$Context"; |
| public static final String ON_CYCLE_DETECTED = "onCycleDetected"; |
| |
| private List<ContainerValue> containerValues; |
| private int counter = 0; |
| private Class cycleRecoverableClass = null; |
| private Class cycleRecoverableContextClass = null; |
| private List<ContainerValue> defaultEmptyContainerValues; //a list of container values that have isDefaultEmptyContainer() set to true |
| private CoreDescriptor descriptor; |
| private volatile boolean initialized = false; |
| private List<NullCapableValue> nullCapableValues; |
| private XPathNode rootXPathNode; |
| private List<TransformationMapping> transformationMappings; |
| private boolean xsiTypeIndicatorField; |
| |
| public XPathObjectBuilder(CoreDescriptor descriptor) { |
| this.descriptor = descriptor; |
| this.rootXPathNode = new XPathNode(); |
| } |
| |
| private XPathNode addChild(XPathFragment xPathFragment, NodeValue nodeValue, NamespaceResolver namespaceResolver) { |
| return rootXPathNode.addChild(xPathFragment, nodeValue, namespaceResolver); |
| } |
| |
| @Override |
| public boolean addClassIndicatorFieldToRow(AbstractMarshalRecord abstractMarshalRecord) { |
| if (descriptor.hasInheritance() && !xsiTypeIndicatorField) { |
| descriptor.getInheritancePolicy().addClassIndicatorFieldToRow((CoreAbstractRecord) abstractMarshalRecord); |
| return true; |
| } |
| return false; |
| } |
| |
| private void addContainerValue(ContainerValue containerValue) { |
| if (null == this.containerValues) { |
| this.containerValues = new ArrayList(); |
| } |
| containerValue.setIndex(counter++); |
| this.containerValues.add(containerValue); |
| |
| if(containerValue.isDefaultEmptyContainer()){ |
| addDefaultEmptyContainerValue(containerValue); |
| } |
| } |
| |
| private void addDefaultEmptyContainerValue(ContainerValue containerValue){ |
| if (null == this.defaultEmptyContainerValues) { |
| this.defaultEmptyContainerValues = new ArrayList(); |
| } |
| this.defaultEmptyContainerValues.add(containerValue); |
| } |
| |
| @Override |
| public List<Namespace> addExtraNamespacesToNamespaceResolver(Descriptor desc, AbstractMarshalRecord marshalRecord, CoreAbstractSession session, boolean allowOverride, boolean ignoreEqualResolvers) { |
| if (rootXPathNode.getNonAttributeChildren() == null) { |
| return null; |
| } else { |
| return marshalRecord.addExtraNamespacesToNamespaceResolver(desc, session, allowOverride, ignoreEqualResolvers); |
| } |
| } |
| |
| private void addNullCapableValue(NullCapableValue nullCapableValue) { |
| if (null == this.nullCapableValues) { |
| this.nullCapableValues = new ArrayList(); |
| } |
| this.nullCapableValues.add(nullCapableValue); |
| } |
| |
| public void addTransformationMapping(TransformationMapping transformationMapping) { |
| if (null == this.transformationMappings) { |
| this.transformationMappings = new ArrayList(); |
| } |
| transformationMappings.add(transformationMapping); |
| } |
| |
| /** |
| * Return a new instance of the receiver's javaClass. |
| */ |
| @Override |
| public Object buildNewInstance() { |
| return this.descriptor.getInstantiationPolicy().buildNewInstance(); |
| } |
| |
| @Override |
| public XMLRecord buildRow(XMLRecord record, Object object, |
| CoreAbstractSession session, Marshaller marshaller, |
| XPathFragment rootFragment) { |
| lazyInitialize(); |
| XPathNode textNode = rootXPathNode.getTextNode(); |
| List<XPathNode> nonAttributeChildren = rootXPathNode.getNonAttributeChildren(); |
| if (null == textNode && null == nonAttributeChildren) { |
| return record; |
| } |
| |
| Descriptor xmlDescriptor = (Descriptor) descriptor; |
| XPathNode node = rootXPathNode; |
| MarshalRecord marshalRecord = (MarshalRecord) record; |
| QName schemaType = null; |
| |
| if (marshalRecord.getCycleDetectionStack().contains(object, marshaller.isEqualUsingIdenity())) { |
| if (cycleRecoverableClass == null) { |
| initCycleRecoverableClasses(); |
| } |
| if (cycleRecoverableClass != null && cycleRecoverableClass.isAssignableFrom(object.getClass())) { |
| try { |
| Object jaxbMarshaller = marshaller.getProperty(Constants.JAXB_MARSHALLER); |
| // Create a proxy instance of CycleRecoverable$Context, a parameter to |
| // the onCycleDetected method |
| Object contextProxy = CycleRecoverableContextProxy.getProxy(cycleRecoverableContextClass, jaxbMarshaller); |
| // Invoke onCycleDetected method, passing in proxy, and reset |
| // 'object' to the returned value |
| Method onCycleDetectedMethod = object.getClass().getMethod(ON_CYCLE_DETECTED, new Class[] { cycleRecoverableContextClass }); |
| object = PrivilegedAccessHelper.invokeMethod(onCycleDetectedMethod, object, new Object[] { contextProxy }); |
| } catch (Exception e) { |
| throw XMLMarshalException.marshalException(e); |
| } |
| |
| // Returned object might have a different descriptor |
| xmlDescriptor = (Descriptor) session.getDescriptor(object.getClass()); |
| if (xmlDescriptor != null) { |
| node = ((ObjectBuilder) xmlDescriptor.getObjectBuilder()).getRootXPathNode(); |
| } else { |
| node = null; |
| } |
| |
| // Push new object |
| marshalRecord.getCycleDetectionStack().push(object); |
| |
| // Write xsi:type if onCycleDetected returned an object of a type different than the one mapped |
| if (xmlDescriptor != descriptor) { |
| if (xmlDescriptor == null) { |
| schemaType = record.getConversionManager().schemaType(object.getClass()); |
| } else { |
| schemaType = xmlDescriptor.getSchemaReference().getSchemaContextAsQName(); |
| } |
| marshalRecord.writeXsiTypeAttribute(xmlDescriptor, schemaType.getNamespaceURI(), schemaType.getLocalPart(), schemaType.getPrefix(), false); |
| } |
| } else { |
| // Push the duplicate object anyway, so that we can get the complete cycle string |
| marshalRecord.getCycleDetectionStack().push(object); |
| throw XMLMarshalException.objectCycleDetected(marshalRecord.getCycleDetectionStack().getCycleString()); |
| } |
| } else { |
| marshalRecord.getCycleDetectionStack().push(object); |
| } |
| |
| NamespaceResolver namespaceResolver = null; |
| if (xmlDescriptor != null) { |
| namespaceResolver = xmlDescriptor.getNamespaceResolver(); |
| } |
| MarshalContext marshalContext = null; |
| if (xmlDescriptor != null && xmlDescriptor.isSequencedObject()) { |
| SequencedObject sequencedObject = (SequencedObject) object; |
| marshalContext = new SequencedMarshalContext(sequencedObject.getSettings()); |
| } else { |
| marshalContext = ObjectMarshalContext.getInstance(); |
| } |
| if (null == nonAttributeChildren) { |
| textNode.marshal((MarshalRecord) record, object, session, namespaceResolver, marshaller, marshalContext, rootFragment); |
| } else { |
| if (node == null) { |
| // No descriptor for this object, so manually create a MappingNodeValue and marshal it |
| XPathNode n = new XPathNode(); |
| CompositeObjectMapping<AbstractSession, AttributeAccessor, ContainerPolicy, Converter, ClassDescriptor, DatabaseField, XMLMarshaller, Session, UnmarshalKeepAsElementPolicy, XMLUnmarshaller, org.eclipse.persistence.oxm.record.XMLRecord> m = new XMLCompositeObjectMapping(); |
| m.setXPath("."); |
| XMLCompositeObjectMappingNodeValue nv = new XMLCompositeObjectMappingNodeValue(m); |
| n.setMarshalNodeValue(nv); |
| nv.marshalSingleValue(new XPathFragment("."), marshalRecord, null, object, session, namespaceResolver, marshalContext); |
| } else { |
| for (int x = 0, size = marshalContext.getNonAttributeChildrenSize(node); x < size; x++) { |
| XPathNode xPathNode = (XPathNode) marshalContext.getNonAttributeChild(x, node); |
| xPathNode.marshal((MarshalRecord) record, object, session, namespaceResolver, marshaller, marshalContext.getMarshalContext(x), rootFragment); |
| } |
| } |
| } |
| marshalRecord.getCycleDetectionStack().pop(); |
| return record; |
| } |
| |
| @Override |
| public Class classFromRow(UnmarshalRecord record, CoreAbstractSession session) { |
| return descriptor.getInheritancePolicy().classFromRow((CoreAbstractRecord) record, session); |
| } |
| |
| /** |
| * Create a new row/record for the object builder. |
| * This allows subclasses to define different record types. |
| */ |
| @Override |
| public CoreAbstractRecord createRecord(CoreAbstractSession session) { |
| UnmarshalRecordImpl record = new UnmarshalRecordImpl(this); |
| record.setSession(session); |
| return record; |
| } |
| |
| @Override |
| public Object extractPrimaryKeyFromObject(Object object, CoreAbstractSession session) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public List<ContainerValue> getContainerValues() { |
| return this.containerValues; |
| } |
| |
| @Override |
| public List<ContainerValue> getDefaultEmptyContainerValues() { |
| return this.defaultEmptyContainerValues; |
| } |
| |
| @Override |
| public CoreDescriptor getDescriptor() { |
| return descriptor; |
| } |
| |
| @Override |
| public CoreMapping getMappingForField(CoreField field) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public List<NullCapableValue> getNullCapableValues() { |
| return this.nullCapableValues; |
| } |
| |
| @Override |
| public XPathNode getRootXPathNode() { |
| lazyInitialize(); |
| return this.rootXPathNode; |
| } |
| |
| @Override |
| public List<TransformationMapping> getTransformationMappings() { |
| return this.transformationMappings; |
| } |
| |
| private void initCycleRecoverableClasses() { |
| try { |
| this.cycleRecoverableClass = PrivilegedAccessHelper.getClassForName(CYCLE_RECOVERABLE); |
| this.cycleRecoverableContextClass = PrivilegedAccessHelper.getClassForName(CYCLE_RECOVERABLE_CONTEXT); |
| } catch (Throwable e) { |
| //try old one |
| try { |
| this.cycleRecoverableClass = PrivilegedAccessHelper.getClassForName(CYCLE_RECOVERABLE_OLD); |
| this.cycleRecoverableContextClass = PrivilegedAccessHelper.getClassForName(CYCLE_RECOVERABLE_CONTEXT_OLD); |
| } catch (Throwable e2) { |
| //ignore |
| } |
| } |
| } |
| |
| @Override |
| public boolean isXsiTypeIndicatorField() { |
| return xsiTypeIndicatorField; |
| } |
| |
| void lazyInitialize() { |
| if(initialized) { |
| return; |
| } |
| synchronized(this) { |
| if(initialized) { |
| return; |
| } |
| Descriptor xmlDescriptor = (Descriptor) descriptor; |
| |
| // MAPPINGS |
| Iterator mappingIterator = xmlDescriptor.getMappings().iterator(); |
| Iterator fieldTransformerIterator; |
| Mapping xmlMapping; |
| |
| // Transformation Mapping |
| TransformationMapping transformationMapping; |
| FieldTransformerNodeValue fieldTransformerNodeValue; |
| Object[] nextFieldToTransformer; |
| |
| // Simple Type Translator |
| TypeNodeValue typeNodeValue; |
| |
| NodeValue mappingNodeValue = null; |
| Field xmlField; |
| while (mappingIterator.hasNext()) { |
| xmlMapping = (Mapping)mappingIterator.next(); |
| |
| xmlField = (Field)xmlMapping.getField(); |
| if (xmlMapping.isTransformationMapping()) { |
| transformationMapping = (TransformationMapping)xmlMapping; |
| addTransformationMapping(transformationMapping); |
| fieldTransformerIterator = transformationMapping.getFieldToTransformers().iterator(); |
| while (fieldTransformerIterator.hasNext()) { |
| fieldTransformerNodeValue = new FieldTransformerNodeValue(transformationMapping); |
| nextFieldToTransformer = (Object[])fieldTransformerIterator.next(); |
| xmlField = (Field)nextFieldToTransformer[0]; |
| fieldTransformerNodeValue.setXMLField(xmlField); |
| fieldTransformerNodeValue.setFieldTransformer((CoreFieldTransformer)nextFieldToTransformer[1]); |
| addChild(xmlField.getXPathFragment(), fieldTransformerNodeValue, xmlDescriptor.getNamespaceResolver()); |
| } |
| } else { |
| if (xmlMapping.isAbstractDirectMapping()) { |
| mappingNodeValue = new XMLDirectMappingNodeValue((DirectMapping)xmlMapping); |
| } else if (xmlMapping.isAbstractCompositeObjectMapping()) { |
| mappingNodeValue = new XMLCompositeObjectMappingNodeValue((CompositeObjectMapping)xmlMapping); |
| } else if (xmlMapping.isAbstractCompositeCollectionMapping()) { |
| CompositeCollectionMapping collectionMapping = (CompositeCollectionMapping) xmlMapping; |
| mappingNodeValue = new XMLCompositeCollectionMappingNodeValue(collectionMapping); |
| if (collectionMapping.getWrapperNullPolicy() != null) { |
| addChild(xmlField.getXPathFragment(), new CollectionGroupingElementNodeValue((ContainerValue) mappingNodeValue), xmlDescriptor.getNamespaceResolver()); |
| } |
| } else if (xmlMapping.isAbstractCompositeDirectCollectionMapping()) { |
| DirectCollectionMapping collectionMapping = (DirectCollectionMapping) xmlMapping; |
| mappingNodeValue = new XMLCompositeDirectCollectionMappingNodeValue(collectionMapping); |
| if (collectionMapping.getWrapperNullPolicy() != null) { |
| addChild(xmlField.getXPathFragment(), new CollectionGroupingElementNodeValue((ContainerValue) mappingNodeValue), xmlDescriptor.getNamespaceResolver()); |
| } |
| } else if (xmlMapping instanceof InverseReferenceMapping) { |
| xmlMapping = (Mapping)((InverseReferenceMapping)xmlMapping).getInlineMapping(); |
| if(xmlMapping == null){ |
| continue; |
| } |
| xmlField = (Field)xmlMapping.getField(); |
| if(xmlMapping.isAbstractCompositeCollectionMapping()){ |
| mappingNodeValue=new XMLCompositeCollectionMappingNodeValue((CompositeCollectionMapping)xmlMapping, true); |
| } |
| if(xmlMapping.isAbstractCompositeObjectMapping()){ |
| mappingNodeValue=new XMLCompositeObjectMappingNodeValue((CompositeObjectMapping)xmlMapping, true); |
| } |
| |
| } else if (xmlMapping instanceof VariableXPathCollectionMapping) { |
| mappingNodeValue = new XMLVariableXPathCollectionMappingNodeValue((VariableXPathCollectionMapping)xmlMapping); |
| } else if (xmlMapping instanceof VariableXPathObjectMapping){ |
| mappingNodeValue = new XMLVariableXPathObjectMappingNodeValue((VariableXPathObjectMapping)xmlMapping); |
| } else if (xmlMapping instanceof AnyObjectMapping) { |
| mappingNodeValue = new XMLAnyObjectMappingNodeValue((AnyObjectMapping)xmlMapping); |
| } else if (xmlMapping instanceof AnyCollectionMapping) { |
| mappingNodeValue = new XMLAnyCollectionMappingNodeValue((AnyCollectionMapping)xmlMapping); |
| } else if (xmlMapping instanceof AnyAttributeMapping) { |
| mappingNodeValue = new XMLAnyAttributeMappingNodeValue((AnyAttributeMapping)xmlMapping); |
| } else if (xmlMapping instanceof BinaryDataMapping) { |
| mappingNodeValue = new XMLBinaryDataMappingNodeValue((BinaryDataMapping)xmlMapping); |
| } else if (xmlMapping instanceof BinaryDataCollectionMapping) { |
| mappingNodeValue = new XMLBinaryDataCollectionMappingNodeValue((BinaryDataCollectionMapping)xmlMapping); |
| } else if (xmlMapping instanceof FragmentMapping) { |
| mappingNodeValue = new XMLFragmentMappingNodeValue((FragmentMapping)xmlMapping); |
| } else if (xmlMapping instanceof FragmentCollectionMapping) { |
| mappingNodeValue = new XMLFragmentCollectionMappingNodeValue((FragmentCollectionMapping)xmlMapping); |
| } else if (xmlMapping instanceof CollectionReferenceMapping) { |
| CollectionReferenceMapping xmlColMapping = (CollectionReferenceMapping)xmlMapping; |
| |
| List fields = xmlColMapping.getFields(); |
| Field xmlColMappingField = (Field) xmlColMapping.getField(); |
| XPathNode branchNode; |
| if(null == xmlColMappingField) { |
| if(fields.size() > 1 && !xmlColMapping.usesSingleNode()) { |
| addChild(XPathFragment.SELF_FRAGMENT, new XMLCollectionReferenceMappingMarshalNodeValue(xmlColMapping), xmlDescriptor.getNamespaceResolver()); |
| } |
| branchNode = rootXPathNode; |
| } else { |
| branchNode = addChild(((Field) xmlColMapping.getField()).getXPathFragment(), new XMLCollectionReferenceMappingMarshalNodeValue(xmlColMapping), xmlDescriptor.getNamespaceResolver()); |
| } |
| |
| int containerIndex = -1; |
| for (int i = 0, size = fields.size(); i < size; i++) { |
| Field xmlFld = (Field)fields.get(i); |
| mappingNodeValue = new XMLCollectionReferenceMappingNodeValue(xmlColMapping, xmlFld); |
| if(i == 0){ |
| addContainerValue((ContainerValue)mappingNodeValue); |
| containerIndex = ((ContainerValue)mappingNodeValue).getIndex(); |
| }else{ |
| ((ContainerValue)mappingNodeValue).setIndex(containerIndex); |
| } |
| branchNode.addChild(xmlFld.getXPathFragment(), mappingNodeValue, xmlDescriptor.getNamespaceResolver()); |
| } |
| continue; |
| } else if (xmlMapping instanceof ObjectReferenceMapping) { |
| ObjectReferenceMapping xmlORMapping = (ObjectReferenceMapping)xmlMapping; |
| Iterator fieldIt = xmlORMapping.getFields().iterator(); |
| while (fieldIt.hasNext()) { |
| Field xmlFld = (Field)fieldIt.next(); |
| mappingNodeValue = new XMLObjectReferenceMappingNodeValue(xmlORMapping, xmlFld); |
| addChild(xmlFld.getXPathFragment(), mappingNodeValue, xmlDescriptor.getNamespaceResolver()); |
| } |
| continue; |
| } else if (xmlMapping instanceof ChoiceObjectMapping) { |
| ChoiceObjectMapping xmlChoiceMapping = (ChoiceObjectMapping)xmlMapping; |
| Iterator fields = xmlChoiceMapping.getChoiceElementMappings().keySet().iterator(); |
| Field firstField = (Field)fields.next(); |
| XMLChoiceObjectMappingNodeValue firstNodeValue = new XMLChoiceObjectMappingNodeValue(xmlChoiceMapping, firstField); |
| firstNodeValue.setNullCapableNodeValue(firstNodeValue); |
| addChild(firstField.getXPathFragment(), firstNodeValue, xmlDescriptor.getNamespaceResolver()); |
| while(fields.hasNext()) { |
| Field next = (Field)fields.next(); |
| XMLChoiceObjectMappingNodeValue nodeValue = new XMLChoiceObjectMappingNodeValue(xmlChoiceMapping, next); |
| nodeValue.setNullCapableNodeValue(firstNodeValue); |
| addChild(next.getXPathFragment(), nodeValue, xmlDescriptor.getNamespaceResolver()); |
| } |
| continue; |
| } else if(xmlMapping instanceof ChoiceCollectionMapping) { |
| ChoiceCollectionMapping xmlChoiceMapping = (ChoiceCollectionMapping)xmlMapping; |
| |
| Iterator<Entry<Field, Mapping>> fields = xmlChoiceMapping.getChoiceElementMappings().entrySet().iterator(); |
| Entry<Field, Mapping> firstEntry = fields.next(); |
| Field firstField = firstEntry.getKey(); |
| |
| XMLChoiceCollectionMappingUnmarshalNodeValue unmarshalValue = new XMLChoiceCollectionMappingUnmarshalNodeValue(xmlChoiceMapping, firstField); |
| XMLChoiceCollectionMappingMarshalNodeValue marshalValue = new XMLChoiceCollectionMappingMarshalNodeValue(xmlChoiceMapping, firstField); |
| |
| //The reason behind LinkedHashMap is the order of items when for-cycling HashMap.getEntrySet() or HashMap.getKeySet(). |
| //This change fixes non-determinism (implementation in JDK8 has changed so the order is different (sometimes) than in JDK6 and JDK7). |
| HashMap<Field, NodeValue> fieldToNodeValues = new LinkedHashMap<>(); |
| unmarshalValue.setContainerNodeValue(unmarshalValue); |
| unmarshalValue.setFieldToNodeValues(fieldToNodeValues); |
| if(xmlChoiceMapping.isMixedContent() && (xmlChoiceMapping.getMixedContentMapping() == firstEntry.getValue())) { |
| unmarshalValue.setIsMixedNodeValue(true); |
| marshalValue.setIsMixedNodeValue(true); |
| } |
| this.addContainerValue(unmarshalValue); |
| ((ContainerValue)unmarshalValue.getChoiceElementNodeValue()).setIndex(unmarshalValue.getIndex()); |
| fieldToNodeValues.put(firstField, unmarshalValue); |
| addChild(firstField.getXPathFragment(), unmarshalValue, xmlDescriptor.getNamespaceResolver()); |
| addChild(firstField.getXPathFragment(), marshalValue, xmlDescriptor.getNamespaceResolver()); |
| while(fields.hasNext()) { |
| Entry<Field, Mapping> nextEntry = fields.next(); |
| Field nextField = nextEntry.getKey(); |
| XMLChoiceCollectionMappingUnmarshalNodeValue nodeValue = new XMLChoiceCollectionMappingUnmarshalNodeValue(xmlChoiceMapping, nextField); |
| nodeValue.setContainerNodeValue(unmarshalValue); |
| nodeValue.setIndex(unmarshalValue.getIndex()); |
| ((ContainerValue)nodeValue.getChoiceElementNodeValue()).setIndex(unmarshalValue.getIndex()); |
| addChild(nextField.getXPathFragment(), nodeValue, xmlDescriptor.getNamespaceResolver()); |
| fieldToNodeValues.put(nextField, nodeValue); |
| if(xmlChoiceMapping.isMixedContent() && (xmlChoiceMapping.getMixedContentMapping() == nextEntry.getValue())) { |
| nodeValue.setIsMixedNodeValue(true); |
| } |
| } |
| if(xmlChoiceMapping.isAny()) { |
| XMLChoiceCollectionMappingUnmarshalNodeValue nodeValue = new XMLChoiceCollectionMappingUnmarshalNodeValue(xmlChoiceMapping, null, xmlChoiceMapping.getAnyMapping()); |
| nodeValue.setContainerNodeValue(unmarshalValue); |
| nodeValue.setIndex(unmarshalValue.getIndex()); |
| ((ContainerValue)nodeValue.getChoiceElementNodeValue()).setIndex(unmarshalValue.getIndex()); |
| addChild(null, nodeValue, xmlDescriptor.getNamespaceResolver()); |
| fieldToNodeValues.put(null, nodeValue); |
| if(xmlChoiceMapping.isMixedContent()) { |
| nodeValue.setIsMixedNodeValue(true); |
| } |
| } |
| marshalValue.setFieldToNodeValues(fieldToNodeValues); |
| continue; |
| } |
| if (mappingNodeValue.isContainerValue()) { |
| addContainerValue((ContainerValue)mappingNodeValue); |
| } |
| if (mappingNodeValue.isNullCapableValue()) { |
| addNullCapableValue((NullCapableValue)mappingNodeValue); |
| } |
| if (xmlField != null) { |
| addChild(xmlField.getXPathFragment(), mappingNodeValue, xmlDescriptor.getNamespaceResolver()); |
| } else { |
| addChild(null, mappingNodeValue, xmlDescriptor.getNamespaceResolver()); |
| } |
| } |
| } |
| |
| if (descriptor.hasInheritance()) { |
| Field indicatorField = (Field) descriptor |
| .getInheritancePolicy().getClassIndicatorField(); |
| if (indicatorField != null) { |
| if (indicatorField.getLastXPathFragment().getNamespaceURI() != null |
| && indicatorField |
| .getLastXPathFragment() |
| .getNamespaceURI() |
| .equals(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI) |
| && indicatorField.getLastXPathFragment() |
| .getLocalName() |
| .equals(Constants.SCHEMA_TYPE_ATTRIBUTE)) { |
| xsiTypeIndicatorField = true; |
| } |
| |
| } |
| } |
| |
| initialized = true; |
| } |
| } |
| |
| @Override |
| public boolean marshalAttributes(MarshalRecord marshalRecord, Object object, CoreAbstractSession session) { |
| lazyInitialize(); |
| boolean hasValue = false; |
| NamespaceResolver namespaceResolver = ((Descriptor)descriptor).getNamespaceResolver(); |
| |
| List<XPathNode> attributeChildren = rootXPathNode.getAttributeChildren(); |
| if (null != attributeChildren) { |
| ObjectMarshalContext objectMarshalContext = ObjectMarshalContext.getInstance(); |
| for (XPathNode anAttributeChildren : attributeChildren) { |
| hasValue = anAttributeChildren.marshal(marshalRecord, object, session, namespaceResolver, null, objectMarshalContext, null) || hasValue; |
| } |
| } |
| |
| if (rootXPathNode.getAnyAttributeNode() != null) { |
| hasValue = rootXPathNode.getAnyAttributeNode().marshal(marshalRecord, object, session, namespaceResolver, null, ObjectMarshalContext.getInstance(), null) || hasValue; |
| } |
| |
| List<XPathNode> selfChildren = rootXPathNode.getSelfChildren(); |
| if (null != selfChildren) { |
| for (XPathNode selfXPathNode : selfChildren) { |
| NodeValue marshalNodeValue = selfXPathNode.getMarshalNodeValue(); |
| if(marshalNodeValue instanceof MappingNodeValue) { |
| Mapping selfMapping = ((MappingNodeValue) marshalNodeValue).getMapping(); |
| Object value = selfMapping.getAttributeValueFromObject(object); |
| Descriptor referenceDescriptor = (Descriptor)selfMapping.getReferenceDescriptor(); |
| Descriptor valueDescriptor; |
| if(value != null && (referenceDescriptor == null || referenceDescriptor.hasInheritance())){ |
| valueDescriptor = (Descriptor)session.getDescriptor(value.getClass()); |
| } else { |
| valueDescriptor = referenceDescriptor; |
| } |
| if(null != valueDescriptor) { |
| marshalRecord.addXsiTypeAndClassIndicatorIfRequired(valueDescriptor, referenceDescriptor, (Field) selfMapping.getField(), false); |
| } |
| } |
| selfXPathNode.marshalSelfAttributes(marshalRecord, object, session, namespaceResolver, marshalRecord.getMarshaller()); |
| } |
| } |
| |
| return hasValue; |
| } |
| |
| @Override |
| public CoreAbstractRecord createRecordFromXMLContext(XMLContext context) { |
| return createRecord((AbstractSession)context.getSession()); |
| } |
| |
| } |