/******************************************************************************* * Copyright (c) 1998, 2015 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 v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.internal.jaxb; import java.lang.reflect.Constructor; import java.lang.reflect.Type; import java.security.AccessController; import java.util.HashMap; import javax.xml.bind.annotation.adapters.XmlAdapter; import javax.xml.namespace.QName; import org.eclipse.persistence.core.mappings.converters.CoreConverter; import org.eclipse.persistence.exceptions.ConversionException; import org.eclipse.persistence.exceptions.JAXBException; import org.eclipse.persistence.internal.oxm.XMLBinaryDataHelper; import org.eclipse.persistence.internal.oxm.XMLConversionManager; import org.eclipse.persistence.internal.oxm.mappings.BinaryDataMapping; import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; import org.eclipse.persistence.internal.security.PrivilegedClassForName; import org.eclipse.persistence.internal.security.PrivilegedGetConstructorFor; import org.eclipse.persistence.internal.security.PrivilegedNewInstanceFromClass; import org.eclipse.persistence.internal.sessions.AbstractSession; import org.eclipse.persistence.jaxb.JAXBMarshaller; import org.eclipse.persistence.jaxb.JAXBUnmarshaller; import org.eclipse.persistence.mappings.DatabaseMapping; import org.eclipse.persistence.mappings.converters.ObjectTypeConverter; import org.eclipse.persistence.oxm.XMLMarshaller; import org.eclipse.persistence.oxm.XMLUnmarshaller; import org.eclipse.persistence.sessions.Session; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; /** * Converter that wraps an XmlAdapter. * * @see javax.xml.bind.annotation.adapters.XmlAdapter */ public class XMLJavaTypeConverter extends org.eclipse.persistence.oxm.mappings.converters.XMLConverterAdapter { protected Class boundType = Object.class; protected Class valueType = Object.class; protected Class xmlAdapterClass; protected String xmlAdapterClassName; protected XmlAdapter xmlAdapter; protected QName schemaType; protected DatabaseMapping mapping; protected CoreConverter nestedConverter; /** * The default constructor. This constructor should be used * in conjunction with the setXmlAdapterClass method or * setXmlAdapterClassName method. */ public XMLJavaTypeConverter() { } /** * This constructor takes the XmlAdapter class to be used with this * converter. * * @param xmlAdapterClass */ public XMLJavaTypeConverter(Class xmlAdapterClass) { setXmlAdapterClass(xmlAdapterClass); } /** * This constructor takes an adapter class name. The adapter * class will be loaded during initialization. * * @param xmlAdapterClassName */ public XMLJavaTypeConverter(String xmlAdapterClassName) { this.xmlAdapterClassName = xmlAdapterClassName; } /** * This constructor takes the XmlAdapter class to be used with this * converter, as well as a schema type to be used during the conversion * operation. During unmarshal, the value type will be converted to * the schema type, then from that type to the bound type. The opposite * will occur during marshal. * * @param xmlAdapterClass * @param schemaType */ public XMLJavaTypeConverter(Class xmlAdapterClass, QName schemaType) { setSchemaType(schemaType); setXmlAdapterClass(xmlAdapterClass); } /** * This constructor takes the XmlAdapter class name to be used with this * converter (loaded during initialization), as well as a schema type to * be used during the conversion operation. During unmarshal, the value * type will be converted to the schema type, then from that type to the * bound type. The opposite will occur during marshal. * * @param xmlAdapterClassName * @param schemaType */ public XMLJavaTypeConverter(String xmlAdapterClassName, QName schemaType) { setSchemaType(schemaType); setXmlAdapterClassName(xmlAdapterClassName); } /** * Wraps the XmlAdapter unmarshal method. */ public Object convertDataValueToObjectValue(Object dataValue, Session session, XMLUnmarshaller unmarshaller) { try { XmlAdapter adapter = this.xmlAdapter; if (unmarshaller != null) { HashMap adapters = (HashMap) unmarshaller.getProperty(JAXBUnmarshaller.XML_JAVATYPE_ADAPTERS); if (adapters != null) { XmlAdapter runtimeAdapter = (XmlAdapter) adapters.get(this.xmlAdapterClass); if (runtimeAdapter != null) { adapter = runtimeAdapter; } } } Object toConvert = dataValue; //apply nested converter first if(nestedConverter != null) { toConvert = nestedConverter.convertDataValueToObjectValue(toConvert, session); } else { if ((dataValue != null) && !(dataValue.getClass() == this.valueType)) { if (this.mapping instanceof BinaryDataMapping) { toConvert = XMLBinaryDataHelper.getXMLBinaryDataHelper().convertObject(dataValue, valueType, (AbstractSession) session, this.mapping.getContainerPolicy()); } else { if (getSchemaType() != null) { toConvert = ((XMLConversionManager) session.getDatasourcePlatform().getConversionManager()).convertObject(dataValue, valueType, getSchemaType()); } else { toConvert = session.getDatasourcePlatform().getConversionManager().convertObject(dataValue, valueType); } } } } return adapter.unmarshal(toConvert); } catch (Exception ex) { if(unmarshaller.getErrorHandler() == null){ throw ConversionException.couldNotBeConverted(dataValue, boundType, ex); } try { unmarshaller.getErrorHandler().warning(new SAXParseException(null, null, ex)); return null; } catch (SAXException e) { throw ConversionException.couldNotBeConverted(dataValue, boundType, ex); } } } /** * Wraps the XmlAdapter marshal method. */ public Object convertObjectValueToDataValue(Object objectValue, Session session, XMLMarshaller marshaller) { try { XmlAdapter adapter = this.xmlAdapter; if (marshaller != null) { HashMap adapters = (HashMap) marshaller.getProperty(JAXBMarshaller.XML_JAVATYPE_ADAPTERS); if (adapters != null) { XmlAdapter runtimeAdapter = (XmlAdapter) adapters.get(this.xmlAdapterClass); if (runtimeAdapter != null) { adapter = runtimeAdapter; } } } Object dataValue = adapter.marshal(objectValue); if(nestedConverter != null) { dataValue = nestedConverter.convertObjectValueToDataValue(dataValue, session); } return dataValue; } catch (Exception ex) { if(marshaller.getErrorHandler() == null){ throw ConversionException.couldNotBeConverted(objectValue, valueType, ex); } try { marshaller.getErrorHandler().warning(new SAXParseException(null, null, ex)); return null; } catch (SAXException e) { throw ConversionException.couldNotBeConverted(objectValue, valueType, ex); } } } /** * Get the schema type to be used during conversion. */ public QName getSchemaType() { return schemaType; } /** * Return the XmlAdapter class for this converter. * * @return xmlAdapterClass */ public Class getXmlAdapterClass() { return xmlAdapterClass; } /** * Return the XmlAdapter class name for this converter. * If null, the name will be set to "". * * @return xmlAdapterClassName */ public String getXmlAdapterClassName() { if (xmlAdapterClassName == null) { xmlAdapterClassName = ""; } return xmlAdapterClassName; } /** * Figure out the BoundType and ValueType for the XmlAdapter class, then * either create an instance of the XmlAdapter, or if an instance is set * on the marshaller, use it. * * @param mapping * @param session */ public void initialize(DatabaseMapping mapping, Session session) { // if the adapter class is null, try the adapter class name ClassLoader loader = session.getDatasourceLogin().getDatasourcePlatform().getConversionManager().getLoader(); if (xmlAdapterClass == null) { try { if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { xmlAdapterClass = AccessController.doPrivileged(new PrivilegedClassForName(getXmlAdapterClassName(), true, loader)); } else { xmlAdapterClass = PrivilegedAccessHelper.getClassForName(getXmlAdapterClassName(), true, loader); } } catch (Exception e) { throw JAXBException.adapterClassNotLoaded(getXmlAdapterClassName(), e); } } // validate adapter class extends javax.xml.bind.annotation.adapters.XmlAdapter if (!XmlAdapter.class.isAssignableFrom(xmlAdapterClass)) { throw JAXBException.invalidAdapterClass(getXmlAdapterClassName()); } setBoundTypeAndValueTypeInCaseOfGenericXmlAdapter(); try { try { if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { xmlAdapter = (XmlAdapter) AccessController.doPrivileged(new PrivilegedNewInstanceFromClass(getXmlAdapterClass())); } else { xmlAdapter = (XmlAdapter) PrivilegedAccessHelper.newInstanceFromClass(getXmlAdapterClass()); } } catch (IllegalAccessException e) { Constructor ctor = null; if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { ctor = AccessController.doPrivileged(new PrivilegedGetConstructorFor(xmlAdapterClass, new Class[0], true)); } else { ctor = PrivilegedAccessHelper.getDeclaredConstructorFor(xmlAdapterClass, new Class[0], true); } xmlAdapter = (XmlAdapter) PrivilegedAccessHelper.invokeConstructor(ctor, new Object[0]); } } catch (Exception ex) { throw JAXBException.adapterClassCouldNotBeInstantiated(getXmlAdapterClassName(), ex); } if(nestedConverter != null) { if(nestedConverter instanceof ObjectTypeConverter) { ((ObjectTypeConverter)nestedConverter).convertClassNamesToClasses(loader); } nestedConverter.initialize(mapping, session); } } private void setBoundTypeAndValueTypeInCaseOfGenericXmlAdapter() { Type[] parameterizedTypeArguments = GenericsClassHelper.getParameterizedTypeArguments(xmlAdapterClass, XmlAdapter.class); if (null != parameterizedTypeArguments) { Class valueTypeClass = GenericsClassHelper.getClassOfType(parameterizedTypeArguments[0]); if (null != valueTypeClass) { valueType = valueTypeClass; } if (valueType.isInterface()) { valueType = Object.class; // during unmarshalling we'll need to instantiate this, so -> no interfaces } Class boundTypeClass = GenericsClassHelper.getClassOfType(parameterizedTypeArguments[1]); if (null != boundTypeClass) { boundType = boundTypeClass; } } } /** * Satisfy the interface. */ public boolean isMutable() { return false; } /** * Set the schema type to be used during conversion - if one is * required. */ public void setSchemaType(QName qname) { schemaType = qname; } /** * Set the XmlAdapter class to be used with this converter. * * @param xmlAdapterClass */ public void setXmlAdapterClass(Class xmlAdapterClass) { this.xmlAdapterClass = xmlAdapterClass; } /** * Set the XmlAdapter class to be used with this converter. * * @param xmlAdapterClassName */ public void setXmlAdapterClassName(String xmlAdapterClassName) { this.xmlAdapterClassName = xmlAdapterClassName; } /** * Get the nested converter to that is used in conjunction with the adapter. */ public CoreConverter getNestedConverter() { return nestedConverter; } /** * Set a nested converter to be used in conjunction with the adapter. On marshal, * the nested converter is invoked after the adapter. On umarshal it is invoked before. * Primarily used to support enumerations with adapters. */ public void setNestedConverter(CoreConverter nestedConverter) { this.nestedConverter = nestedConverter; } }