
net.sf.javaprinciples.data.transformer.JaxbMapper Maven / Gradle / Ivy
The newest version!
package net.sf.javaprinciples.data.transformer;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementRef;
import org.apache.cxf.jaxb.JAXBUtils;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import net.sf.javaprinciples.core.ObjectClassHelper;
import net.sf.javaprinciples.core.UnexpectedException;
import net.sf.javaprinciples.data.transformer.spring.JaxbEnumToStringConverter;
import net.sf.javaprinciples.data.transformer.spring.StringToJaxbEnumConverterFactory;
/**
* A Mapper class to inspect and treat object types as a {@link JAXBElement}. Provides
* helper methods to get and set attributes on objects and JAXBElements.
*
* @author Kay Chevalier
*/
public class JaxbMapper implements ObjectTypeMapper
{
public static final String OBJECT_FACTORY_CLASS_NAME = "ObjectFactory";
public static final String CREATE_METHOD_NAME = "create";
private JaxbEnumToStringConverter jaxbEnumToStringConverter;
private StringToJaxbEnumConverterFactory stringToJaxbEnumConverterFactory;
private ConversionService conversionService;
@Override
public void assignAttributeToObject(Object attributeObject, Object parentObject, String attributeName)
{
if (attributeObject == null)
return;
attributeName = nameToIdentifier(attributeName);
if (isJAXBElement(parentObject, attributeName) && !(attributeObject instanceof JAXBElement))
{
Object objectFactory = createObjectFactoryFromParentObject(parentObject);
attributeObject = invokeObjectFactoryCreateMethod(attributeObject, parentObject, attributeName, objectFactory);
}
else
{
Class extends Enum> attributeTypeClass = (Class extends Enum>)ObjectClassHelper.getAttributeTypeByName(parentObject, attributeName);
attributeObject = convertValueToEnum(attributeObject, attributeTypeClass);
}
BeanWrapperImpl beanWrapper = new BeanWrapperImpl(parentObject);
if (conversionService != null)
{
beanWrapper.setConversionService(conversionService);
}
beanWrapper.setPropertyValue(attributeName, attributeObject);
}
private Object convertValueToEnum(Object attributeObject, Class extends Enum> attributeTypeClass)
{
if (attributeTypeClass != null && attributeTypeClass.isEnum() && attributeObject instanceof String)
{
Converter converter = stringToJaxbEnumConverterFactory.getConverter(attributeTypeClass);
return converter.convert((String)attributeObject);
}
else if (attributeTypeClass != null && attributeTypeClass.isEnum() && attributeObject instanceof Boolean)
{
Converter converter = stringToJaxbEnumConverterFactory.getConverter(attributeTypeClass);
return converter.convert(((Boolean)attributeObject).booleanValue() ? "Y" : "N"); //TODO This assumes any boolean enums will be a 'Y' or 'N'
}
return attributeObject;
}
private Object invokeObjectFactoryCreateMethod(Object attributeObject, Object parentObject, String attributeName,
Object objectFactory)
{
attributeName = nameToIdentifier(attributeName);
Method method = findObjectFactoryCreateMethod(objectFactory, attributeName, parentObject);
try
{
Class extends Enum> paramType = (Class extends Enum>)findObjectFactoryCreateMethodParamType(objectFactory, attributeName, parentObject);
if (paramType.isEnum() && attributeObject instanceof String)
{
attributeObject = convertValueToEnum(attributeObject, paramType);
}
//TODO KC - replace this code
if (String.class.equals(paramType))
{
attributeObject = String.valueOf(attributeObject);
}
if (Long.class.equals(paramType))
{
attributeObject = Long.parseLong((String)attributeObject);
}
return method.invoke(objectFactory, paramType.cast(attributeObject));
}
catch (IllegalAccessException e)
{
throw new UnexpectedException(String.format("Unable to invoke create method for JAXBElement for attribute %s",
attributeName), e);
}
catch (InvocationTargetException e)
{
throw new UnexpectedException(String.format("Unable to invoke create method for JAXBElement for attribute %s",
attributeName), e);
}
catch (IllegalArgumentException e)
{
throw new UnexpectedException(String.format("Unable to invoke create method for JAXBElement for attribute %s",
attributeName), e);
}
catch (ClassCastException e)
{
throw new UnexpectedException(String.format("Unable to cast attribute %s to required parameter type",
attributeName), e);
}
}
/**
* Instantiates an instance of the attribute object. Given the parentObject, retrieves the
* class type of the corresponding attribute to the attributeName. If the attribute type
* is a {@link JAXBElement} an object of the class type of the JAXBElement value is instantiated.
* @param parentObject - Object containing the attribute
* @param attributeName - the name of the attribute
* @return an instantiated object of the attribute
*/
public Object instantiateObjectFromAttributeName(Object parentObject, String attributeName)
{
attributeName = nameToIdentifier(attributeName);
java.lang.Class> attributeValueType;
if (isJAXBElement(parentObject, attributeName))
{
Object objectFactory = createObjectFactoryFromParentObject(parentObject);
attributeValueType = findObjectFactoryCreateMethodParamType(objectFactory, attributeName, parentObject);
}
else
{
attributeValueType = new BeanWrapperImpl(parentObject).getPropertyType(attributeName);
}
return ObjectClassHelper.createObjectFromClassName(attributeValueType.getName());
}
/**
* Creates an instance of the parent objects corresponding ObjectFactory.
* It is assumed that the object factory has a name of ObjectFactory and resides
* in the same package as the parent object.
*
* @param parentObject - the object to create the ObjectFactory class instance from
* @return ObjectFactory - the ObjectFactory instance
*/
public Object createObjectFactoryFromParentObject(Object parentObject)
{
String parentObjectPackage = parentObject.getClass().getPackage().getName();
String objectFactoryClassName = parentObjectPackage + "." + OBJECT_FACTORY_CLASS_NAME;
return ObjectClassHelper.createObjectFromClassName(objectFactoryClassName);
}
private Class> findObjectFactoryCreateMethodParamType(Object objectFactory, String attributeName, Object parentObject)
{
Method method = findObjectFactoryCreateMethod(objectFactory, attributeName, parentObject);
Class>[] classTypes = method.getParameterTypes();
return classTypes[0];
}
private Method findObjectFactoryCreateMethod(Object objectFactory, String attributeName, Object parentObject)
{
attributeName = nameToIdentifier(attributeName);
char[] stringArray = attributeName.toCharArray();
stringArray[0] = Character.toUpperCase(stringArray[0]);
String capitalizedAttributeName = new String(stringArray);
Method[] methods = objectFactory.getClass().getMethods();
Method parentObjectCreateMethod = findObjectFactoryCreateMethod(parentObject.getClass(), methods, capitalizedAttributeName);
if (parentObjectCreateMethod != null)
return parentObjectCreateMethod;
Method parentSuperClassObjectCreateMethod = findObjectFactoryCreateMethod(parentObject.getClass().getSuperclass(), methods, capitalizedAttributeName);
if (parentSuperClassObjectCreateMethod != null)
return parentSuperClassObjectCreateMethod;
throw new UnexpectedException(String.format("Unable to find create method on " + OBJECT_FACTORY_CLASS_NAME + " for attribute %s",
attributeName));
}
private Method findObjectFactoryCreateMethod(Class> parentObject, Method[] createFactoryMethods, String capitalizedAttributeName)
{
String createMethodName = CREATE_METHOD_NAME + parentObject.getSimpleName() + capitalizedAttributeName;
for (Method method : createFactoryMethods)
{
if (method.getName().equals(createMethodName))
{
return method;
}
}
return null;
}
/**
* Retrieves the value of the sourceAttributeName property from the given
* input object. Checks if the input object is of {@link JAXBElement} type and
* if null considers this as a nillable attribute and throws {@link AttributeNotFoundException}.
* If the sourceAttribute value is not a {@link JAXBElement} then nillable is not true and the
* value can be null. Extracts the value attribute from the JAXBElement.
*
* @param input - the input object to retrieve the attribute instance from
* @param sourceAttributeName - the name of the attribute
* @throws AttributeNotFoundException
*/
@Override
public Object getSourceAttribute(Object input, String sourceAttributeName) throws AttributeNotFoundException
{
sourceAttributeName = nameToIdentifier(sourceAttributeName, input);
Object value = getProperty(input, sourceAttributeName);
if (isJAXBElement(input, sourceAttributeName))
{
if ((value == null || ((JAXBElement)value).getValue() == null))
throw new AttributeNotFoundException(String.format("Attribute: %s not found and is a nillable JAXBElement.",
sourceAttributeName));
value = ((JAXBElement)value).getValue();
}
if(value instanceof Enum)
{
value = jaxbEnumToStringConverter.convert((Enum)value);
}
return value;
}
public Object getAttributeFromObject(Object input, String sourceAttributeName) throws AttributeNotFoundException
{
sourceAttributeName = nameToIdentifier(sourceAttributeName, input);
Object value = getProperty(input, sourceAttributeName);
return value;
}
public Class> getParameterizedCollectionTypeFromObject(Object input, String sourceAttributeName) throws AttributeNotFoundException
{
sourceAttributeName = nameToIdentifier(sourceAttributeName, input);
try
{
Field listField = input.getClass().getDeclaredField(sourceAttributeName);
Object listFieldGenericType = listField.getGenericType();
if (listFieldGenericType instanceof ParameterizedType)
{
ParameterizedType listType = (ParameterizedType) listFieldGenericType;
return (Class>) listType.getActualTypeArguments()[0];
}
return null;
}
catch (NoSuchFieldException e)
{
throw new UnexpectedException(String.format("Unable to determine list type for attribute %s", sourceAttributeName));
}
}
public boolean isJAXBElement(Object owningObject, String attributeName)
{
attributeName = nameToIdentifier(attributeName);
Class> attributeTypeClass = ObjectClassHelper.getAttributeTypeByName(owningObject, attributeName);
return JAXBElement.class.equals(attributeTypeClass);
}
/**
* Formats a targetNamespace into a package name according to the rules
* specified in the JAXB Specification.
*
* @param targetNamespace - the targetNamespace string to be converted
* @return String - the converted package name
*/
@Override
public String formatPackageName(String targetNamespace)
{
return JAXBUtils.namespaceURIToPackage(targetNamespace);
}
/**
* Formats a name into a java identifier according to the rules
* specified in the JAXB Specification for a {@link JAXBUtils.IdentifierType} VARIABLE.
*
* @param name - the name to be converted
* @return String - the java identifier
*/
public String nameToIdentifier(String name)
{
// if (isAllUpperCase(name))
// {
// return name;
// }
name = net.sf.javaprinciples.core.JAXBUtils.nameToIdentifier(name, net.sf.javaprinciples.core.JAXBUtils.IdentifierType.CLASS);
char[] stringArray = name.toCharArray();
stringArray[0] = Character.toLowerCase(stringArray[0]);
return new String(stringArray);
}
public Class determineModelTypeFromTarget(String name, Object owningObject)
{
String correctedName = nameToIdentifier(name);
Field[] fields = owningObject.getClass().getDeclaredFields();
if (fields == null)
{
throw new UnexpectedException("No fields for: " + owningObject.getClass());
}
for (Field field : fields)
{
XmlElementRef annotation = field.getAnnotation(XmlElementRef.class);
if (annotation != null && name.equals(annotation.name()))
{
return annotation.type();
}
XmlElement xmlAnnotation = field.getAnnotation(XmlElement.class);
if (xmlAnnotation != null && name.equals(xmlAnnotation.name()))
{
return xmlAnnotation.type();
}
}
return null;
}
public String nameToIdentifier(String name, Object owningObject)
{
if (name == null)
{
return null;
}
String correctedName = nameToIdentifier(name);
Field[] fields = owningObject.getClass().getDeclaredFields();
if (fields == null)
return correctedName;
for (Field field : fields)
{
XmlElementRef annotation = field.getAnnotation(XmlElementRef.class);
if (annotation != null && name.equals(annotation.name()))
{
if (Collection.class.isAssignableFrom(field.getType()))
{
if (!correctedName.endsWith("s"))
{
correctedName = correctedName + "s";
}
}
return correctedName;
}
XmlElement xmlAnnotation = field.getAnnotation(XmlElement.class);
if (xmlAnnotation != null && name.equals(xmlAnnotation.name()))
{
if (Collection.class.isAssignableFrom(field.getType()))
{
if (!correctedName.endsWith("s"))
{
correctedName = correctedName + "s";
}
}
return correctedName;
}
}
return correctedName;
}
private boolean isAllUpperCase(String toCheck)
{
for (char c : toCheck.toCharArray())
{
if (Character.isLowerCase(c))
{
return false;
}
}
return true;
}
private Object getProperty(Object input, String sourceAttributeName)
{
if (input instanceof JAXBElement)
{
input = ((JAXBElement)input).getValue();
}
return new BeanWrapperImpl(input).getPropertyValue(sourceAttributeName);
}
public void setStringToJaxbEnumConverterFactory(StringToJaxbEnumConverterFactory stringToJaxbEnumConverterFactory)
{
this.stringToJaxbEnumConverterFactory = stringToJaxbEnumConverterFactory;
}
public void setJaxbEnumToStringConverter(JaxbEnumToStringConverter jaxbEnumToStringConverter)
{
this.jaxbEnumToStringConverter = jaxbEnumToStringConverter;
}
public void setConversionService(ConversionService conversionService)
{
this.conversionService = conversionService;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy