Here are the specifics on what I needed to do. The basic idea is to dynamically call Enum.values()[index] to do the conversion from Integer index into the specific enum. To remove the need for introspection in consequent calls, the result of initial call to Enum.values() is cached by the Enum specific class.
At first, I extended from DozerConverter, which has some nice functionality in it, however, that did not work, as at times, destination object is null and I could not get the type of class from a null, so I copied the code from DozerConverter and modified it to also pass in the types of the classes needed for conversion. Then, I wrote my Integer -> Enum converter based on that.
import org.apache.commons.lang3.ClassUtils;
import org.dozer.ConfigurableCustomConverter;
import org.dozer.MappingException;
/**
* This class should be extended in order to implement new Custom Converters for value transformation.
*/
public abstract class CustomDozerConverter<A, B> implements ConfigurableCustomConverter {
private String parameter;
private final Class<A> prototypeA;
private final Class<B> prototypeB;
/**
* Defines two types, which will take part transformation.
* As Dozer supports bi-directional mapping it is not known which of the classes is source and
* which is destination. It will be decided in runtime.
*
* @param prototypeA type one
* @param prototypeB type two
*/
public CustomDozerConverter(final Class<A> prototypeA, final Class<B> prototypeB) {
this.prototypeA = prototypeA;
this.prototypeB = prototypeB;
}
// Method first checks exact type matches and only then checks for assignement
public Object convert(final Object existingDestinationFieldValue, final Object sourceFieldValue, final Class<?> dClass, final Class<?> sClass) {
final Class<?> dstClass = ClassUtils.primitiveToWrapper(dClass);
final Class<?> srcClass = ClassUtils.primitiveToWrapper(sClass);
if (prototypeA.equals(dstClass)) {
return convertFrom((B) sourceFieldValue, srcClass, (A) existingDestinationFieldValue, dstClass);
} else if (prototypeB.equals(dstClass)) {
return convertTo((A) sourceFieldValue, srcClass, (B) existingDestinationFieldValue, dstClass);
} else if (prototypeA.equals(srcClass)) {
return convertTo((A) sourceFieldValue, srcClass, (B) existingDestinationFieldValue, dstClass);
} else if (prototypeB.equals(srcClass)) {
return convertFrom((B) sourceFieldValue, srcClass, (A) existingDestinationFieldValue, dstClass);
} else if (prototypeA.isAssignableFrom(dstClass)) {
return convertFrom((B) sourceFieldValue, srcClass, (A) existingDestinationFieldValue, dstClass);
} else if (prototypeB.isAssignableFrom(dstClass)) {
return convertTo((A) sourceFieldValue, srcClass, (B) existingDestinationFieldValue, dstClass);
} else if (prototypeA.isAssignableFrom(srcClass)) {
return convertTo((A) sourceFieldValue, srcClass, (B) existingDestinationFieldValue, dstClass);
} else if (prototypeB.isAssignableFrom(srcClass)) {
return convertFrom((B) sourceFieldValue, srcClass, (A) existingDestinationFieldValue, dstClass);
} else {
throw new MappingException("Destination Type (" + dstClass.getName()
+ ") is not accepted by this Custom Converter ("
+ this.getClass().getName() + ")!");
}
}
/**
* Converts the source field to the destination field and return the resulting destination
* value.
*
* @param source the value of the source field
* @param destination the current value of the desitinatino field (or null)
* @return the resulting value for the destinatino field
*/
abstract public B convertTo(A source, Class sourceClass, B destination, Class destinationClass);
/**
* Converts the source field to the destination field and return the resulting destination
* value
*
* @param source the value of the source field
* @param destination the current value of the desitinatino field (or null)
* @return the resulting value for the destinatino field
*/
abstract public A convertFrom(B source, Class sourceClass, A destination, Class destinationClass);
/**
* Sets the configured parameter value for this converter instance.
* Should be called by Dozer internaly before actual mapping.
*
* @param parameter configured parameter value
*/
public void setParameter(String parameter) {
this.parameter = parameter;
}
/**
* Retrieves the static parameter configured for this particular converter instance.
* It is not advisable to call this method from converter constructor as the parameter is not yet there.
*
* @return parameter value
* @throws IllegalStateException if parameter has not been set yet.
*/
public String getParameter() {
if (parameter == null) {
throw new IllegalStateException("Custom Converter Parameter has not yet been set!");
}
return parameter;
}
}
import org.apache.commons.beanutils.MethodUtils;
import org.dozer.MappingException;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class IntegerEnumConverter extends CustomDozerConverter<Integer, Enum> {
private static ConcurrentMap<Class, Enum[]>
cache =
new ConcurrentHashMap<>();
public IntegerEnumConverter() {
super(Integer.class, Enum.class);
}
// Method first checks exact type matches and only then checks for assignement
@Override
public Enum convertTo(final Integer source, final Class srcClass, final Enum destination, final Class dstClass) {
//Return nth value
//
return
getValues(dstClass)[source];
}
@Override
public Integer convertFrom(final Enum source, final Class srcClass, final Integer destination, final Class dstClass) {
return
source.ordinal();
}
private static Enum[] getValues(final Class clazz) {
//Lookup in the map
//
Enum[]
retval = cache.get(clazz);
//If we don't have values yet, add them to the map
//
if (retval == null) {
try {
cache.putIfAbsent(
clazz,
retval = (Enum[]) MethodUtils.invokeStaticMethod(clazz, "values", null)
);
}
catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new MappingException("Unable to get values() from enum of type: " + clazz.getName());
}
}
//Return
//
return
retval;
}
}
It would have been nice if Dozer included an intermediate class where convertTo and convertFrom were also passed in the types and from that class DozerConverter could be extended. But this worked just fine.