| /* |
| * 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.oxm.mappings; |
| |
| import java.security.AccessController; |
| import java.security.PrivilegedActionException; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.Map.Entry; |
| import java.util.Vector; |
| |
| import javax.xml.namespace.QName; |
| |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| import org.eclipse.persistence.exceptions.DatabaseException; |
| import org.eclipse.persistence.exceptions.DescriptorException; |
| import org.eclipse.persistence.exceptions.ValidationException; |
| import org.eclipse.persistence.internal.descriptors.DescriptorIterator; |
| import org.eclipse.persistence.internal.descriptors.InstanceVariableAttributeAccessor; |
| import org.eclipse.persistence.internal.helper.ClassConstants; |
| import org.eclipse.persistence.internal.helper.DatabaseField; |
| import org.eclipse.persistence.internal.identitymaps.CacheKey; |
| import org.eclipse.persistence.internal.oxm.NodeValue; |
| import org.eclipse.persistence.internal.oxm.XMLChoiceFieldToClassAssociation; |
| import org.eclipse.persistence.internal.oxm.XMLConversionManager; |
| import org.eclipse.persistence.internal.oxm.XPathFragment; |
| import org.eclipse.persistence.internal.oxm.mappings.ChoiceCollectionMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.Field; |
| import org.eclipse.persistence.internal.oxm.mappings.XMLContainerMapping; |
| import org.eclipse.persistence.internal.queries.CollectionContainerPolicy; |
| import org.eclipse.persistence.internal.queries.ContainerPolicy; |
| import org.eclipse.persistence.internal.queries.JoinedAttributeManager; |
| import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; |
| import org.eclipse.persistence.internal.security.PrivilegedClassForName; |
| import org.eclipse.persistence.internal.sessions.AbstractRecord; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| import org.eclipse.persistence.internal.sessions.ChangeRecord; |
| import org.eclipse.persistence.internal.sessions.MergeManager; |
| import org.eclipse.persistence.internal.sessions.ObjectChangeSet; |
| import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl; |
| import org.eclipse.persistence.internal.sessions.remote.ObjectDescriptor; |
| import org.eclipse.persistence.mappings.AttributeAccessor; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| import org.eclipse.persistence.mappings.converters.Converter; |
| import org.eclipse.persistence.mappings.foundation.AbstractCompositeCollectionMapping; |
| import org.eclipse.persistence.mappings.foundation.AbstractCompositeDirectCollectionMapping; |
| import org.eclipse.persistence.oxm.XMLField; |
| import org.eclipse.persistence.oxm.XMLMarshaller; |
| import org.eclipse.persistence.oxm.XMLRoot; |
| import org.eclipse.persistence.oxm.XMLUnmarshaller; |
| import org.eclipse.persistence.oxm.mappings.converters.XMLConverter; |
| import org.eclipse.persistence.oxm.mappings.nullpolicy.AbstractNullPolicy; |
| import org.eclipse.persistence.oxm.record.DOMRecord; |
| import org.eclipse.persistence.oxm.record.XMLEntry; |
| import org.eclipse.persistence.oxm.record.XMLRecord; |
| import org.eclipse.persistence.queries.ObjectBuildingQuery; |
| import org.eclipse.persistence.queries.ObjectLevelReadQuery; |
| import org.eclipse.persistence.sessions.Session; |
| import org.eclipse.persistence.sessions.remote.DistributedSession; |
| |
| /** |
| * PUBLIC: |
| * <p><b>Purpose:</b>Provide a mapping that can map a single attribute to a number of |
| * different elements in an XML Document. This will be used to map to Choices or Substitution |
| * Groups in an XML Schema |
| * <p><b>Responsibilities:</b><ul> |
| * <li>Allow the user to specify XPath {@literal ->} Type mappings</li> |
| * <li>Handle reading and writing of XML Documents containing a collection of choice or substitution |
| * group elements</li> |
| * </ul> |
| * <p>The XMLChoiceCollectionMapping is the collection version of the XMLChoiceMapping. This mapping |
| * allows the user to specify a number of different xpaths, and types associated with those xpaths. |
| * When any of these elements are encountered in the XML Document, they are read in as the correct |
| * type and added to the collection. |
| * <p><b>Setting up XPath mappings:</b>Unlike other OXM Mappings, instead of setting a single xpath, |
| * the addChoiceElement method is used to specify an xpath and the type associated with this xpath. |
| * <br> |
| * xmlChoiceCollectionMapping.addChoiceElement("mystring/text()", String.class); |
| * <br> |
| * xmlChoiceCollectionMapping.addChoiceElement("myaddress", Address.class); |
| * |
| */ |
| |
| public class XMLChoiceCollectionMapping extends DatabaseMapping implements ChoiceCollectionMapping<AbstractSession, AttributeAccessor, ContainerPolicy, Converter, ClassDescriptor, DatabaseField, XMLMarshaller, Session, XMLUnmarshaller, XMLField, XMLMapping, XMLRecord>, XMLMapping { |
| private Map<XMLField, Class<?>> fieldToClassMappings; |
| private Map<Class<?>, XMLField> classToFieldMappings; |
| private Map<Class<?>, List<XMLField>> classToSourceFieldsMappings; |
| private Map<String, List<XMLField>> classNameToSourceFieldsMappings; |
| private Map<XMLField, XMLMapping> choiceElementMappings; |
| private Map<String, XMLMapping> choiceElementMappingsByClassName; |
| private Map<Class<?>, XMLMapping> choiceElementMappingsByClass; |
| private Map<XMLField, String> fieldToClassNameMappings; |
| private Map<String, XMLField> classNameToFieldMappings; |
| private Map<XMLField, Converter> fieldsToConverters; |
| private ContainerPolicy containerPolicy; |
| private boolean isDefaultEmptyContainer = XMLContainerMapping.EMPTY_CONTAINER_DEFAULT; |
| private boolean isMixedContent; |
| private String mixedGroupingElement; |
| private AbstractNullPolicy wrapperNullPolicy; |
| |
| private boolean isAny; |
| private boolean isWriteOnly; |
| private static final AttributeAccessor temporaryAccessor = new InstanceVariableAttributeAccessor(); |
| private boolean reuseContainer; |
| private Converter converter; |
| private XMLCompositeDirectCollectionMapping mixedContentMapping; |
| private XMLAnyCollectionMapping anyMapping; |
| |
| private static final String DATA_HANDLER = "jakarta.activation.DataHandler"; |
| private static final String MIME_MULTIPART = "jakarta.mail.internet.MimeMultipart"; |
| private static final String IMAGE = "java.awt.Image"; |
| |
| public XMLChoiceCollectionMapping() { |
| fieldToClassMappings = new HashMap<>(); |
| fieldToClassNameMappings = new HashMap<>(); |
| classToFieldMappings = new HashMap<>(); |
| classNameToFieldMappings = new HashMap<>(); |
| choiceElementMappings = new LinkedHashMap<>(); |
| fieldsToConverters = new HashMap<>(); |
| choiceElementMappingsByClassName = new LinkedHashMap<>(); |
| choiceElementMappingsByClass = new LinkedHashMap<>(); |
| this.containerPolicy = ContainerPolicy.buildDefaultPolicy(); |
| } |
| |
| /** |
| * Return the converter on the mapping. |
| * A converter can be used to convert between the object's value and database value of the attribute. |
| */ |
| @Override |
| public Converter getConverter() { |
| return converter; |
| } |
| |
| /** |
| * Set the converter on the mapping. |
| * A converter can be used to convert between the object's value and database value of the attribute. |
| */ |
| @Override |
| public void setConverter(Converter converter) { |
| this.converter = converter; |
| } |
| |
| /** |
| * INTERNAL: |
| * Clone the attribute from the clone and assign it to the backup. |
| */ |
| @Override |
| public void buildBackupClone(Object clone, Object backup, UnitOfWorkImpl unitOfWork) { |
| throw DescriptorException.invalidMappingOperation(this, "buildBackupClone"); |
| } |
| |
| /** |
| * INTERNAL: |
| * Clone the attribute from the original and assign it to the clone. |
| */ |
| @Override |
| public void buildClone(Object original, CacheKey cacheKey, Object clone, Integer refreshCascade, AbstractSession cloningSession) { |
| throw DescriptorException.invalidMappingOperation(this, "buildClone"); |
| } |
| |
| @Override |
| public void buildCloneFromRow(AbstractRecord databaseRow, JoinedAttributeManager joinManager, Object clone, CacheKey sharedCacheKey, ObjectBuildingQuery sourceQuery, UnitOfWorkImpl unitOfWork, AbstractSession executionSession) { |
| throw DescriptorException.invalidMappingOperation(this, "buildCloneFromRow"); |
| } |
| |
| /** |
| * INTERNAL: |
| * Cascade perform delete through mappings that require the cascade |
| */ |
| @Override |
| public void cascadePerformRemoveIfRequired(Object object, UnitOfWorkImpl uow, Map visitedObjects) { |
| //objects referenced by this mapping are not registered as they have |
| // no identity, this is a no-op. |
| } |
| |
| /** |
| * INTERNAL: |
| * Cascade registerNew for Create through mappings that require the cascade |
| */ |
| @Override |
| public void cascadeRegisterNewIfRequired(Object object, UnitOfWorkImpl uow, Map visitedObjects) { |
| //Our current XML support does not make use of the UNitOfWork. |
| } |
| |
| /** |
| * INTERNAL: |
| * This method was created in VisualAge. |
| * @return prototype.changeset.ChangeRecord |
| */ |
| @Override |
| public ChangeRecord compareForChange(Object clone, Object backup, ObjectChangeSet owner, AbstractSession session) { |
| throw DescriptorException.invalidMappingOperation(this, "compareForChange"); |
| } |
| |
| /** |
| * INTERNAL: |
| * Compare the attributes belonging to this mapping for the objects. |
| */ |
| @Override |
| public boolean compareObjects(Object firstObject, Object secondObject, AbstractSession session) { |
| throw DescriptorException.invalidMappingOperation(this, "compareObjects"); |
| } |
| |
| /** |
| * INTERNAL: |
| * An object has been serialized from the server to the client. |
| * Replace the transient attributes of the remote value holders |
| * with client-side objects. |
| */ |
| @Override |
| public void fixObjectReferences(Object object, Map<Object, ObjectDescriptor> objectDescriptors, Map<Object, Object> processedObjects, ObjectLevelReadQuery query, DistributedSession session) { |
| throw DescriptorException.invalidMappingOperation(this, "fixObjectReferences"); |
| } |
| |
| /** |
| * INTERNAL: |
| * Iterate on the appropriate attribute value. |
| */ |
| @Override |
| public void iterate(DescriptorIterator iterator) { |
| throw DescriptorException.invalidMappingOperation(this, "iterate"); |
| } |
| |
| /** |
| * INTERNAL: |
| * Merge changes from the source to the target object. |
| */ |
| @Override |
| public void mergeChangesIntoObject(Object target, ChangeRecord changeRecord, Object source, MergeManager mergeManager, AbstractSession targetSession) { |
| throw DescriptorException.invalidMappingOperation(this, "mergeChangesIntoObject"); |
| } |
| |
| /** |
| * INTERNAL: |
| * Merge changes from the source to the target object. |
| */ |
| @Override |
| public void mergeIntoObject(Object target, boolean isTargetUninitialized, Object source, MergeManager mergeManager, AbstractSession targetSession) { |
| throw DescriptorException.invalidMappingOperation(this, "mergeIntoObject"); |
| } |
| |
| @Override |
| public Object valueFromRow(AbstractRecord row, JoinedAttributeManager joinManager, ObjectBuildingQuery sourceQuery, CacheKey cacheKey, AbstractSession executionSession, boolean isTargetProtected, Boolean[] wasCacheUsed) throws DatabaseException { |
| List<XMLEntry> values = ((DOMRecord)row).getValuesIndicatingNoEntry(this.getFields()); |
| Object container = getContainerPolicy().containerInstance(values.size()); |
| for(XMLEntry next:values) { |
| Field valueField = next.getXMLField(); |
| DatabaseMapping nextMapping = (DatabaseMapping)this.choiceElementMappings.get(valueField); |
| if(nextMapping.isAbstractCompositeCollectionMapping()) { |
| XMLCompositeCollectionMapping xmlMapping = (XMLCompositeCollectionMapping)nextMapping; |
| Object value = xmlMapping.buildObjectFromNestedRow((AbstractRecord)next.getValue(), joinManager, sourceQuery, executionSession, isTargetProtected); |
| value = convertDataValueToObjectValue(value, executionSession, ((XMLRecord) row).getUnmarshaller()); |
| getContainerPolicy().addInto(value, container, executionSession); |
| } else if(nextMapping instanceof XMLCompositeDirectCollectionMapping){ |
| XMLCompositeDirectCollectionMapping xmlMapping = (XMLCompositeDirectCollectionMapping)nextMapping; |
| Object value = next.getValue(); |
| value = convertDataValueToObjectValue(value, executionSession, ((XMLRecord) row).getUnmarshaller()); |
| getContainerPolicy().addInto(value, container, executionSession); |
| } |
| } |
| ArrayList<XMLMapping> processedMappings = new ArrayList<>(); |
| for(XMLMapping mapping:choiceElementMappings.values()) { |
| if(((DatabaseMapping)mapping).isObjectReferenceMapping() && ((DatabaseMapping)mapping).isCollectionMapping() && !(processedMappings.contains(mapping))) { |
| ((XMLCollectionReferenceMapping)mapping).readFromRowIntoObject(row, joinManager, ((XMLRecord)row).getCurrentObject(), cacheKey, sourceQuery, executionSession, isTargetProtected, container); |
| processedMappings.add(mapping); |
| } |
| } |
| return container; |
| } |
| |
| @Override |
| public void writeFromObjectIntoRow(Object object, AbstractRecord row, AbstractSession session, WriteType writeType) throws DescriptorException { |
| if(this.isReadOnly()) { |
| return; |
| } |
| Object attributeValue = getAttributeValueFromObject(object); |
| List<XMLEntry> nestedRows = new ArrayList<>(); |
| XMLRecord record = (XMLRecord)row; |
| //First determine which Field is associated with each value: |
| if(null != attributeValue) { |
| ContainerPolicy cp = getContainerPolicy(); |
| Object iterator = cp.iteratorFor(attributeValue); |
| if(null != iterator) { |
| while(cp.hasNext(iterator)) { |
| Object value = cp.next(iterator, session); |
| value = convertObjectValueToDataValue(value, session, record.getMarshaller()); |
| NodeValue associatedNodeValue = null; |
| XMLField associatedField = null; |
| Object fieldValue = value; |
| if(value instanceof XMLRoot) { |
| XMLRoot rootValue = (XMLRoot)value; |
| String localName = rootValue.getLocalName(); |
| String namespaceUri = rootValue.getNamespaceURI(); |
| fieldValue = rootValue.getObject(); |
| associatedField = getFieldForName(localName, namespaceUri); |
| if(associatedField == null) { |
| associatedField = getClassToFieldMappings().get(fieldValue.getClass()); |
| } |
| } else { |
| associatedField = getClassToFieldMappings().get(value.getClass()); |
| } |
| if(associatedField == null) { |
| //this may be a reference mapping |
| List<XMLField> sourceFields = classToSourceFieldsMappings.get(value.getClass()); |
| if(sourceFields != null && sourceFields.size() > 0) { |
| DatabaseMapping xmlMapping = (DatabaseMapping)this.choiceElementMappings.get(sourceFields.get(0)); |
| for(XMLField next:sourceFields) { |
| fieldValue = ((XMLCollectionReferenceMapping)xmlMapping).buildFieldValue(value, next, session); |
| XMLEntry entry = new XMLEntry(); |
| entry.setValue(fieldValue); |
| entry.setXMLField(next); |
| nestedRows.add(entry); |
| } |
| } |
| } else { |
| DatabaseMapping xmlMapping = (DatabaseMapping)this.choiceElementMappings.get(associatedField); |
| if(xmlMapping.isAbstractCompositeCollectionMapping()) { |
| fieldValue = ((XMLCompositeCollectionMapping)xmlMapping).buildCompositeRow(fieldValue, session, row, writeType); |
| } |
| |
| XMLEntry entry = new XMLEntry(); |
| entry.setValue(fieldValue); |
| entry.setXMLField(associatedField); |
| nestedRows.add(entry); |
| } |
| } |
| } |
| } |
| row.put(getFields(), nestedRows); |
| } |
| |
| private XMLField getFieldForName(String localName, String namespaceUri) { |
| Iterator<DatabaseField> fields = getFields().iterator(); |
| while(fields.hasNext()) { |
| XMLField nextField = (XMLField)fields.next(); |
| 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; |
| } |
| @Override |
| public void writeSingleValue(Object value, Object parent, XMLRecord row, AbstractSession session) { |
| |
| } |
| |
| @Override |
| public boolean isXMLMapping() { |
| return true; |
| } |
| |
| @Override |
| public Vector<DatabaseField> getFields() { |
| if(fields == null || fields.size() == 0) { |
| fields = this.collectFields(); |
| } |
| return this.fields; |
| } |
| |
| @Override |
| protected Vector<DatabaseField> collectFields() { |
| return new Vector<>(fieldToClassMappings.keySet()); |
| } |
| |
| public void addChoiceElement(String xpath, Class<?> elementType) { |
| XMLField field = new XMLField(xpath); |
| addChoiceElement(field, elementType); |
| } |
| |
| @Override |
| public void addChoiceElement(String xpath, String elementTypeName) { |
| XMLField field = new XMLField(xpath); |
| addChoiceElement(field, elementTypeName); |
| } |
| |
| |
| public void addChoiceElement(XMLField xmlField, Class<?> elementType) { |
| getFieldToClassMappings().put(xmlField, elementType); |
| if(!(this.fieldToClassNameMappings.containsKey(xmlField))) { |
| this.fieldToClassNameMappings.put(xmlField, elementType.getName()); |
| } |
| if (classToFieldMappings.get(elementType) == null) { |
| classToFieldMappings.put(elementType, xmlField); |
| } |
| addChoiceElementMapping(xmlField, elementType); |
| } |
| |
| public void addChoiceElement(List<XMLField> srcFields, Class<?> elementType, List<XMLField> tgtFields) { |
| for(XMLField sourceField:srcFields) { |
| getFieldToClassMappings().put(sourceField, elementType); |
| this.fieldToClassNameMappings.put(sourceField, elementType.getName()); |
| } |
| if (getClassToSourceFieldsMappings().get(elementType) == null) { |
| getClassToSourceFieldsMappings().put(elementType, srcFields); |
| } |
| addChoiceElementMapping(srcFields, elementType, tgtFields); |
| } |
| |
| @Override |
| public void addChoiceElement(List<XMLField> srcFields, String elementTypeName, List<XMLField> tgtFields) { |
| for(XMLField sourceField:srcFields) { |
| this.fieldToClassNameMappings.put(sourceField, elementTypeName); |
| } |
| if (getClassNameToSourceFieldsMappings().get(elementTypeName) == null) { |
| getClassNameToSourceFieldsMappings().put(elementTypeName, srcFields); |
| } |
| addChoiceElementMapping(srcFields, elementTypeName, tgtFields); |
| } |
| |
| public void addChoiceElement(String srcXPath, Class<?> elementType, String tgtXPath) { |
| XMLField srcField = new XMLField(srcXPath); |
| XMLField tgtField = new XMLField(tgtXPath); |
| addChoiceElement(srcField, elementType, tgtField); |
| } |
| |
| @Override |
| public void addChoiceElement(String srcXpath, String elementTypeName, String tgtXpath) { |
| XMLField field = new XMLField(srcXpath); |
| XMLField tgtField = new XMLField(tgtXpath); |
| this.fieldToClassNameMappings.put(field, elementTypeName); |
| if(this.classNameToFieldMappings.get(elementTypeName) == null) { |
| this.classNameToFieldMappings.put(elementTypeName, field); |
| } |
| addChoiceElementMapping(field, elementTypeName, tgtField); |
| } |
| |
| public void addChoiceElement(XMLField sourceField, Class<?> elementType, XMLField targetField) { |
| getFieldToClassMappings().put(sourceField, elementType); |
| this.fieldToClassNameMappings.put(sourceField, elementType.getName()); |
| if (classToFieldMappings.get(elementType) == null) { |
| classToFieldMappings.put(elementType, sourceField); |
| } |
| addChoiceElementMapping(sourceField, elementType, targetField); |
| } |
| |
| private void addChoiceElementMapping(List<XMLField> sourceFields, Class<?> theClass, List<XMLField> targetFields) { |
| XMLCollectionReferenceMapping xmlMapping = new XMLCollectionReferenceMapping(); |
| xmlMapping.setReferenceClass(theClass); |
| xmlMapping.setAttributeAccessor(temporaryAccessor); |
| for(int i = 0; i < sourceFields.size(); i++) { |
| XMLField sourceField = sourceFields.get(i); |
| xmlMapping.addSourceToTargetKeyFieldAssociation(sourceField, targetFields.get(i)); |
| this.choiceElementMappings.put(sourceField, xmlMapping); |
| } |
| this.choiceElementMappingsByClass.put(theClass, xmlMapping); |
| } |
| |
| private void addChoiceElementMapping(List<XMLField> sourceFields, String theClass, List<XMLField> targetFields) { |
| XMLCollectionReferenceMapping xmlMapping = new XMLCollectionReferenceMapping(); |
| xmlMapping.setReferenceClassName(theClass); |
| xmlMapping.setAttributeAccessor(temporaryAccessor); |
| for(int i = 0; i < sourceFields.size(); i++) { |
| XMLField sourceField = sourceFields.get(i); |
| xmlMapping.addSourceToTargetKeyFieldAssociation(sourceField, targetFields.get(i)); |
| this.choiceElementMappings.put(sourceField, xmlMapping); |
| } |
| this.choiceElementMappingsByClassName.put(theClass, xmlMapping); |
| } |
| |
| private void addChoiceElementMapping(XMLField sourceField, Class<?> theClass, XMLField targetField) { |
| XMLCollectionReferenceMapping mapping = new XMLCollectionReferenceMapping(); |
| mapping.setReferenceClass(theClass); |
| mapping.setAttributeAccessor(temporaryAccessor); |
| mapping.addSourceToTargetKeyFieldAssociation(sourceField, targetField); |
| this.choiceElementMappings.put(sourceField, mapping); |
| this.choiceElementMappingsByClass.put(theClass, mapping); |
| } |
| |
| private void addChoiceElementMapping(XMLField sourceField, String className, XMLField targetField) { |
| XMLCollectionReferenceMapping mapping = new XMLCollectionReferenceMapping(); |
| mapping.setReferenceClassName(className); |
| mapping.setAttributeAccessor(temporaryAccessor); |
| mapping.addSourceToTargetKeyFieldAssociation(sourceField, targetField); |
| this.choiceElementMappings.put(sourceField, mapping); |
| this.choiceElementMappingsByClassName.put(className, mapping); |
| } |
| |
| @Override |
| public void addChoiceElement(XMLField field, String elementTypeName) { |
| this.fieldToClassNameMappings.put(field, elementTypeName); |
| if (classNameToFieldMappings.get(elementTypeName) == null) { |
| classNameToFieldMappings.put(elementTypeName, field); |
| } |
| addChoiceElementMapping(field, elementTypeName); |
| } |
| |
| |
| @Override |
| public Map<XMLField, Class<?>> getFieldToClassMappings() { |
| return fieldToClassMappings; |
| } |
| |
| @Override |
| public void initialize(AbstractSession session) throws DescriptorException { |
| super.initialize(session); |
| |
| if (this.converter != null) { |
| this.converter.initialize(this, session); |
| } |
| ArrayList<XMLMapping> mappingsList = new ArrayList<>(); |
| mappingsList.addAll(getChoiceElementMappings().values()); |
| for(XMLMapping next:getChoiceElementMappingsByClass().values()) { |
| if(!(mappingsList.contains(next))) { |
| mappingsList.add(next); |
| } |
| } |
| |
| if(isAny){ |
| //anyMapping = new XMLAnyCollectionMapping(); |
| mappingsList.add(anyMapping); |
| } |
| Iterator<XMLMapping> mappings = mappingsList.iterator(); |
| while(mappings.hasNext()){ |
| DatabaseMapping nextMapping = (DatabaseMapping)mappings.next(); |
| Converter converter = null; |
| if(fieldsToConverters != null) { |
| converter = fieldsToConverters.get(nextMapping.getField()); |
| } |
| if(nextMapping.isAbstractCompositeDirectCollectionMapping()){ |
| XMLConversionManager xmlConversionManager = (XMLConversionManager) session.getDatasourcePlatform().getConversionManager(); |
| QName schemaType = xmlConversionManager.schemaType(((AbstractCompositeDirectCollectionMapping)nextMapping).getAttributeElementClass()); |
| if(schemaType != null) { |
| ((XMLField)nextMapping.getField()).setSchemaType(schemaType); |
| } |
| |
| if(converter != null){ |
| ((AbstractCompositeDirectCollectionMapping)nextMapping).setValueConverter(converter); |
| } |
| ((AbstractCompositeDirectCollectionMapping)nextMapping).setContainerPolicy(getContainerPolicy()); |
| }else if(nextMapping.isAbstractCompositeCollectionMapping()){ |
| if(converter != null){ |
| ((AbstractCompositeCollectionMapping)nextMapping).setConverter(converter); |
| } |
| ((AbstractCompositeCollectionMapping)nextMapping).setContainerPolicy(getContainerPolicy()); |
| } else if(nextMapping instanceof XMLBinaryDataCollectionMapping) { |
| ((XMLBinaryDataCollectionMapping)nextMapping).setContainerPolicy(getContainerPolicy()); |
| if(converter != null) { |
| ((XMLBinaryDataCollectionMapping)nextMapping).setValueConverter(converter); |
| } |
| } else if (nextMapping instanceof XMLAnyCollectionMapping){ |
| ((XMLAnyCollectionMapping)nextMapping).setContainerPolicy(getContainerPolicy()); |
| if(converter != null && converter instanceof XMLConverter) { |
| ((XMLAnyCollectionMapping)nextMapping).setConverter((XMLConverter)converter); |
| } |
| }else{ |
| ((XMLCollectionReferenceMapping)nextMapping).setContainerPolicy(getContainerPolicy()); |
| ((XMLCollectionReferenceMapping)nextMapping).setReuseContainer(true); |
| } |
| |
| nextMapping.initialize(session); |
| } |
| } |
| |
| @Override |
| public Map<Class<?>, XMLField> getClassToFieldMappings() { |
| return classToFieldMappings; |
| } |
| |
| @Override |
| public Map<XMLField, XMLMapping> getChoiceElementMappings() { |
| return choiceElementMappings; |
| } |
| |
| @Override |
| public ContainerPolicy getContainerPolicy() { |
| return containerPolicy; |
| } |
| |
| public void setContainerPolicy(ContainerPolicy cp) { |
| this.containerPolicy = cp; |
| } |
| |
| public void useCollectionClass(Class<?> concreteContainerClass) { |
| this.setContainerPolicy(ContainerPolicy.buildPolicyFor(concreteContainerClass)); |
| } |
| |
| @Override |
| public void useCollectionClassName(String concreteContainerClassName) { |
| this.setContainerPolicy(new CollectionContainerPolicy(concreteContainerClassName)); |
| } |
| |
| @Override |
| public void convertClassNamesToClasses(ClassLoader classLoader) { |
| Iterator<Entry<XMLField, String>> entries = fieldToClassNameMappings.entrySet().iterator(); |
| while (entries.hasNext()) { |
| Map.Entry<XMLField, String> entry = entries.next(); |
| String className = entry.getValue(); |
| Class<?> elementType = null; |
| try { |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { |
| try { |
| elementType = AccessController.doPrivileged(new PrivilegedClassForName<>(className, true, classLoader)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(className, exception.getException()); |
| } |
| } else { |
| elementType = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(className, true, classLoader); |
| } |
| } catch (ClassNotFoundException exc) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(className, exc); |
| } |
| XMLMapping mapping = this.choiceElementMappings.get(entry.getKey()); |
| mapping.convertClassNamesToClasses(classLoader); |
| |
| if(fieldToClassMappings.get(entry.getKey()) == null) { |
| fieldToClassMappings.put(entry.getKey(), elementType); |
| } |
| } |
| for(Entry<String, XMLField> next: this.classNameToFieldMappings.entrySet()) { |
| String className = next.getKey(); |
| Class<?> elementType = null; |
| try { |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { |
| try { |
| elementType = AccessController.doPrivileged(new PrivilegedClassForName<>(className, true, classLoader)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(className, exception.getException()); |
| } |
| } else { |
| elementType = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(className, true, classLoader); |
| } |
| } catch (ClassNotFoundException exc) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(className, exc); |
| } |
| classToFieldMappings.put(elementType, next.getValue()); |
| } |
| if(classNameToSourceFieldsMappings != null) { |
| Iterator<Entry<String, List<XMLField>>> sourceFieldEntries = classNameToSourceFieldsMappings.entrySet().iterator(); |
| while(sourceFieldEntries.hasNext()) { |
| Entry<String, List<XMLField>> nextEntry = sourceFieldEntries.next(); |
| String className = nextEntry.getKey(); |
| List<XMLField> fields = nextEntry.getValue(); |
| Class<?> elementType = null; |
| try { |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { |
| try { |
| elementType = AccessController.doPrivileged(new PrivilegedClassForName<>(className, true, classLoader)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(className, exception.getException()); |
| } |
| } else { |
| elementType = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(className, true, classLoader); |
| } |
| } catch (ClassNotFoundException exc) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(className, exc); |
| } |
| this.getClassToSourceFieldsMappings().put(elementType,fields); |
| } |
| } |
| if(!choiceElementMappingsByClassName.isEmpty()) { |
| for(Entry<String, XMLMapping> next:choiceElementMappingsByClassName.entrySet()) { |
| Class<?> elementType = null; |
| String className = next.getKey(); |
| try { |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { |
| try { |
| elementType = AccessController.doPrivileged(new PrivilegedClassForName<>(className, true, classLoader)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(className, exception.getException()); |
| } |
| } else { |
| elementType = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(className, true, classLoader); |
| } |
| } catch (ClassNotFoundException exc) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(className, exc); |
| } |
| if(this.choiceElementMappingsByClass.get(elementType) == null) { |
| this.choiceElementMappingsByClass.put(elementType, next.getValue()); |
| } |
| next.getValue().convertClassNamesToClasses(classLoader); |
| } |
| } |
| } |
| |
| @Override |
| public void addConverter(XMLField field, Converter converter) { |
| if(this.fieldsToConverters == null) { |
| fieldsToConverters = new HashMap<>(); |
| } |
| fieldsToConverters.put(field, converter); |
| } |
| |
| @Override |
| public Converter getConverter(XMLField field) { |
| if(null != this.fieldsToConverters) { |
| Converter converter = fieldsToConverters.get(field); |
| if(null != converter) { |
| return converter; |
| } |
| if(null != this.choiceElementMappings) { |
| DatabaseMapping mapping = (DatabaseMapping) this.choiceElementMappings.get(field); |
| if(null == mapping) { |
| return null; |
| } |
| if(mapping.isAbstractCompositeDirectCollectionMapping()) { |
| return ((XMLCompositeDirectCollectionMapping)mapping).getValueConverter(); |
| } else if(mapping.isAbstractDirectMapping()) { |
| return ((XMLDirectMapping)mapping).getConverter(); |
| } |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public ArrayList getChoiceFieldToClassAssociations() { |
| ArrayList associations = new ArrayList(); |
| if(this.fieldToClassNameMappings.size() > 0) { |
| |
| Set<Entry<XMLField, String>> entries = fieldToClassNameMappings.entrySet(); |
| Iterator<Entry<XMLField, String>> iter = entries.iterator(); |
| while(iter.hasNext()){ |
| Entry<XMLField, String> nextEntry = iter.next(); |
| XMLField xmlField = nextEntry.getKey(); |
| String className = nextEntry.getValue(); |
| XMLChoiceFieldToClassAssociation association = new XMLChoiceFieldToClassAssociation(xmlField, className); |
| associations.add(association); |
| } |
| } |
| return associations; |
| } |
| |
| |
| public void setChoiceFieldToClassAssociations(ArrayList associations) { |
| if(associations.size() > 0) { |
| for(Object next:associations) { |
| XMLChoiceFieldToClassAssociation<Converter, XMLField> association = (XMLChoiceFieldToClassAssociation)next; |
| this.addChoiceElement(association.getXmlField(), association.getClassName()); |
| if(association.getConverter() != null) { |
| this.addConverter(association.getXmlField(), association.getConverter()); |
| } |
| } |
| } |
| } |
| |
| private void addChoiceElementMapping(XMLField xmlField, String className){ |
| if (xmlField.getLastXPathFragment().nameIsText() || xmlField.getLastXPathFragment().isAttribute()) { |
| XMLCompositeDirectCollectionMapping xmlMapping = new XMLCompositeDirectCollectionMapping(); |
| xmlMapping.setAttributeElementClassName(className); |
| xmlMapping.setField(xmlField); |
| xmlMapping.setAttributeAccessor(temporaryAccessor); |
| this.choiceElementMappings.put(xmlField, xmlMapping); |
| this.choiceElementMappingsByClassName.put(className, xmlMapping); |
| } else { |
| if(isBinaryType(className)) { |
| XMLBinaryDataCollectionMapping xmlMapping = new XMLBinaryDataCollectionMapping(); |
| xmlMapping.setField(xmlField); |
| xmlMapping.setAttributeAccessor(temporaryAccessor); |
| Class<Object> theClass = XMLConversionManager.getDefaultXMLManager().convertClassNameToClass(className); |
| xmlMapping.setAttributeElementClass(theClass); |
| this.choiceElementMappings.put(xmlField, xmlMapping); |
| this.choiceElementMappingsByClassName.put(className, xmlMapping); |
| } else { |
| XMLCompositeCollectionMapping xmlMapping = new XMLCompositeCollectionMapping(); |
| if(!className.equals("java.lang.Object")){ |
| xmlMapping.setReferenceClassName(className); |
| } |
| xmlMapping.setField(xmlField); |
| xmlMapping.setAttributeAccessor(temporaryAccessor); |
| this.choiceElementMappings.put(xmlField, xmlMapping); |
| this.choiceElementMappingsByClassName.put(className, xmlMapping); |
| } |
| } |
| } |
| |
| private void addChoiceElementMapping(XMLField xmlField, Class<?> theClass){ |
| |
| if (xmlField.getLastXPathFragment().nameIsText() || xmlField.getLastXPathFragment().isAttribute()) { |
| XMLCompositeDirectCollectionMapping xmlMapping = new XMLCompositeDirectCollectionMapping(); |
| xmlMapping.setAttributeElementClass(theClass); |
| xmlMapping.setField(xmlField); |
| xmlMapping.setAttributeAccessor(temporaryAccessor); |
| this.choiceElementMappings.put(xmlField, xmlMapping); |
| this.choiceElementMappingsByClass.put(theClass, xmlMapping); |
| } else { |
| if(isBinaryType(theClass)) { |
| XMLBinaryDataCollectionMapping xmlMapping = new XMLBinaryDataCollectionMapping(); |
| xmlMapping.setField(xmlField); |
| xmlMapping.setAttributeElementClass(theClass); |
| xmlMapping.setAttributeAccessor(temporaryAccessor); |
| this.fieldsToConverters.put(xmlField, xmlMapping.getValueConverter()); |
| this.choiceElementMappings.put(xmlField, xmlMapping); |
| this.choiceElementMappingsByClass.put(theClass, xmlMapping); |
| } else { |
| XMLCompositeCollectionMapping xmlMapping = new XMLCompositeCollectionMapping(); |
| if(!theClass.equals(ClassConstants.OBJECT)){ |
| xmlMapping.setReferenceClass(theClass); |
| } |
| xmlMapping.setField(xmlField); |
| xmlMapping.setAttributeAccessor(temporaryAccessor); |
| this.choiceElementMappings.put(xmlField, xmlMapping); |
| this.choiceElementMappingsByClass.put(theClass, xmlMapping); |
| } |
| } |
| } |
| |
| @Override |
| public boolean isWriteOnly() { |
| return this.isWriteOnly; |
| } |
| |
| @Override |
| public void setIsWriteOnly(boolean b) { |
| this.isWriteOnly = b; |
| } |
| |
| @Override |
| public boolean isAny() { |
| return this.isAny; |
| } |
| |
| public void setIsAny(boolean b) { |
| this.isAny = b; |
| } |
| |
| @Override |
| public void preInitialize(AbstractSession session) throws DescriptorException { |
| getAttributeAccessor().setIsWriteOnly(this.isWriteOnly()); |
| getAttributeAccessor().setIsReadOnly(this.isReadOnly()); |
| super.preInitialize(session); |
| //Collection<XMLMapping> allMappings = new ArrayList<XMLMapping>(); |
| ArrayList<XMLMapping> mappingsList = new ArrayList<>(); |
| mappingsList.addAll(getChoiceElementMappings().values()); |
| if(isAny){ |
| anyMapping = new XMLAnyCollectionMapping(); |
| //if(mixedGroupingElement != null){ |
| // anyMapping.setField(new XMLField(mixedGroupingElement)); |
| //} |
| anyMapping.setMixedContent(false); |
| anyMapping.setKeepAsElementPolicy(UnmarshalKeepAsElementPolicy.KEEP_UNKNOWN_AS_ELEMENT); |
| anyMapping.setUseXMLRoot(true); |
| mappingsList.add(anyMapping); |
| } |
| for(XMLMapping next:getChoiceElementMappingsByClass().values()) { |
| if(!(mappingsList.contains(next))) { |
| mappingsList.add(next); |
| } |
| } |
| for(XMLMapping next:getChoiceElementMappingsByClass().values()) { |
| if(!(mappingsList.contains(next))) { |
| mappingsList.add(next); |
| } |
| } |
| Iterator<XMLMapping> mappings = mappingsList.iterator(); |
| while(mappings.hasNext()){ |
| DatabaseMapping nextMapping = (DatabaseMapping)mappings.next(); |
| nextMapping.setAttributeName(this.getAttributeName()); |
| if(nextMapping.getAttributeAccessor() == temporaryAccessor){ |
| nextMapping.setAttributeAccessor(getAttributeAccessor()); |
| } |
| nextMapping.setIsReadOnly(this.isReadOnly()); |
| ((XMLMapping)nextMapping).setIsWriteOnly(this.isWriteOnly()); |
| |
| nextMapping.setDescriptor(getDescriptor()); |
| |
| nextMapping.preInitialize(session); |
| } |
| } |
| |
| @Override |
| public void setAttributeValueInObject(Object object, Object value) throws DescriptorException { |
| if(isWriteOnly()) { |
| return; |
| } |
| super.setAttributeValueInObject(object, value); |
| } |
| |
| /** |
| * Return true if the original container on the object should be used if |
| * present. If it is not present then the container policy will be used to |
| * create the container. |
| */ |
| @Override |
| public boolean getReuseContainer() { |
| return reuseContainer; |
| } |
| |
| /** |
| * Specify whether the original container on the object should be used if |
| * present. If it is not present then the container policy will be used to |
| * create the container. |
| */ |
| @Override |
| public void setReuseContainer(boolean reuseContainer) { |
| this.reuseContainer = reuseContainer; |
| } |
| |
| @Override |
| public Map<Class<?>, List<XMLField>> getClassToSourceFieldsMappings() { |
| if(this.classToSourceFieldsMappings == null) { |
| this.classToSourceFieldsMappings = new HashMap<>(); |
| } |
| return this.classToSourceFieldsMappings; |
| } |
| |
| private Map<String, List<XMLField>> getClassNameToSourceFieldsMappings() { |
| if(this.classNameToSourceFieldsMappings == null) { |
| this.classNameToSourceFieldsMappings = new HashMap<>(); |
| } |
| return this.classNameToSourceFieldsMappings; |
| } |
| |
| private boolean isBinaryType(String className) { |
| if(className.equals(byte[].class.getName()) || className.equals(Byte[].class.getName()) || className.equals(DATA_HANDLER) |
| || className.equals(IMAGE) || className.equals(MIME_MULTIPART)) { |
| return true; |
| } |
| return false; |
| } |
| |
| private boolean isBinaryType(Class<?> theClass) { |
| String className = theClass.getName(); |
| if(className.equals(byte[].class.getName()) || className.equals(Byte[].class.getName()) || className.equals(DATA_HANDLER) |
| || className.equals(IMAGE) || className.equals(MIME_MULTIPART)) { |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public Map<String, XMLField> getClassNameToFieldMappings() { |
| return classNameToFieldMappings; |
| } |
| |
| @Override |
| public boolean isMixedContent() { |
| // return this.mixedContentMapping != null; |
| return isMixedContent; |
| } |
| |
| /** |
| * PUBLIC: |
| * Allows the user to indicate that this mapping should also allow for mixed content in addition to |
| * any of the elements in the choice. The grouping element parameter is used in the case that there is |
| * a common grouping element to all the other elements in this choice. If so, that grouping element can |
| * be specified here to allow the mixed content to be written/detected inside the wrapper element. |
| * @since EclipseLink 2.3.1 |
| */ |
| @Override |
| public void setMixedContent(String groupingElement) { |
| isMixedContent = true; |
| |
| String xpath = groupingElement; |
| if(groupingElement.length() == 0) { |
| xpath = "text()"; |
| } else { |
| xpath += "/" + "text()"; |
| } |
| XMLField field = new XMLField(xpath); |
| XMLCompositeDirectCollectionMapping xmlMapping = new XMLCompositeDirectCollectionMapping(); |
| Class<String> theClass = ClassConstants.STRING; |
| xmlMapping.setAttributeElementClass(theClass); |
| xmlMapping.setField(field); |
| xmlMapping.setAttributeAccessor(temporaryAccessor); |
| this.mixedContentMapping = xmlMapping; |
| this.choiceElementMappings.put(field, xmlMapping); |
| |
| } |
| |
| /** |
| * PUBLIC: |
| * Allows the user to indicate that this mapping should also allow for mixed content in addition to |
| * any of the elements in the choice. |
| * @since EclipseLink 2.3.1 |
| */ |
| @Override |
| public void setMixedContent(boolean mixed) { |
| if(!mixed) { |
| this.mixedContentMapping = null; |
| } else { |
| setMixedContent(""); |
| } |
| isMixedContent = mixed; |
| } |
| |
| @Override |
| public XMLCompositeDirectCollectionMapping getMixedContentMapping() { |
| return this.mixedContentMapping; |
| } |
| |
| @Override |
| public XMLAnyCollectionMapping getAnyMapping(){ |
| return anyMapping; |
| } |
| |
| /** |
| * 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 isDefaultEmptyContainer; |
| } |
| |
| /** |
| * INTERNAL |
| * Indicate whether by default an empty container should be set on the |
| * field/property if the collection is not present in the XML document. |
| * @since EclipseLink 2.3.3 |
| */ |
| @Override |
| public void setDefaultEmptyContainer(boolean defaultEmptyContainer) { |
| this.isDefaultEmptyContainer = defaultEmptyContainer; |
| } |
| |
| @Override |
| public AbstractNullPolicy getWrapperNullPolicy() { |
| return this.wrapperNullPolicy; |
| } |
| |
| @Override |
| public void setWrapperNullPolicy(AbstractNullPolicy policy) { |
| this.wrapperNullPolicy = policy; |
| } |
| |
| @Override |
| public Map<Class<?>, XMLMapping> getChoiceElementMappingsByClass() { |
| return choiceElementMappingsByClass; |
| } |
| |
| public void setChoiceElementMappingsByClass(Map<Class<?>, XMLMapping> choiceElementMappingsByClass) { |
| this.choiceElementMappingsByClass = choiceElementMappingsByClass; |
| } |
| |
| /** |
| * INTERNAL |
| * @since EclipseLink 2.5.0 |
| */ |
| @Override |
| public Object convertObjectValueToDataValue(Object value, Session session, XMLMarshaller marshaller) { |
| if (null != converter) { |
| if (converter instanceof XMLConverter) { |
| return ((XMLConverter)converter).convertObjectValueToDataValue(value, session, marshaller); |
| } else { |
| return converter.convertObjectValueToDataValue(value, session); |
| } |
| } |
| return value; |
| } |
| |
| /** |
| * INTERNAL |
| * @since EclipseLink 2.5.0 |
| */ |
| @Override |
| public Object convertDataValueToObjectValue(Object fieldValue, Session session, XMLUnmarshaller unmarshaller) { |
| if (null != converter) { |
| if (converter instanceof XMLConverter) { |
| return ((XMLConverter)converter).convertDataValueToObjectValue(fieldValue, session, unmarshaller); |
| } else { |
| return converter.convertDataValueToObjectValue(fieldValue, session); |
| } |
| } |
| return fieldValue; |
| } |
| } |