/*******************************************************************************
* Copyright (c) 1998, 2008 Oracle. 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.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.HashMap;
import org.eclipse.persistence.exceptions.ConversionException;
import org.eclipse.persistence.internal.oxm.XMLConversionManager;
import org.eclipse.persistence.internal.oxm.XMLBinaryDataHelper;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.security.PrivilegedClassForName;
import org.eclipse.persistence.internal.security.PrivilegedGetDeclaredMethods;
import org.eclipse.persistence.internal.security.PrivilegedNewInstanceFromClass;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.oxm.XMLMarshaller;
import org.eclipse.persistence.oxm.XMLUnmarshaller;
import org.eclipse.persistence.jaxb.JAXBMarshaller;
import org.eclipse.persistence.jaxb.JAXBUnmarshaller;
import org.eclipse.persistence.oxm.mappings.XMLBinaryDataMapping;
import org.eclipse.persistence.sessions.Session;
import java.lang.reflect.Method;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.namespace.QName;
/**
* 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;
/**
* The default constructor. This constructor should be used
* in conjunction with the setXmlAdapterClass method or
* setXmlAdapterClassName method.
*
* @see setXmlAdapterClass(Class)
*/
public XMLJavaTypeConverter() {
}
/**
* This constructor takes the XmlAdapter class to be used with this
* converter.
*
* @param xmlAdapter
*/
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 xmlAdapter
* @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 xmlAdapter
* @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;
if ((dataValue != null) && !(dataValue.getClass() == this.valueType)) {
if (this.mapping instanceof XMLBinaryDataMapping) {
toConvert = XMLBinaryDataHelper.getXMLBinaryDataHelper().convertObject(dataValue, valueType, (AbstractSession) session);
} 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) {
throw ConversionException.couldNotBeConverted(dataValue, boundType);
}
}
/**
* 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;
}
}
}
return adapter.marshal(objectValue);
} catch (Exception ex) {
throw ConversionException.couldNotBeConverted(objectValue, valueType);
}
}
/**
* 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
if (xmlAdapterClass == null) {
// TODO: which classloader should we use here?
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
try {
xmlAdapterClass = (Class) AccessController.doPrivileged(new PrivilegedClassForName(getXmlAdapterClassName()));
} catch (PrivilegedActionException ex) {
if (ex.getCause() instanceof ClassNotFoundException) {
throw (ClassNotFoundException) ex.getCause();
}
throw (RuntimeException) ex.getCause();
}
} else {
xmlAdapterClass = PrivilegedAccessHelper.getClassForName(getXmlAdapterClassName());
}
} catch (ClassNotFoundException cnfe) {
// TODO: should throw an exception here
return;
}
}
this.mapping = mapping;
Method[] methods = null;
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
try {
methods = (Method[]) AccessController.doPrivileged(new PrivilegedGetDeclaredMethods(xmlAdapterClass));
;
} catch (PrivilegedActionException ex) {
throw (RuntimeException) ex.getCause();
}
} else {
methods = PrivilegedAccessHelper.getDeclaredMethods(xmlAdapterClass);
}
Method method;
// look for marshal method
for (int i = 0; i < methods.length; i++) {
method = methods[i];
// for some reason, getDeclaredMethods is returning inherited
// methods - need to filter
if (method.getName().equals("marshal") && (method.getReturnType() != Object.class) && (method.getParameterTypes()[0] != Object.class)) {
valueType = method.getReturnType();
boundType = method.getParameterTypes()[0];
break;
}
}
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
try {
xmlAdapter = (XmlAdapter) AccessController.doPrivileged(new PrivilegedNewInstanceFromClass(getXmlAdapterClass()));
;
} catch (PrivilegedActionException ex) {
if (ex.getCause() instanceof IllegalAccessException) {
throw (IllegalAccessException) ex.getCause();
}
if (ex.getCause() instanceof InstantiationException) {
throw (InstantiationException) ex.getCause();
}
throw (RuntimeException) ex.getCause();
}
} else {
xmlAdapter = (XmlAdapter) PrivilegedAccessHelper.newInstanceFromClass(getXmlAdapterClass());
}
} catch (Exception ex) {
// TODO: need to throw an exception here
}
}
/**
* 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 xmlAdapterClass
*/
public void setXmlAdapterClassName(String xmlAdapterClassName) {
this.xmlAdapterClassName = xmlAdapterClassName;
}
}