After all this type about TypeConversion I wanted to share something I have
been using. The usual flow for type conversion involves checking for a
globally registered typeConverter for the class, then checking the Validate
annotation for a converter parameter, and finally trying to instantiate the
object with a single String constructor.

I wanted to have a little more control over everything (and reduce the
amount of type converter classes in my code), for example, even though all
my persistable classes can benefit from a generic dao typeConverter, there
are times when I'd rather not look up an object by its id if I have a list
of them in memory somewhere (a static country list for example). So I
modified the DefaultActionBeanPropertyBinder to add another way of
converting an object, looking for a method named
convert{SimpleClassName}(String string) in the action bean hierarchy and
for the object hierarchy. For example if I wanted to convert a Country
object, and theres no typeConverters registered, it will check for a method
in my ActionBean called convertCountry(String string), if not, it will look
for convertPersistable(String string) (that is my parent class for dao
objects), if not it will check parent actionBeans for convertCountry(String
string), and then convertPersistable(String string) etc. This has made it
very easy to throw quick converters in my code, and its easier to find
where this conversion is happening.

One last thing, I changed the code to remove the step that tries to
instantiate the object with a String constructor, as this led to some very
hard to understand bugs for me. Here is the code if youd like to use it:

public class ActionBeanPropertyBinder extends
DefaultActionBeanPropertyBinder {

// logger
protected final Logger logger = LoggerFactory.getLogger(getClass());

@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
protected List<Object> convert(ActionBean bean, ParameterName propertyName,
String[] values, Class<?> declaredType, Class<?> scalarType,
ValidationMetadata validationInfo, List<ValidationError> errors) throws
Exception {

List<Object> returns = new ArrayList<Object>();
Class returnType = null;

/*
 * Dig up the type converter.  This gets a bit tricky because we need to
handle the following cases:
 * 1. We need to simply find a converter for the declared type of a simple
property
 * 2. We need to find a converter for the element type in a list/array/map
 * 3. We have a domain model object that implements List/Map and has a
converter itself
 */

TypeConverterFactory factory = getConfiguration().getTypeConverterFactory();
Locale locale = bean.getContext().getRequest().getLocale();
TypeConverter<?> converter = factory.getTypeConverter(declaredType, locale);
 if (validationInfo != null && validationInfo.converter() != null)
{
// if a specific converter was requested and it's the same type as one we'd
use
// for the declared type, set the return type appropriately
if (converter != null &&
validationInfo.converter().isAssignableFrom(converter.getClass()))
returnType = declaredType;
 // otherwise assume that it's a converter for the scalar type inside a
collection
else
returnType = scalarType;
 converter = factory.getInstance(validationInfo.converter(), locale);
}
 // else, if we got a converter for the declared type (e.g. Foo implements
List<Bar>)
// then convert for the declared type
else if (converter != null)
{
returnType = declaredType;
}
// else look for a converter for the scalar type (Bar in List<Bar>)
else
{
converter  = factory.getTypeConverter(scalarType, locale);
returnType = scalarType;
}

// convert the value
for (String value : values)
{
if (validationInfo != null && validationInfo.encrypted())
value = CryptoUtil.decrypt(value);

if (!"".equals(value))
{
try
{
Object retval = null;
 if (converter != null)
retval = converter.convert(value, returnType, errors);
 else
{
Method method = findConvertMethodForActionBeanHierarchy(bean.getClass(),
returnType);

if (method != null)
retval = method.invoke(bean, new Object[] {value});

else
throw new UnsupportedOperationException("Could not convert
parameter=["+propertyName.getName()+"] " +
"with value=["+value+"] to type=["+returnType.getSimpleName()+"]. " +
"No type converter was registered or actionBean method found with
signature: " +
"public "+returnType.getSimpleName()+" convert"+returnType.getSimpleName()+
"(String input)");
}

// if we managed to get a non-null converted value, add it to the return set
if (retval != null)
returns.add(retval);

// set the field name and value on the error
for (ValidationError error : errors)
{
error.setFieldName(propertyName.getStrippedName());
error.setFieldValue(value);
}
 }
catch (Exception e)
{
logger.error("Type converter=["+converter+"] threw an exception for
value=["+value+"]", e);
}
}
}

return returns;
}

/**
 * Traverses the action bean hierarchy to search for a suitable convert
method.
 *
 * @param actionBean
 * @param targetType
 * @return Method
 */
private Method findConvertMethodForActionBeanHierarchy(Class<?> actionBean,
Class<?> targetType) {
Method method = findConvertMethodForTargetTypeHierarchy(actionBean,
targetType);

if(method != null)
return method;

return actionBean.getSuperclass() == null ? null :
findConvertMethodForActionBeanHierarchy(actionBean.getSuperclass(),
targetType);
}

/**
 * Traverses the target type hierarchy to search for a suitable convert
method.
 *
 * @param actionBean
 * @param targetType
 * @return Method
 */
private Method findConvertMethodForTargetTypeHierarchy(Class<?> actionBean,
Class<?> targetType) {
for(Method method : actionBean.getDeclaredMethods())
if(method.getName().equalsIgnoreCase("convert"+targetType.getSimpleName()))
if(method.getParameterTypes().length == 1 && method.getParameterTypes()[0]
== String.class)
return method;
 return targetType.getSuperclass() == null ? null :
findConvertMethodForTargetTypeHierarchy(actionBean,
targetType.getSuperclass());
}

}


Don't forget to register it in web.xml as a stripes parameter:
<init-param>
    <param-name>ActionBeanPropertyBinder.Class</param-name>
    <param-value>ActionBeanPropertyBinder</param-value>
</init-param>
------------------------------------------------------------------------------
All the data continuously generated in your IT infrastructure 
contains a definitive record of customers, application performance, 
security threats, fraudulent activity, and more. Splunk takes this 
data and makes sense of it. IT sense. And common sense.
http://p.sf.net/sfu/splunk-novd2d
_______________________________________________
Stripes-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-users

Reply via email to