Commits

Anonymous committed 8836b09

Fixing multiple issues:
o CreateIfNull doesn't works
o Deprecate AnnotationXWorkConverter and refactor methods to XWorkConverter
o Deprecate GenericsObjectTypeDeterminer and refactor methods to DefaultObjectTypeDeterminer

Issue Number: XW-527, XW-540, XW-539

git-svn-id: http://svn.opensymphony.com/svn/xwork/branches/2.0@1548e221344d-f017-0410-9bd5-d282ab1896d7

  • Participants
  • Parent commits 7b720b2
  • Branches 2.0, xwork_2_0_7

Comments (0)

Files changed (8)

src/java/com/opensymphony/xwork2/conversion/annotations/TypeConversion.java

 
     /**
      * The ConversionRule can be a PROPERTY, KEY, KEY_PROPERTY, ELEMENT, COLLECTION (deprecated) or a MAP.
-     * Note: Collection and Map vonversion rules can be determined via com.opensymphony.xwork2.util.GenericsObjectTypeDeterminer.
+     * Note: Collection and Map vonversion rules can be determined via com.opensymphony.xwork2.util.DefaultObjectTypeDeterminer.
      *
-     * @see com.opensymphony.xwork2.util.GenericsObjectTypeDeterminer
+     * @see com.opensymphony.xwork2.util.DefaultObjectTypeDeterminer
      */
     ConversionRule rule() default ConversionRule.PROPERTY;
 

src/java/com/opensymphony/xwork2/util/AnnotationXWorkConverter.java

  */
 package com.opensymphony.xwork2.util;
 
