Revision: 896
          http://stripes.svn.sourceforge.net/stripes/?rev=896&view=rev
Author:   tfenne
Date:     2008-04-28 04:11:59 -0700 (Mon, 28 Apr 2008)

Log Message:
-----------
Fix for STS-536: when picking a type converter, if you have a model object with 
a type converter registered, and your model type just happens to implement a 
collection interface, we should prefer that type converter over the one for 
it's scalar/element types.

Modified Paths:
--------------
    
trunk/stripes/src/net/sourceforge/stripes/controller/DefaultActionBeanPropertyBinder.java

Modified: 
trunk/stripes/src/net/sourceforge/stripes/controller/DefaultActionBeanPropertyBinder.java
===================================================================
--- 
trunk/stripes/src/net/sourceforge/stripes/controller/DefaultActionBeanPropertyBinder.java
   2008-04-24 23:38:51 UTC (rev 895)
+++ 
trunk/stripes/src/net/sourceforge/stripes/controller/DefaultActionBeanPropertyBinder.java
   2008-04-28 11:11:59 UTC (rev 896)
@@ -20,14 +20,39 @@
 import net.sourceforge.stripes.action.Wizard;
 import net.sourceforge.stripes.config.Configuration;
 import net.sourceforge.stripes.exception.StripesRuntimeException;
-import net.sourceforge.stripes.util.*;
-import net.sourceforge.stripes.util.bean.*;
-import net.sourceforge.stripes.validation.*;
+import net.sourceforge.stripes.util.CollectionUtil;
+import net.sourceforge.stripes.util.CryptoUtil;
+import net.sourceforge.stripes.util.HtmlUtil;
+import net.sourceforge.stripes.util.Log;
+import net.sourceforge.stripes.util.ReflectUtil;
+import net.sourceforge.stripes.util.bean.BeanUtil;
+import net.sourceforge.stripes.util.bean.ExpressionException;
+import net.sourceforge.stripes.util.bean.NoSuchPropertyException;
+import net.sourceforge.stripes.util.bean.PropertyExpression;
+import net.sourceforge.stripes.util.bean.PropertyExpressionEvaluation;
+import net.sourceforge.stripes.validation.ScopedLocalizableError;
+import net.sourceforge.stripes.validation.TypeConverter;
+import net.sourceforge.stripes.validation.TypeConverterFactory;
+import net.sourceforge.stripes.validation.ValidationError;
+import net.sourceforge.stripes.validation.ValidationErrors;
+import net.sourceforge.stripes.validation.ValidationMetadata;
 import net.sourceforge.stripes.validation.expression.ExpressionValidator;
 
 import javax.servlet.http.HttpServletRequest;
-import java.lang.reflect.*;
-import java.util.*;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
 
 /**
  * <p>
@@ -154,7 +179,7 @@
 
                     // Only do type conversion if there aren't errors already
                     if (errors.isEmpty()) {
-                        convertedValues = convert(bean, name, values, 
scalarType, validationInfo, errors);
+                        convertedValues = convert(bean, name, values, type, 
scalarType, validationInfo, errors);
                         allConvertedFields.put(name, convertedValues);
                     }
 
@@ -695,58 +720,85 @@
      * </p>
      * 
      * @param bean the ActionBean on which the property to convert exists
+     * @param propertyName the name of the property being converted
      * @param values a String array of values to attempt conversion of
+     * @param declaredType the declared type of the ActionBean property
+     * @param scalarType if the declaredType is a collection, map or array 
then this will
+     *           be the type contained within the collection/map value/array, 
otherwise
+     *           the same as declaredType
+     * @param validationInfo the validation metadata for the property if 
defined
      * @param errors a List into which ValidationError objects will be 
populated for any errors
      *            discovered during conversion.
-     * @param validationInfo the validation metadata for the property if 
defined
      * @return List<Object> a List of objects containing only objects of the 
desired type. It is
      *         not guaranteed to be the same length as the values array passed 
in.
      */
     @SuppressWarnings("unchecked")
     protected List<Object> convert(ActionBean bean, ParameterName 
propertyName, String[] values,
-            Class propertyType, ValidationMetadata validationInfo, 
List<ValidationError> errors)
+                                   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
-        TypeConverter converter = 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 = 
this.configuration.getTypeConverterFactory();
+        TypeConverter<?> converter = null;
         Locale locale = bean.getContext().getRequest().getLocale();
+
+        converter = factory.getTypeConverter(declaredType, locale);
         if (validationInfo != null && validationInfo.converter() != null) {
-            converter = 
this.configuration.getTypeConverterFactory().getInstance(
-                    validationInfo.converter(), locale);
+            // If a specific converter was requested and it's the same type as 
one we'd use
+            // for the delcared 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 {
+                converter = factory.getInstance(validationInfo.converter(), 
locale);
+                returnType = scalarType;
+            }
         }
+        // Else, if we got a converter for the declared type (e.g. Foo 
implementes 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 = 
this.configuration.getTypeConverterFactory().getTypeConverter(propertyType,
-                    locale);
+            converter  = factory.getTypeConverter(scalarType, locale);
+            returnType = scalarType;
         }
 
         log.debug("Converting ", values.length, " value(s) using ", (converter 
!= null ?
             "converter " + converter.getClass().getName()
           : "Constructor(String) if available"));
 
-        for (int i = 0; i < values.length; ++i) {
-            String value = values[i];
+        for (String value : values) {
             if (!"".equals(value)) {
                 try {
                     if (validationInfo != null && validationInfo.encrypted()) {
-                        value = CryptoUtil.decrypt(values[i]);
+                        value = CryptoUtil.decrypt(value);
                     }
 
                     Object retval = null;
                     if (converter != null) {
-                        retval = converter.convert(value, propertyType, 
errors);
+                        retval = converter.convert(value, returnType, errors);
                     }
                     else {
-                        Constructor constructor = 
propertyType.getConstructor(String.class);
+                        Constructor<?> constructor = 
returnType.getConstructor(String.class);
                         if (constructor != null) {
                             retval = constructor.newInstance(value);
                         }
                         else {
-                            log.debug("Could not find a way to convert the 
parameter ",
-                                    propertyName.getName(), " to a ", 
propertyType.getSimpleName(),
-                                    ". No TypeConverter could be found and the 
class does not ",
-                                    "have a constructor that takes a single 
String parameter.");
+                            log.debug("Could not find a way to convert the 
parameter ", propertyName.getName(),
+                                      " to a ", returnType.getSimpleName(), ". 
No TypeConverter could be ",
+                                      "found and the class does not ", "have a 
constructor that takes a ",
+                                      "single String parameter.");
                         }
                     }
 
@@ -762,7 +814,6 @@
                     }
                 }
                 catch (Exception e) {
-                    // TODO: figure out what to do, if anything, with these 
exceptions
                     log.warn(e, "Looks like type converter ", converter, " 
threw an exception.");
                 }
             }


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

-------------------------------------------------------------------------
This SF.net email is sponsored by the 2008 JavaOne(SM) Conference 
Don't miss this year's exciting event. There's still time to save $100. 
Use priority code J8TL2D2. 
http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone
_______________________________________________
Stripes-development mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-development

Reply via email to