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