-import com.opensymphony.xwork2.conversion.annotations.Conversion;
-import com.opensymphony.xwork2.conversion.annotations.ConversionRule;
-import com.opensymphony.xwork2.conversion.annotations.ConversionType;
-import com.opensymphony.xwork2.conversion.annotations.TypeConversion;
-import ognl.TypeConverter;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import java.io.InputStream;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Properties;
-
-
 /**
  * <!-- START SNIPPET: javadoc -->
  * <p/>
  * @author <a href="mailto:plightbo@gmail.com">Pat Lightbody</a>
  * @author Rainer Hermanns
  * @see com.opensymphony.xwork2.util.XWorkConverter
+ * @deprecated Since XWork 2.0.4, the implementation of XWorkConverter handles the processing of annotations.
  */
 public class AnnotationXWorkConverter extends XWorkConverter {
-
-    private static final Log _log = LogFactory.getLog(AnnotationXWorkConverter.class);  
-  
-    /**
-     * Looks for converter mappings for the specified class and adds it to an existing map.  Only new converters are
-     * added.  If a converter is defined on a key that already exists, the converter is ignored.
-     *
-     * @param mapping an existing map to add new converter mappings to
-     * @param clazz   class to look for converter mappings for
-     */
-    void addConverterMapping(Map mapping, Class clazz) {
-
-        try {
-            InputStream is = FileManager.loadFile(buildConverterFilename(clazz), clazz);
-
-            if (is != null) {
-                Properties prop = new Properties();
-                prop.load(is);
-
-                Iterator it = prop.entrySet().iterator();
-
-                while (it.hasNext()) {
-                    Map.Entry entry = (Map.Entry) it.next();
-                    String key = (String) entry.getKey();
-
-                    if (mapping.containsKey(key)) {
-                        break;
-                    }
-                    if (LOG.isDebugEnabled()) {
-                        LOG.debug(key + ":" + entry.getValue());
-                    }
-
-                    if (key.startsWith(DefaultObjectTypeDeterminer.KEY_PROPERTY_PREFIX)
-                            || key.startsWith(DefaultObjectTypeDeterminer.CREATE_IF_NULL_PREFIX)) {
-                        mapping.put(key, entry.getValue());
-                    }
-                    //for properties of classes
-                    else if (!(key.startsWith(DefaultObjectTypeDeterminer.ELEMENT_PREFIX) ||
-                            key.startsWith(DefaultObjectTypeDeterminer.KEY_PREFIX) ||
-                            key.startsWith(DefaultObjectTypeDeterminer.DEPRECATED_ELEMENT_PREFIX))
-                            ) {
-                        mapping.put(key, createTypeConverter((String) entry.getValue()));
-                    }
-                    //for keys of Maps
-                    else if (key.startsWith(DefaultObjectTypeDeterminer.KEY_PREFIX)) {
-
-                        Class converterClass = Thread.currentThread().getContextClassLoader().loadClass((String) entry.getValue());
-                        if (LOG.isDebugEnabled()) {
-                            LOG.debug("Converter class: " + converterClass);
-                        }
-                        //check if the converter is a type converter if it is one
-                        //then just put it in the map as is. Otherwise
-                        //put a value in for the type converter of the class
-                        if (converterClass.isAssignableFrom(TypeConverter.class)) {
-
-                            mapping.put(key, createTypeConverter((String) entry.getValue()));
-
-
-                        } else {
-
-                            mapping.put(key, converterClass);
-                            if (LOG.isDebugEnabled()) {
-                                LOG.debug("Object placed in mapping for key "
-                                        + key
-                                        + " is "
-                                        + mapping.get(key));
-                            }
-
-                        }
-
-
-                    }
-                    //elements(values) of maps / lists
-                    else {
-                        mapping.put(key, Thread.currentThread().getContextClassLoader().loadClass((String) entry.getValue()));
-                    }
-                }
-            }
-        } catch (Exception ex) {
-            LOG.error("Problem loading properties for " + clazz.getName(), ex);
-        }
-
-        // Process annotations
-        Annotation[] annotations = clazz.getAnnotations();
-
-        for (Annotation annotation : annotations) {
-            if (annotation instanceof Conversion) {
-                Conversion conversion = (Conversion) annotation;
-
-                for (TypeConversion tc : conversion.conversions()) {
-
-                    String key = tc.key();
-
-                    if (mapping.containsKey(key)) {
-                        break;
-                    }
-                    if (LOG.isDebugEnabled()) {
-                        LOG.debug(key + ":" + key);
-                    }
-
-                    if (key != null) {
-                        try {
-                        	if (tc.type()  == ConversionType.APPLICATION) {
-                                defaultMappings.put(key, createTypeConverter(tc.converter()));
-                            } else {
-                                if (tc.rule().toString().equals(ConversionRule.KEY_PROPERTY) || tc.rule().toString().equals(ConversionRule.CREATE_IF_NULL)) {
-                                    mapping.put(key, tc.value());
-                                }
-                                //for properties of classes
-                                else if (!(tc.rule().toString().equals(ConversionRule.ELEMENT.toString())) ||
-                                        tc.rule().toString().equals(ConversionRule.KEY.toString()) ||
-                                        tc.rule().toString().equals(ConversionRule.COLLECTION.toString())
-                                        ) {
-                                    mapping.put(key, createTypeConverter(tc.converter()));
-                                }
-                                //for keys of Maps
-                                else if (tc.rule().toString().equals(ConversionRule.KEY.toString())) {
-                                    Class converterClass = Thread.currentThread().getContextClassLoader().loadClass(tc.converter());
-                                    if (LOG.isDebugEnabled()) {
-                                        LOG.debug("Converter class: " + converterClass);
-                                    }
-                                    //check if the converter is a type converter if it is one
-                                    //then just put it in the map as is. Otherwise
-                                    //put a value in for the type converter of the class
-                                    if (converterClass.isAssignableFrom(TypeConverter.class)) {
-                                        mapping.put(key, createTypeConverter(tc.converter()));
-                                    } else {
-                                        mapping.put(key, converterClass);
-                                        if (LOG.isDebugEnabled()) {
-                                            LOG.debug("Object placed in mapping for key "
-                                                    + key
-                                                    + " is "
-                                                    + mapping.get(key));
-                                        }
-
-                                    }
-
-                                }
-                                //elements(values) of maps / lists
-                                else {
-                                    mapping.put(key, Thread.currentThread().getContextClassLoader().loadClass(tc.converter()));
-                                }
-                            }
-                        } catch (Exception e) {
-                        }
-                    }
-                }
-            }
-        }
-
-        Method[] methods = clazz.getMethods();
-
-        for (Method method : methods) {
-
-            annotations = method.getAnnotations();
-
-            for (Annotation annotation : annotations) {
-                if (annotation instanceof TypeConversion) {
-                    TypeConversion tc = (TypeConversion) annotation;
-
-                    String key = tc.key();
-                    if (mapping.containsKey(key)) {
-                        break;
-                    }
-                    // Default to the property name
-                    if ( key != null && key.length() == 0) {
-                        key = AnnotationUtils.resolvePropertyName(method);
-                        _log.debug("key from method name... " + key + " - " + method.getName());
-                    }
-
-
-                    if (LOG.isDebugEnabled()) {
-                        LOG.debug(key + ":" + key);
-                    }
-
-                    if (key != null) {
-                        try {
-                        	if (tc.type() == ConversionType.APPLICATION) {
-                                defaultMappings.put(key, createTypeConverter(tc.converter()));
-                            } else {
-                                if (tc.rule().toString().equals(ConversionRule.KEY_PROPERTY)) {
-                                    mapping.put(key, tc.value());
-                                }
-                                //for properties of classes
-                                else if (!(tc.rule().toString().equals(ConversionRule.ELEMENT.toString())) ||
-                                        tc.rule().toString().equals(ConversionRule.KEY.toString()) ||
-                                        tc.rule().toString().equals(ConversionRule.COLLECTION.toString())
-                                        ) {
-                                    mapping.put(key, createTypeConverter(tc.converter()));
-                                }
-                                //for keys of Maps
-                                else if (tc.rule().toString().equals(ConversionRule.KEY.toString())) {
-                                    Class converterClass = Thread.currentThread().getContextClassLoader().loadClass(tc.converter());
-                                    if (LOG.isDebugEnabled()) {
-                                        LOG.debug("Converter class: " + converterClass);
-                                    }
-                                    //check if the converter is a type converter if it is one
-                                    //then just put it in the map as is. Otherwise
-                                    //put a value in for the type converter of the class
-                                    if (converterClass.isAssignableFrom(TypeConverter.class)) {
-                                        mapping.put(key, createTypeConverter(tc.converter()));
-                                    } else {
-                                        mapping.put(key, converterClass);
-                                        if (LOG.isDebugEnabled()) {
-                                            LOG.debug("Object placed in mapping for key "
-                                                    + key
-                                                    + " is "
-                                                    + mapping.get(key));
-                                        }
-
-                                    }
-
-                                }
-                                //elements(values) of maps / lists
-                                else {
-                                    mapping.put(key, Thread.currentThread().getContextClassLoader().loadClass(tc.converter()));
-                                }
-                            }
-                        } catch (Exception e) {
-                        }
-                    }
-                }
-            }
-
-        }
-    }
 }

