/*
* 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:
* Purpose: 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
*
Responsibilities:
* Allow the user to specify XPath {@literal ->} Type mappings
* Handle reading and writing of XML Documents containing a collection of choice or substitution
* group elements
*
* 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.
*
Setting up XPath mappings: 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.
*
* xmlChoiceCollectionMapping.addChoiceElement("mystring/text()", String.class);
*
* xmlChoiceCollectionMapping.addChoiceElement("myaddress", Address.class);
*
*/
public class XMLChoiceCollectionMapping extends DatabaseMapping implements ChoiceCollectionMapping, XMLMapping {
private Map> fieldToClassMappings;
private Map, XMLField> classToFieldMappings;
private Map, List> classToSourceFieldsMappings;
private Map> classNameToSourceFieldsMappings;
private Map choiceElementMappings;
private Map choiceElementMappingsByClassName;
private Map, XMLMapping> choiceElementMappingsByClass;
private Map fieldToClassNameMappings;
private Map classNameToFieldMappings;
private Map 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 objectDescriptors, Map 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 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 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 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 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 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 getFields() {
if(fields == null || fields.size() == 0) {
fields = this.collectFields();
}
return this.fields;
}
@Override
protected Vector 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 srcFields, Class> elementType, List 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 srcFields, String elementTypeName, List 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 sourceFields, Class> theClass, List 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 sourceFields, String theClass, List 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> getFieldToClassMappings() {
return fieldToClassMappings;
}
@Override
public void initialize(AbstractSession session) throws DescriptorException {
super.initialize(session);
if (this.converter != null) {
this.converter.initialize(this, session);
}
ArrayList 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 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, XMLField> getClassToFieldMappings() {
return classToFieldMappings;
}
@Override
public Map 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> entries = fieldToClassNameMappings.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry 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 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>> sourceFieldEntries = classNameToSourceFieldsMappings.entrySet().iterator();
while(sourceFieldEntries.hasNext()) {
Entry> nextEntry = sourceFieldEntries.next();
String className = nextEntry.getKey();
List 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 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> entries = fieldToClassNameMappings.entrySet();
Iterator> iter = entries.iterator();
while(iter.hasNext()){
Entry 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 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 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 allMappings = new ArrayList();
ArrayList 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 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, List> getClassToSourceFieldsMappings() {
if(this.classToSourceFieldsMappings == null) {
this.classToSourceFieldsMappings = new HashMap<>();
}
return this.classToSourceFieldsMappings;
}
private Map> 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 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 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, XMLMapping> getChoiceElementMappingsByClass() {
return choiceElementMappingsByClass;
}
public void setChoiceElementMappingsByClass(Map, 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;
}
}