blob: a99c675acb01455c8a0091c14190d78f34d09ea3 [file] [log] [blame]
/*
* 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;
import java.util.ArrayList;
import java.util.Iterator;
import javax.xml.namespace.QName;
import org.eclipse.persistence.exceptions.ConversionException;
import org.eclipse.persistence.internal.core.sessions.CoreAbstractSession;
import org.eclipse.persistence.internal.oxm.ConversionManager;
import org.eclipse.persistence.internal.oxm.XMLConversionManager;
import org.eclipse.persistence.internal.oxm.mappings.UnionField;
import org.eclipse.persistence.internal.oxm.record.AbstractUnmarshalRecord;
/**
* <p>Subclass of XMLField for fields that are mapped to unions.
* Maintains a list of schema types instead of just one single schema type.
* Schema types can be added using the addSchemaType api.
*
* XMLConstants has a list of useful constants including a list of QNames for
* built-in schema types that can be used when adding schema types.
*
* <p>When reading and writing an element that is mapped with an XMLUnionField, a conversion to
* each of the schema types on the field (in the order they are specified ) is tried
* until a conversion is successful. The java type to convert to is based on the list of
* schema type to java conversion pairs specified on the field. These conversion pairs
* can be modified using the addXMLConversion api.
*
* <p><em>Code Sample</em><br>
* <code>
* In this example the age field could be a date or an int.<br>
* XMLUnionField field = new XMLUnionField("age/text()");<br>
* field.addSchemaType(XMLConstants.DATE_QNAME);<br>
* field.addSchemaType(XMLConstants.INT_QNAME)<br>
* </code>
*
* @see XMLField
* @see XMLConstants
*/
public class XMLUnionField extends XMLField implements UnionField<XMLConversionManager, NamespaceResolver> {
private ArrayList schemaTypes;
/**
* Constructs an XMLUnionField
*/
public XMLUnionField() {
super();
schemaTypes = new ArrayList();
}
/**
* Constructs an XMLUnionField with the xpath set to the specified xPath
* @param xPath The xpath expression for the field
*/
public XMLUnionField(String xPath) {
super(xPath);
schemaTypes = new ArrayList();
}
/**
* Return the list of schema types
* @return the list of types
*/
@Override
public ArrayList getSchemaTypes() {
return schemaTypes;
}
/**
* Sets the schema types that this attribute can be mapped to
* Valid QName schema types can be found on org.eclipse.persistence.oxm.XMLConstants
* @param value An ArrayList containing the schema types.
* @see org.eclipse.persistence.oxm.XMLConstants
*/
public void setSchemaTypes(ArrayList value) {
schemaTypes = value;
}
/**
* Adds the new type value to the list of types
* @param value QName to be added to the list of schema types
*/
@Override
public void addSchemaType(QName value) {
if (value != null) {
if (schemaTypes == null) {
schemaTypes = new ArrayList();
}
// don't add things twice - for now the MW adds only some types...
// once they add all types this can be removed
if (!contains(schemaTypes, value)) {
schemaTypes.add(value);
}
}
}
/**
* Return the first schema type in the list of schema types
* @return the first item in the collection of schema types
*/
@Override
public QName getSchemaType() {
if (schemaTypes != null) {
return (QName) getSchemaTypes().get(0);
}
return null;
}
/**
* Adds the new type value to the list of types
* @param value The value to be added to the list of schema types
*/
@Override
public void setSchemaType(QName value) {
addSchemaType(value);
}
/**
* Checks for existance of a schema type in the list.
*
* @param types List of types
* @param value the QName to look for in the list
* @return true if 'value' exists in the list, false otherwise
*/
private boolean contains(ArrayList types, QName value) {
QName type;
Iterator it = types.iterator();
while (it.hasNext()) {
type = (QName) it.next();
if (type.equals(value)) {
return true;
}
}
return false;
}
/**
* INTERNAL:
* returns true since this is a union field
*/
@Override
public boolean isUnionField() {
return true;
}
@Override
public QName getSchemaTypeForValue(Object value, CoreAbstractSession session) {
if(leafElementType != null){
return leafElementType;
}else if (isTypedTextField()) {
ConversionManager conversionManager = (ConversionManager) session.getDatasourcePlatform().getConversionManager();
return getXMLType(value.getClass(), conversionManager);
}
return getSingleValueToWriteForUnion(value, session);
}
protected QName getSingleValueToWriteForUnion(Object value, CoreAbstractSession session) {
ArrayList schemaTypes = getSchemaTypes();
QName schemaType = null;
for (int i = 0, schemaTypesSize = schemaTypes.size(); i < schemaTypesSize; i++) {
QName nextQName = (QName)getSchemaTypes().get(i);
try {
if (nextQName != null) {
ConversionManager conversionManager = (ConversionManager) session.getDatasourcePlatform().getConversionManager();
Class<?> javaClass = getJavaClass(nextQName, conversionManager);
conversionManager.convertObject(value, javaClass, nextQName);
schemaType = nextQName;
break;
}
} catch (ConversionException ce) {
if (i == (schemaTypes.size() - 1)) {
schemaType = nextQName;
}
}
}
return schemaType;
}
/**
* INTERNAL:
*/
@Override
public Object convertValueBasedOnSchemaType(Object value, XMLConversionManager xmlConversionManager, AbstractUnmarshalRecord record) {
Object convertedValue = value;
for (int i = 0; i < schemaTypes.size(); i++) {
QName nextQName = (QName) schemaTypes.get(i);
try {
if (nextQName != null) {
if(XMLConstants.QNAME_QNAME.equals(nextQName)){
xmlConversionManager.buildQNameFromString((String)value, record);
break;
}else{
Class<?> javaClass = getJavaClass(nextQName, xmlConversionManager);
convertedValue = xmlConversionManager.convertObject(value, javaClass, nextQName);
break;
}
}
} catch (ConversionException ce) {
if (i == (schemaTypes.size() - 1)) {
throw ce;
}
}
}
return convertedValue;
}
/**
* Return the class for a given qualified XML Schema type.
* If the class is a primitive the corresponding wrapper class is returned
* @param qname The qualified name of the XML Schema type to use as a key in the lookup
* @return The class associated with the specified schema type, if no corresponding match found returns null
*/
@Override
public Class<?> getJavaClass(QName qname) {
return getJavaClass(qname, XMLConversionManager.getDefaultXMLManager());
}
/**
* INTERNAL
* @return the class for a given qualified XML Schema type.
* @since EclipseLink 2.6.0
*/
@Override
public Class<?> getJavaClass(QName qname, ConversionManager conversionManager) {
if (userXMLTypes != null) {
Class<?> theClass = (Class) userXMLTypes.get(qname);
if(theClass != null){
return theClass;
}
}
Class<Object> javaClass = conversionManager.javaType(qname);
return XMLConversionManager.getObjectClass(javaClass);
}
/**
* INTERNAL
*/
@Override
public boolean isSchemaType(QName schemaType){
if(getSchemaTypes() == null || getSchemaTypes().size() ==0){
return false;
}
return contains(getSchemaTypes(), schemaType);
}
}