src/java/com/opensymphony/xwork2/util/DefaultObjectTypeDeterminer.java

 package com.opensymphony.xwork2.util;
 
 import java.util.Map;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.lang.reflect.ParameterizedType;
+import java.beans.IntrospectionException;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import ognl.OgnlRuntime;
+import ognl.OgnlException;
 
 /**
  * <!-- START SNIPPET: javadoc -->
  * @see XWorkMapPropertyAccessor
  */
 public class DefaultObjectTypeDeterminer implements ObjectTypeDeterminer {
+    
     protected static final Log LOG = LogFactory.getLog(DefaultObjectTypeDeterminer.class);
 
     public static final String KEY_PREFIX = "Key_";
     public static final String CREATE_IF_NULL_PREFIX = "CreateIfNull_";
     public static final String DEPRECATED_ELEMENT_PREFIX = "Collection_";
 
-
     /**
-     * Determines the key class by looking for the value of Key_${property} in the properties file for the given class.
+     * Determines the key class by looking for the value of @Key annotation for the given class.
+     * If no annotation is found, the key class is determined by using the generic parametrics.
+     *
+     * As fallback, it determines the key class by looking for the value of Key_${property} in the properties
+     * file for the given class.
      *
      * @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
      * @param property    the property of the Map or Collection for the given parent class
-     * @see ObjectTypeDeterminer#getKeyClass(Class, String)
+     * @see com.opensymphony.xwork2.util.ObjectTypeDeterminer#getKeyClass(Class, String)
      */
     public Class getKeyClass(Class parentClass, String property) {
-        return (Class) XWorkConverter.getInstance()
-                .getConverter(parentClass, KEY_PREFIX + property);
+        Key annotation = getAnnotation(parentClass, property, Key.class);
+
+        if (annotation != null) {
+            return annotation.value();
+        }
+
+        Class clazz = getClass(parentClass, property, false);
+
+        if (clazz != null) {
+            return clazz;
+        }
+
+        return (Class) XWorkConverter.getInstance().getConverter(parentClass, KEY_PREFIX + property);
     }
 
+
     /**
-     * Determines the key class by looking for the value of Element_${property} in the properties file for the given
-     * class. Also looks for the deprecated Collection_${property}
+     * Determines the element class by looking for the value of @Element annotation for the given
+     * class.
+     * If no annotation is found, the element class is determined by using the generic parametrics.
+     *
+     * As fallback, it determines the key class by looking for the value of Element_${property} in the properties
+     * file for the given class. Also looks for the deprecated Collection_${property}
      *
      * @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
      * @param property    the property of the Map or Collection for the given parent class
-     * @see ObjectTypeDeterminer#getElementClass(Class, String, Object)
+     * @see com.opensymphony.xwork2.util.ObjectTypeDeterminer#getElementClass(Class, String, Object)
      */
     public Class getElementClass(Class parentClass, String property, Object key) {
-        Class clazz = (Class) XWorkConverter.getInstance()
-                .getConverter(parentClass, ELEMENT_PREFIX + property);
+        Element annotation = getAnnotation(parentClass, property, Element.class);
+
+        if (annotation != null) {
+            return annotation.value();
+        }
+
+        Class clazz = getClass(parentClass, property, true);
+
+        if (clazz != null) {
+            return clazz;
+        }
+
+        clazz = (Class) XWorkConverter.getInstance().getConverter(parentClass, ELEMENT_PREFIX + property);
+
         if (clazz == null) {
             clazz = (Class) XWorkConverter.getInstance()
                     .getConverter(parentClass, DEPRECATED_ELEMENT_PREFIX + property);
             }
         }
         return clazz;
+
     }
 
+
     /**
-     * Determines the String key property for a Collection by getting it from the conversion properties file using the
-     * KeyProperty_ prefix. KeyProperty_${property}=somePropertyOfBeansInTheSet
+     * Determines the key property for a Collection by getting it from the @KeyProperty annotation.
+     *
+     * As fallback, it determines the String key property for a Collection by getting it from the conversion properties
+     * file using the KeyProperty_ prefix. KeyProperty_${property}=somePropertyOfBeansInTheSet
      *
-     * @param parentClass the Class which contains as a property the Map or Collection we are finding the KeyProperty for.
+     * @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
      * @param property    the property of the Map or Collection for the given parent class
+     * @see com.opensymphony.xwork2.util.ObjectTypeDeterminer#getKeyProperty(Class, String)
      */
     public String getKeyProperty(Class parentClass, String property) {
-        return (String) XWorkConverter.getInstance()
-                .getConverter(parentClass, KEY_PROPERTY_PREFIX + property);
+        KeyProperty annotation = getAnnotation(parentClass, property, KeyProperty.class);
+
+        if (annotation != null) {
+            return annotation.value();
+        }
+
+        return (String) XWorkConverter.getInstance().getConverter(parentClass, KEY_PROPERTY_PREFIX + property);
     }
