Revision: 1052
          http://stripes.svn.sourceforge.net/stripes/?rev=1052&view=rev
Author:   bengunter
Date:     2009-02-25 21:19:10 +0000 (Wed, 25 Feb 2009)

Log Message:
-----------
In preparation for some work on STS-614, I have extracted and generalized the 
nifty TypeConverter search code from DefaultTypeConverterFactory to a 
TypeHandlerCache class.

Modified Paths:
--------------
    
trunk/stripes/src/net/sourceforge/stripes/validation/DefaultTypeConverterFactory.java

Added Paths:
-----------
    trunk/stripes/src/net/sourceforge/stripes/util/TypeHandlerCache.java

Added: trunk/stripes/src/net/sourceforge/stripes/util/TypeHandlerCache.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/util/TypeHandlerCache.java        
                        (rev 0)
+++ trunk/stripes/src/net/sourceforge/stripes/util/TypeHandlerCache.java        
2009-02-25 21:19:10 UTC (rev 1052)
@@ -0,0 +1,129 @@
+/* Copyright 2009 Ben Gunter
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package net.sourceforge.stripes.util;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import net.sourceforge.stripes.validation.TypeConverter;
+import net.sourceforge.stripes.validation.TypeConverterFactory;
+
+/**
+ * Provides an efficient way to map "handler" classes to other classes, while 
taking into
+ * consideration the target type's implemented interfaces and superclasses. 
For example,
+ * {...@link TypeConverterFactory} uses this class to map an implementation of 
{...@link TypeConverter} to
+ * classes.
+ * 
+ * @author Ben Gunter
+ */
+public class TypeHandlerCache<T> {
+    private static final Log log = Log.getInstance(TypeHandlerCache.class);
+
+    /** A direct map of target types to handlers. */
+    private Map<Class<?>, Class<? extends T>> handlers = new 
ConcurrentHashMap<Class<?>, Class<? extends T>>();
+
+    /**
+     * Cache of indirect type handler results, determined by examining a 
target type's implemented
+     * interfaces and superclasses.
+     */
+    private Map<Class<?>, Class<? extends T>> indirectCache = new 
ConcurrentHashMap<Class<?>, Class<? extends T>>();
+
+    /**
+     * Gets the (rather confusing) Map of handler classes. The Map uses the 
target class as the key
+     * in the Map, and the Class object representing the handler as the value.
+     * 
+     * @return the Map of classes to their handlers
+     */
+    public Map<Class<?>, Class<? extends T>> getHandlers() {
+        return handlers;
+    }
+
+    /**
+     * Adds a handler to the set of registered handlers, overriding an 
existing handler if one was
+     * already registered for the target type. Calls {...@link 
#clearIndirectCache()} because a new
+     * direct mapping can affect the indirect search results.
+     * 
+     * @param targetType the type for which the handler will handle conversions
+     * @param handlerClass the implementation class that will handle the 
conversions
+     */
+    public void add(Class<?> targetType, Class<? extends T> handlerClass) {
+        handlers.put(targetType, handlerClass);
+        clearIndirectCache();
+    }
+
+    /**
+     * Gets the applicable type handler for the class passed in.
+     * 
+     * @param forType The target type
+     * @return The handler class associated with the target type, or null if 
none is found.
+     */
+    public Class<? extends T> getHandlerClass(Class<?> forType) {
+        Class<? extends T> handlerClass = findHandlerClass(forType);
+        if (handlerClass != null) {
+            return handlerClass;
+        }
+        else {
+            log.trace("Couldn't find a type handler for ", forType);
+            return null;
+        }
+    }
+
+    /**
+     * Search for a type handler class that best matches the requested class.
+     * 
+     * @param targetType The target type
+     * @return The first applicable type handler found or null if no match 
could be found
+     */
+    protected Class<? extends T> findHandlerClass(Class<?> targetType) {
+        if (handlers.containsKey(targetType))
+            return handlers.get(targetType);
+        else if (indirectCache.containsKey(targetType))
+            return indirectCache.get(targetType);
+        else if (targetType.isEnum()) {
+            Class<? extends T> handlerClass = findHandlerClass(Enum.class);
+            if (handlerClass != null)
+                return cacheHandlerClass(targetType, handlerClass);
+        }
+        else {
+            for (Annotation annotation : targetType.getAnnotations()) {
+                Class<? extends Annotation> annotationType = 
annotation.annotationType();
+                if (handlers.containsKey(annotationType))
+                    return cacheHandlerClass(targetType, 
handlers.get(annotationType));
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Add handler class {...@code handlerClass} for converting objects of 
type {...@code clazz}.
+     * 
+     * @param clazz The type of object being converted
+     * @param handlerClass The class of the handler
+     * @return The {...@code targetType} parameter
+     */
+    protected Class<? extends T> cacheHandlerClass(Class<?> clazz, Class<? 
extends T> handlerClass) {
+        log.debug("Caching type handler for ", clazz, " => ", handlerClass);
+        indirectCache.put(clazz, handlerClass);
+        return handlerClass;
+    }
+
+    /** Clear the indirect cache. This is called by {...@link #add(Class, 
Class)}. */
+    public void clearIndirectCache() {
+        log.debug("Clearing indirect type handler cache");
+        indirectCache.clear();
+    }
+}

Modified: 
trunk/stripes/src/net/sourceforge/stripes/validation/DefaultTypeConverterFactory.java
===================================================================
--- 
trunk/stripes/src/net/sourceforge/stripes/validation/DefaultTypeConverterFactory.java
       2009-02-25 20:37:57 UTC (rev 1051)
+++ 
trunk/stripes/src/net/sourceforge/stripes/validation/DefaultTypeConverterFactory.java
       2009-02-25 21:19:10 UTC (rev 1052)
@@ -14,17 +14,16 @@
  */
 package net.sourceforge.stripes.validation;
 
-import net.sourceforge.stripes.config.Configuration;
-import net.sourceforge.stripes.util.Log;
-
-import java.util.Map;
+import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.util.Date;
 import java.util.Locale;
-import java.util.concurrent.ConcurrentHashMap;
-import java.lang.annotation.Annotation;
-import java.math.BigInteger;
-import java.math.BigDecimal;
+import java.util.Map;
 
+import net.sourceforge.stripes.config.Configuration;
+import net.sourceforge.stripes.util.Log;
+import net.sourceforge.stripes.util.TypeHandlerCache;
+
 /**
  * Default TypeConverterFactory implementation that simply creates an instance 
level map of all the
  * TypeConverters included in the Stripes distribution, and their applicable 
classes.  Can handle
@@ -35,14 +34,9 @@
 public class DefaultTypeConverterFactory implements TypeConverterFactory {
     private static final Log log = 
Log.getInstance(DefaultTypeConverterFactory.class);
 
-    /** A rather generic-heavy Map that maps target type to TypeConverter. */
-    private Map<Class<?>, Class<? extends TypeConverter<?>>> converters =
-        new ConcurrentHashMap<Class<?>, Class<? extends TypeConverter<?>>>();
+    /** Caches {...@link TypeConverter} to {...@link Class} mappings. */
+    private TypeHandlerCache<TypeConverter<?>> cache = new 
TypeHandlerCache<TypeConverter<?>>();
 
-    /** Cache of indirect type converter results. */
-    private Map<Class<?>, Class<? extends TypeConverter<?>>> classCache =
-            new ConcurrentHashMap<Class<?>, Class<? extends 
TypeConverter<?>>>();
-
     /** Stores a reference to the Configuration passed in at initialization 
time. */
     private Configuration configuration;
 
@@ -52,30 +46,30 @@
     public void init(final Configuration configuration) {
         this.configuration = configuration;
 
-        converters.put(Boolean.class, BooleanTypeConverter.class);
-        converters.put(Boolean.TYPE,  BooleanTypeConverter.class);
-        converters.put(Byte.class,    ByteTypeConverter.class);
-        converters.put(Byte.TYPE,     ByteTypeConverter.class);
-        converters.put(Short.class,   ShortTypeConverter.class);
-        converters.put(Short.TYPE,    ShortTypeConverter.class);
-        converters.put(Integer.class, IntegerTypeConverter.class);
-        converters.put(Integer.TYPE,  IntegerTypeConverter.class);
-        converters.put(Long.class,    LongTypeConverter.class);
-        converters.put(Long.TYPE,     LongTypeConverter.class);
-        converters.put(Float.class,   FloatTypeConverter.class);
-        converters.put(Float.TYPE,    FloatTypeConverter.class);
-        converters.put(Double.class,  DoubleTypeConverter.class);
-        converters.put(Double.TYPE,   DoubleTypeConverter.class);
-        converters.put(Date.class,    DateTypeConverter.class);
-        converters.put(BigInteger.class, BigIntegerTypeConverter.class);
-        converters.put(BigDecimal.class, BigDecimalTypeConverter.class);
-        converters.put(Enum.class, EnumeratedTypeConverter.class);
+        cache.add(Boolean.class,    BooleanTypeConverter.class);
+        cache.add(Boolean.TYPE,     BooleanTypeConverter.class);
+        cache.add(Byte.class,       ByteTypeConverter.class);
+        cache.add(Byte.TYPE,        ByteTypeConverter.class);
+        cache.add(Short.class,      ShortTypeConverter.class);
+        cache.add(Short.TYPE,       ShortTypeConverter.class);
+        cache.add(Integer.class,    IntegerTypeConverter.class);
+        cache.add(Integer.TYPE,     IntegerTypeConverter.class);
+        cache.add(Long.class,       LongTypeConverter.class);
+        cache.add(Long.TYPE,        LongTypeConverter.class);
+        cache.add(Float.class,      FloatTypeConverter.class);
+        cache.add(Float.TYPE,       FloatTypeConverter.class);
+        cache.add(Double.class,     DoubleTypeConverter.class);
+        cache.add(Double.TYPE,      DoubleTypeConverter.class);
+        cache.add(Date.class,       DateTypeConverter.class);
+        cache.add(BigInteger.class, BigIntegerTypeConverter.class);
+        cache.add(BigDecimal.class, BigDecimalTypeConverter.class);
+        cache.add(Enum.class,       EnumeratedTypeConverter.class);
 
         // Now some less useful, but still helpful converters
-        converters.put(String.class, StringTypeConverter.class);
-        converters.put(Object.class, ObjectTypeConverter.class);
-        converters.put(Character.class, CharacterTypeConverter.class);
-        converters.put(Character.TYPE, CharacterTypeConverter.class); 
+        cache.add(String.class,     StringTypeConverter.class);
+        cache.add(Object.class,     ObjectTypeConverter.class);
+        cache.add(Character.class,  CharacterTypeConverter.class);
+        cache.add(Character.TYPE,   CharacterTypeConverter.class);
     }
 
     /** Provides subclasses with access to the configuration provided at 
initialization. */
@@ -90,7 +84,7 @@
      * @return the Map of TypeConverter classes
      */
     protected Map<Class<?>,Class<? extends TypeConverter<?>>> 
getTypeConverters() {
-        return this.converters;
+        return this.cache.getHandlers();
     }
 
     /**
@@ -101,8 +95,8 @@
      * @param converterClass the implementation class that will handle the 
conversions
      */
     public void add(Class<?> targetType, Class<? extends TypeConverter<?>> 
converterClass) {
-        this.converters.put(targetType, converterClass);
-        clearCache();
+        cache.add(targetType, converterClass);
+        cache.clearIndirectCache();
     }
     
     /**
@@ -117,8 +111,8 @@
      * @throws Exception if the TypeConverter cannot be instantiated
      */
     @SuppressWarnings("unchecked")
-       public TypeConverter getTypeConverter(Class forType, Locale locale) 
throws Exception {
-        Class<? extends TypeConverter<?>> converterClass = 
findTypeConverterClass(forType);
+    public TypeConverter getTypeConverter(Class forType, Locale locale) throws 
Exception {
+        Class<? extends TypeConverter<?>> converterClass = 
cache.getHandlerClass(forType);
         if (converterClass != null) {
             try {
                 return getInstance(converterClass, locale);
@@ -133,55 +127,8 @@
             return null;
         }
     }
-    
-    /**
-     * Search for a type converter class that best matches the requested class.
-     * 
-     * @param targetClass the class of the object that needs to be converted
-     * @return the first applicable type converter found or null if no match 
could be found
-     */
-    protected Class<? extends TypeConverter<?>> 
findTypeConverterClass(Class<?> targetClass) {
-        if (converters.containsKey(targetClass))
-            return converters.get(targetClass);
-        else if (classCache.containsKey(targetClass))
-            return classCache.get(targetClass);
-        else if (targetClass.isEnum()) {
-            Class<? extends TypeConverter<?>> converterClass = 
findTypeConverterClass(Enum.class);
-            if (converterClass != null)
-                return cacheTypeConverterClass(targetClass, converterClass);
-        }
-        else {
-            for (Annotation annotation : targetClass.getAnnotations()) {
-                Class<? extends Annotation> annotationType = 
annotation.annotationType();
-                if (converters.containsKey(annotationType))
-                    return cacheTypeConverterClass(targetClass, 
converters.get(annotationType));
-            }
-        }
-            
-        return null;
-    }
 
     /**
-     * Add converter class {...@code converterClass} for converting objects of 
type {...@code clazz}.
-     * 
-     * @param clazz the type of object being converted
-     * @param converterClass the class of the converter
-     * @return the {...@code targetType} parameter
-     */
-    protected Class<? extends TypeConverter<?>> 
cacheTypeConverterClass(Class<?> clazz,
-            Class<? extends TypeConverter<?>> converterClass) {
-        log.debug("Caching type converter for ", clazz, " => ", 
converterClass);
-        classCache.put(clazz, converterClass);
-        return converterClass;
-    }
-
-    /** Clear the instance cache. This is called by {...@link #add(Class, 
Class)}. */
-    protected void clearCache() {
-        log.debug("Clearing type converter cache");
-        classCache.clear();
-    }
-
-    /**
      * Gets an instance of the TypeConverter class specified.
      *
      * @param clazz the TypeConverter type that is desired


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

------------------------------------------------------------------------------
Open Source Business Conference (OSBC), March 24-25, 2009, San Francisco, CA
-OSBC tackles the biggest issue in open source: Open Sourcing the Enterprise
-Strategies to boost innovation and cut costs with open source participation
-Receive a $600 discount off the registration fee with the source code: SFAD
http://p.sf.net/sfu/XcvMzF8H
_______________________________________________
Stripes-development mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-development

Reply via email to