-    
+
+
     /**
-     * Determines the boolean CreateIfNull property for a Collection or Map by getting it from the conversion properties
-     * file using the CreateIfNull_ prefix. CreateIfNull_${property}=true|false
+     * Determines the createIfNull property for a Collection or Map by getting it from the @CreateIfNull annotation.
      *
-     * @param parentClass the Class which contains as a property the Map or Collection we are finding the CreateIfNull for.
-     * @param property    the property of the Map or Collection for the given parent class
+     * As fallback, it determines the boolean CreateIfNull property for a Collection or Map by getting it from the
+     * conversion properties file using the CreateIfNull_ prefix. CreateIfNull_${property}=true|false
+     *
+     * @param parentClass     the Class which contains as a property the Map or Collection we are finding the key for.
+     * @param property        the property of the Map or Collection for the given parent class
      * @param target
      * @param keyProperty
-     * @param isIndexAccessed
+     * @param isIndexAccessed <tt>true</tt>, if the collection or map is accessed via index, <tt>false</tt> otherwise.
      * @return <tt>true</tt>, if the Collection or Map should be created, <tt>false</tt> otherwise.
+     * @see ObjectTypeDeterminer#getKeyProperty(Class, String)
      */
     public boolean shouldCreateIfNew(Class parentClass,
-            String property, 
-            Object target,
-            String keyProperty,
-            boolean isIndexAccessed) {
-        
-        	String configValue = (String) XWorkConverter.getInstance()
-                    .getConverter(parentClass, CREATE_IF_NULL_PREFIX + property);
-        	//check if a value is in the config
-        	if (configValue!=null) {
-        	    if (configValue.equals("true")) {
-        	        return true;
-        	    }
-        	    if (configValue.equals("false")) {
-        	        return false;
-        	    }        	      
-        	}
-        	
-        	//default values depend on target type 
-        	//and whether this is accessed by an index
-        	//in the case of List
-        	if ((target instanceof Map) || isIndexAccessed) {
-        	    return true;
-        	}	else {
-        	    return false;
-        	}
+                                     String property,
+                                     Object target,
+                                     String keyProperty,
+                                     boolean isIndexAccessed) {
+
+        CreateIfNull annotation = getAnnotation(parentClass, property, CreateIfNull.class);
+
+        if (annotation != null) {
+            return annotation.value();
+        }
+
+        String configValue = (String) XWorkConverter.getInstance().getConverter(parentClass, CREATE_IF_NULL_PREFIX + property);
+        //check if a value is in the config
+        if (configValue!=null) {
+            if (configValue.equals("true")) {
+                return true;
+            }
+            if (configValue.equals("false")) {
+                return false;
+            }
+        }
+
+        //default values depend on target type
+        //and whether this is accessed by an index
+        //in the case of List
+        if ((target instanceof Map) || isIndexAccessed) {
+            return true;
+        }	else {
+            return false;
+        }
+
+    }
+
+    /**
+     * Retrieves an annotation for the specified property of field, setter or getter.
+     *
+     * @param <T>             the annotation type to be retrieved
+     * @param parentClass     the class
+     * @param property        the property
+     * @param annotationClass the annotation
+     * @return the field or setter/getter annotation or <code>null</code> if not found
+     */
+    protected <T extends Annotation> T getAnnotation(Class parentClass, String property, Class<T> annotationClass) {
+        T annotation = null;
+        Field field = OgnlRuntime.getField(parentClass, property);
+
+        if (field != null) {
+            annotation = field.getAnnotation(annotationClass);
+        }
+        if (annotation == null) { // HINT: try with setter
+            annotation = getAnnotationFromSetter(parentClass, property, annotationClass);
+        }
+        if (annotation == null) { // HINT: try with getter
+            annotation = getAnnotationFromGetter(parentClass, property, annotationClass);
+        }
+
+        return annotation;
+    }
+
+    /**
+     * Retrieves an annotation for the specified field of getter.
+     *
+     * @param parentClass     the Class which contains as a property the Map or Collection we are finding the key for.
+     * @param property        the property of the Map or Collection for the given parent class
+     * @param annotationClass The annotation
+     * @return concrete Annotation instance or <tt>null</tt> if none could be retrieved.
+     */
+    private <T extends Annotation>T getAnnotationFromGetter(Class parentClass, String property, Class<T> annotationClass) {
+        try {
+            Method getter = OgnlRuntime.getGetMethod(null, parentClass, property);
+
+            if (getter != null) {
+                return getter.getAnnotation(annotationClass);
+            }
+        }
+        catch (OgnlException ognle) {
+            ; // ignore
+        }
+        catch (IntrospectionException ie) {
+            ; // ignore
+        }
+        return null;
+    }
+
+    /**
+     * Retrieves an annotation for the specified field of setter.
+     *
+     * @param parentClass     the Class which contains as a property the Map or Collection we are finding the key for.
+     * @param property        the property of the Map or Collection for the given parent class
+     * @param annotationClass The annotation
+     * @return concrete Annotation instance or <tt>null</tt> if none could be retrieved.
+     */
+    private <T extends Annotation>T getAnnotationFromSetter(Class parentClass, String property, Class<T> annotationClass) {
+        try {
+            Method setter = OgnlRuntime.getSetMethod(null, parentClass, property);
+
+            if (setter != null) {
+                return setter.getAnnotation(annotationClass);
+            }
+        }
+        catch (OgnlException ognle) {
+            ; // ignore
+        }
+        catch (IntrospectionException ie) {
+            ; // ignore
+        }
+        return null;
+    }
+
+    /**
+     * Returns the class for the given field via generic type check.
+     *
+     * @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
+     * @param property    the property of the Map or Collection for the given parent class
+     * @param element     <tt>true</tt> for indexed types and Maps.
+     * @return Class of the specified field.
+     */
+    private Class getClass(Class parentClass, String property, boolean element) {
+
+
+        try {
+
+            Field field = OgnlRuntime.getField(parentClass, property);
+
+            Type genericType = null;
+
+            // Check fields first
+            if (field != null) {
+                genericType = field.getGenericType();
+            }
+
+            // Try to get ParameterType from setter method
+            if (genericType == null || !(genericType instanceof ParameterizedType)) {
+                try {
+                    Method setter = OgnlRuntime.getSetMethod(null, parentClass, property);
+                    genericType = setter.getGenericParameterTypes()[0];
+                }
+                catch (OgnlException ognle) {
+                    ; // ignore
+                }
+                catch (IntrospectionException ie) {
+                    ; // ignore
+                }
+            }
+
+            // Try to get ReturnType from getter method
+            if (genericType == null || !(genericType instanceof ParameterizedType)) {
+                try {
+                    Method getter = OgnlRuntime.getGetMethod(null, parentClass, property);
+                    genericType = getter.getGenericReturnType();
+                }
+                catch (OgnlException ognle) {
+                    ; // ignore
+                }
+                catch (IntrospectionException ie) {
+                    ; // ignore
+                }
+            }
+
+            if (genericType instanceof ParameterizedType) {
+
+
+                ParameterizedType type = (ParameterizedType) genericType;
+
+                int index = (element && type.getRawType().toString().contains(Map.class.getName())) ? 1 : 0;
+
+                Type resultType = type.getActualTypeArguments()[index];
+
+                if ( resultType instanceof ParameterizedType) {
+                    return resultType.getClass();
+                }
+                return (Class) resultType;
+
+            }
+        } catch (Exception e) {
+            if ( LOG.isDebugEnabled()) {
+                LOG.debug("Error while retrieving generic property class for property=" + property, e);
+            }
+        }
+        return null;
     }
 }

src/java/com/opensymphony/xwork2/util/GenericsObjectTypeDeterminer.java

 
 package com.opensymphony.xwork2.util;
 
-import ognl.OgnlRuntime;
-import ognl.OgnlException;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.lang.reflect.Method;
-import java.lang.annotation.Annotation;
-import java.util.Map;
-import java.beans.IntrospectionException;
-
 /**
  * GenericsObjectTypeDeterminer
- *
+ * 
  * @author Patrick Lightbody
  * @author Rainer Hermanns
  * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
+ * @deprecated Use DefaultObjectTypeDeterminer instead. Since XWork 2.0.4 the DefaultObjectTypeDeterminer handles the
+ *             annotation processing.
  */
 public class GenericsObjectTypeDeterminer extends DefaultObjectTypeDeterminer {
-
-
-    /**
-     * Determines the key class by looking for the value of @Key annotation for the given class.
-     * If no annotation is found, the key class is determined by using the generic parametrics.
-     *
-     * @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
-     * @param property    the property of the Map or Collection for the given parent class
-     * @see com.opensymphony.xwork2.util.ObjectTypeDeterminer#getKeyClass(Class, String)
-     */
-    public Class getKeyClass(Class parentClass, String property) {
-        Key annotation = getAnnotation(parentClass, property, Key.class);
-
-        if (annotation != null) {
-            return annotation.value();
-        }
-
-        Class clazz = getClass(parentClass, property, false);
-
-        if (clazz != null) {
-            return clazz;
-        }
-
-        return super.getKeyClass(parentClass, property);
-    }
-
-    /**
-     * Determines the element class by looking for the value of @Element annotation for the given
-     * class.
-     * If no annotation is found, the element class is determined by using the generic parametrics.
-     *
-     * @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
-     * @param property    the property of the Map or Collection for the given parent class
-     * @see com.opensymphony.xwork2.util.ObjectTypeDeterminer#getElementClass(Class, String, Object)
-     */
-    public Class getElementClass(Class parentClass, String property, Object key) {
-        Element annotation = getAnnotation(parentClass, property, Element.class);
-
-        if (annotation != null) {
-            return annotation.value();
-        }
-
-        Class clazz = getClass(parentClass, property, true);
-
-        if (clazz != null) {
-            return clazz;
-        }
-
-        return super.getElementClass(parentClass, property, key);
-    }
-
-    /**
-     * Determines the key property for a Collection by getting it from the @KeyProperty annotation.
-     *
-     * @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
-     * @param property    the property of the Map or Collection for the given parent class
-     * @see com.opensymphony.xwork2.util.ObjectTypeDeterminer#getKeyProperty(Class, String)
-     */
-    public String getKeyProperty(Class parentClass, String property) {
-        KeyProperty annotation = getAnnotation(parentClass, property, KeyProperty.class);
-
-        if (annotation != null) {
-            return annotation.value();
-        }
-
-        return super.getKeyProperty(parentClass, property);
-    }
-
-    /**
-     * Determines the createIfNull property for a Collection or Map by getting it from the @CreateIfNull annotation.
-     *
-     * @param parentClass     the Class which contains as a property the Map or Collection we are finding the key for.
-     * @param property        the property of the Map or Collection for the given parent class
-     * @param target
-     * @param keyProperty
-     * @param isIndexAccessed <tt>true</tt>, if the collection or map is accessed via index, <tt>false</tt> otherwise.
-     * @see ObjectTypeDeterminer#getKeyProperty(Class, String)
-     */
-    public boolean shouldCreateIfNew(Class parentClass,
-                                     String property,
-                                     Object target,
-                                     String keyProperty,
-                                     boolean isIndexAccessed) {
-
-        CreateIfNull annotation = getAnnotation(parentClass, property, CreateIfNull.class);
-
-        if (annotation != null) {
-            return annotation.value();
-        }
-
-        return super.shouldCreateIfNew(parentClass, property, target, keyProperty, isIndexAccessed);
-    }
-
-    /**
-     * Retrieves an annotation for the specified property of field, setter or getter.
-     *
-     * @param <T>             the annotation type to be retrieved
-     * @param parentClass     the class
-     * @param property        the property
-     * @param annotationClass the annotation
-     * @return the field or setter/getter annotation or <code>null</code> if not found
-     */
-    protected <T extends Annotation> T getAnnotation(Class parentClass, String property, Class<T> annotationClass) {
-        T annotation = null;
-        Field field = OgnlRuntime.getField(parentClass, property);
-
-        if (field != null) {
-            annotation = field.getAnnotation(annotationClass);
-        }
-        if (annotation == null) { // HINT: try with setter
-            annotation = getAnnotationFromSetter(parentClass, property, annotationClass);
-        }
-        if (annotation == null) { // HINT: try with getter
-            annotation = getAnnotationFromGetter(parentClass, property, annotationClass);
-        }
-
-        return annotation;
-    }
-
-    /**
-     * Retrieves an annotation for the specified field of getter.
-     *
-     * @param parentClass     the Class which contains as a property the Map or Collection we are finding the key for.
-     * @param property        the property of the Map or Collection for the given parent class
-     * @param annotationClass The annotation
-     * @return concrete Annotation instance or <tt>null</tt> if none could be retrieved.
-     */
-    private <T extends Annotation>T getAnnotationFromGetter(Class parentClass, String property, Class<T> annotationClass) {
-        try {
-            Method getter = OgnlRuntime.getGetMethod(null, parentClass, property);
-
-            if (getter != null) {
-                return getter.getAnnotation(annotationClass);
-            }
-        }
-        catch (OgnlException ognle) {
-            ; // ignore
-        }
-        catch (IntrospectionException ie) {
-            ; // ignore
-        }
-        return null;
-    }
-
-    /**
-     * Retrieves an annotation for the specified field of setter.
-     *
-     * @param parentClass     the Class which contains as a property the Map or Collection we are finding the key for.
-     * @param property        the property of the Map or Collection for the given parent class
-     * @param annotationClass The annotation
-     * @return concrete Annotation instance or <tt>null</tt> if none could be retrieved.
-     */
-    private <T extends Annotation>T getAnnotationFromSetter(Class parentClass, String property, Class<T> annotationClass) {
-        try {
-            Method setter = OgnlRuntime.getSetMethod(null, parentClass, property);
-
-            if (setter != null) {
-                return setter.getAnnotation(annotationClass);
-            }
-        }
-        catch (OgnlException ognle) {
-            ; // ignore
-        }
-        catch (IntrospectionException ie) {
-            ; // ignore
-        }
-        return null;
-    }
-
-    /**
-     * Returns the class for the given field via generic type check.
-     *
-     * @param parentClass the Class which contains as a property the Map or Collection we are finding the key for.
-     * @param property    the property of the Map or Collection for the given parent class
-     * @param element     <tt>true</tt> for indexed types and Maps.
-     * @return Class of the specified field.
-     */
-    private Class getClass(Class parentClass, String property, boolean element) {
-
-
-        try {
-
-            Field field = OgnlRuntime.getField(parentClass, property);
-
-            Type genericType = null;
-
-            // Check fields first
-            if (field != null) {
-                genericType = field.getGenericType();
-            }
-
-            // Try to get ParameterType from setter method
-            if (genericType == null || !(genericType instanceof ParameterizedType)) {
-                try {
-                    Method setter = OgnlRuntime.getSetMethod(null, parentClass, property);
-                    genericType = setter.getGenericParameterTypes()[0];
-                }
-                catch (OgnlException ognle) {
-                    ; // ignore
-                }
-                catch (IntrospectionException ie) {
-                    ; // ignore
-                }
-            }
-
-            // Try to get ReturnType from getter method
-            if (genericType == null || !(genericType instanceof ParameterizedType)) {
-                try {
-                    Method getter = OgnlRuntime.getGetMethod(null, parentClass, property);
-                    genericType = getter.getGenericReturnType();
-                }
-                catch (OgnlException ognle) {
-                    ; // ignore
-                }
-                catch (IntrospectionException ie) {
-                    ; // ignore
-                }
-            }
-
-            if (genericType instanceof ParameterizedType) {
-
-
-                ParameterizedType type = (ParameterizedType) genericType;
-
-                int index = (element && type.getRawType().toString().contains(Map.class.getName())) ? 1 : 0;
-
-                Type resultType = type.getActualTypeArguments()[index];
-
-                if ( resultType instanceof ParameterizedType) {
-                    return resultType.getClass();
-                }
-                return (Class) resultType;
-
-            }
-        } catch (Exception e) {
-            if ( LOG.isDebugEnabled()) {
-                LOG.debug("Error while retrieving generic property class for property=" + property, e);
-            }
-        }
-        return null;
-    }
 }

src/java/com/opensymphony/xwork2/util/ObjectTypeDeterminerFactory.java

  * <p/>
  * Will use <code>com.opensymphony.xwork2.util.GenericsObjectTypeDeterminer</code> by default.
  *
- * @see com.opensymphony.xwork2.util.GenericsObjectTypeDeterminer
  * @see com.opensymphony.xwork2.util.ObjectTypeDeterminer
  * @see com.opensymphony.xwork2.util.DefaultObjectTypeDeterminer
  *
 public class ObjectTypeDeterminerFactory {
     private static final Log LOG = LogFactory.getLog(ObjectTypeDeterminerFactory.class);
 
-    private static ObjectTypeDeterminer instance = new GenericsObjectTypeDeterminer();
+    private static ObjectTypeDeterminer instance = new DefaultObjectTypeDeterminer();
 
     static {
-        LOG.info("Setting GenericsObjectTypeDeterminer as default ...");
+        LOG.info("Setting DefaultObjectTypeDeterminer as default ...");
     }
 
     /**

src/java/com/opensymphony/xwork2/util/XWorkConverter.java

 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.annotation.Annotation;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.ObjectFactory;
 import com.opensymphony.xwork2.XWorkMessages;
+import com.opensymphony.xwork2.conversion.annotations.Conversion;
+import com.opensymphony.xwork2.conversion.annotations.TypeConversion;
+import com.opensymphony.xwork2.conversion.annotations.ConversionType;
+import com.opensymphony.xwork2.conversion.annotations.ConversionRule;
 
 
 /**
  * @see XWorkBasicConverter
  */
 public class XWorkConverter extends DefaultTypeConverter {
+
     private static XWorkConverter instance;
     protected static final Log LOG = LogFactory.getLog(XWorkConverter.class);
     public static final String REPORT_CONVERSION_ERRORS = "report.conversion.errors";
 
     public static XWorkConverter getInstance() {
          if (instance == null) {
-             try {
-                 Class clazz = Thread.currentThread().getContextClassLoader().loadClass("com.opensymphony.xwork2.util.AnnotationXWorkConverter");
-                 instance = (XWorkConverter) clazz.newInstance();
-                 LOG.info("Detected AnnotationXWorkConverter, initializing it...");
-             } catch (ClassNotFoundException e) {
-                 // this is fine, just fall back to the default object type determiner
-             } catch (Exception e) {
-                 LOG.error("Exception when trying to create new AnnotationXWorkConverter", e);
-             }
-             if ( instance == null ) {
-                 instance = new XWorkConverter();
-             }
+             instance = new XWorkConverter();
          }
 
          return instance;
         } catch (Exception ex) {
             LOG.error("Problem loading properties for " + clazz.getName(), ex);
         }
+
+        // Process annotations
+        Annotation[] annotations = clazz.getAnnotations();
+
+        for (Annotation annotation : annotations) {
+            if (annotation instanceof Conversion) {
+                Conversion conversion = (Conversion) annotation;
+
+                for (TypeConversion tc : conversion.conversions()) {
+
+                    String key = tc.key();
+
+                    if (mapping.containsKey(key)) {
+                        break;
+                    }
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug(key + ":" + key);
+                    }
+
+                    if (key != null) {
+                        try {
+                        	if (tc.type()  == ConversionType.APPLICATION) {
+                                defaultMappings.put(key, createTypeConverter(tc.converter()));
+                            } else {
+                                if (tc.rule().toString().equals(ConversionRule.KEY_PROPERTY) || tc.rule().toString().equals(ConversionRule.CREATE_IF_NULL)) {
+                                    mapping.put(key, tc.value());
+                                }
+                                //for properties of classes
+                                else if (!(tc.rule().toString().equals(ConversionRule.ELEMENT.toString())) ||
+                                        tc.rule().toString().equals(ConversionRule.KEY.toString()) ||
+                                        tc.rule().toString().equals(ConversionRule.COLLECTION.toString())
+                                        ) {
+                                    mapping.put(key, createTypeConverter(tc.converter()));
+                                }
+                                //for keys of Maps
+                                else if (tc.rule().toString().equals(ConversionRule.KEY.toString())) {
+                                    Class converterClass = Thread.currentThread().getContextClassLoader().loadClass(tc.converter());
+                                    if (LOG.isDebugEnabled()) {
+                                        LOG.debug("Converter class: " + converterClass);
+                                    }
+                                    //check if the converter is a type converter if it is one
+                                    //then just put it in the map as is. Otherwise
+                                    //put a value in for the type converter of the class
+                                    if (converterClass.isAssignableFrom(TypeConverter.class)) {
+                                        mapping.put(key, createTypeConverter(tc.converter()));
+                                    } else {
+                                        mapping.put(key, converterClass);
+                                        if (LOG.isDebugEnabled()) {
+                                            LOG.debug("Object placed in mapping for key "
+                                                    + key
+                                                    + " is "
+                                                    + mapping.get(key));
+                                        }
+
+                                    }
+
+                                }
+                                //elements(values) of maps / lists
+                                else {
+                                    mapping.put(key, Thread.currentThread().getContextClassLoader().loadClass(tc.converter()));
+                                }
+                            }
+                        } catch (Exception e) {
+                        }
+                    }
+                }
+            }
+        }
+
+        Method[] methods = clazz.getMethods();
+
+        for (Method method : methods) {
+
+            annotations = method.getAnnotations();
+
+            for (Annotation annotation : annotations) {
+                if (annotation instanceof TypeConversion) {
+                    TypeConversion tc = (TypeConversion) annotation;
+
+                    String key = tc.key();
+                    if (mapping.containsKey(key)) {
+                        break;
+                    }
+                    // Default to the property name
+                    if ( key != null && key.length() == 0) {
+                        key = AnnotationUtils.resolvePropertyName(method);
+                        LOG.debug("key from method name... " + key + " - " + method.getName());
+                    }
+
+
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug(key + ":" + key);
+                    }
+
+                    if (key != null) {
+                        try {
+                        	if (tc.type() == ConversionType.APPLICATION) {
+                                defaultMappings.put(key, createTypeConverter(tc.converter()));
+                            } else {
+                                if (tc.rule().toString().equals(ConversionRule.KEY_PROPERTY)) {
+                                    mapping.put(key, tc.value());
+                                }
+                                //for properties of classes
+                                else if (!(tc.rule().toString().equals(ConversionRule.ELEMENT.toString())) ||
+                                        tc.rule().toString().equals(ConversionRule.KEY.toString()) ||
+                                        tc.rule().toString().equals(ConversionRule.COLLECTION.toString())
+                                        ) {
+                                    mapping.put(key, createTypeConverter(tc.converter()));
+                                }
+                                //for keys of Maps
+                                else if (tc.rule().toString().equals(ConversionRule.KEY.toString())) {
+                                    Class converterClass = Thread.currentThread().getContextClassLoader().loadClass(tc.converter());
+                                    if (LOG.isDebugEnabled()) {
+                                        LOG.debug("Converter class: " + converterClass);
+                                    }
+                                    //check if the converter is a type converter if it is one
+                                    //then just put it in the map as is. Otherwise
+                                    //put a value in for the type converter of the class
+                                    if (converterClass.isAssignableFrom(TypeConverter.class)) {
+                                        mapping.put(key, createTypeConverter(tc.converter()));
+                                    } else {
+                                        mapping.put(key, converterClass);
+                                        if (LOG.isDebugEnabled()) {
+                                            LOG.debug("Object placed in mapping for key "
+                                                    + key
+                                                    + " is "
+                                                    + mapping.get(key));
+                                        }
+
+                                    }
+
+                                }
+                                //elements(values) of maps / lists
+                                else {
+                                    mapping.put(key, Thread.currentThread().getContextClassLoader().loadClass(tc.converter()));
+                                }
+                            }
+                        } catch (Exception e) {
+                        }
+                    }
+                }
+            }
+        }        
     }
 
     /**

src/java/xwork-default.xml

     <bean type="com.opensymphony.xwork2.ActionProxyFactory"  
           class="com.opensymphony.xwork2.DefaultActionProxyFactory"/>
     <bean type="com.opensymphony.xwork2.util.ObjectTypeDeterminer"  
-          class="com.opensymphony.xwork2.util.GenericsObjectTypeDeterminer"/>
+          class="com.opensymphony.xwork2.util.DefaultObjectTypeDeterminer"/>
     <bean name="xwork1" 
           type="com.opensymphony.xwork2.util.ObjectTypeDeterminer"  
           class="com.opensymphony.xwork2.util.DefaultObjectTypeDeterminer"/>
           type="com.opensymphony.xwork2.util.XWorkConverter"  
           class="com.opensymphony.xwork2.util.XWorkConverter" />    
     <bean type="com.opensymphony.xwork2.util.XWorkConverter" 
-          class="com.opensymphony.xwork2.util.AnnotationXWorkConverter" />
+          class="com.opensymphony.xwork2.util.XWorkConverter" />
 
     <!--  static injections -->
     <bean class="com.opensymphony.xwork2.util.OgnlValueStack" static="true" />

src/test/com/opensymphony/xwork2/util/ObjectTypeDeterminerFactoryTest.java

 
 
     public void testDefaultInstanceTypeIsGenericsObjectTypeDeterminer() throws Exception {
-        assertEquals(ObjectTypeDeterminerFactory.getInstance().getClass(),GenericsObjectTypeDeterminer.class);
+        assertEquals(ObjectTypeDeterminerFactory.getInstance().getClass(),DefaultObjectTypeDeterminer.class);
     }
 
     public void testSetInstance() throws Exception {
             ObjectTypeDeterminerFactory.setInstance(null);
             assertEquals(ObjectTypeDeterminerFactory.getInstance(), objectTypeDeterminer);
             ObjectTypeDeterminerFactory.setInstance(new DefaultObjectTypeDeterminer());
-            assertFalse(ObjectTypeDeterminerFactory.getInstance().getClass().equals(objectTypeDeterminer.getClass()));
+            assertTrue(ObjectTypeDeterminerFactory.getInstance().getClass().equals(objectTypeDeterminer.getClass()));
         } finally {
             ObjectTypeDeterminerFactory.setInstance(objectTypeDeterminer);
         }