Revision: 1054
http://stripes.svn.sourceforge.net/stripes/?rev=1054&view=rev
Author: bengunter
Date: 2009-02-26 05:26:51 +0000 (Thu, 26 Feb 2009)
Log Message:
-----------
More work toward STS-614. TypeHandlerCache is now even more general-purpose and
includes the class hierarchy scanning capabilities from
DefaultFormatterFactory. Hierarchy scanning can now be turned on or off, as
required by DefaultTypeConverterFactory. DefaultFormatterFactory has been
refactored to use TypeHandlerCache with hierarchy scanning enabled.
Modified Paths:
--------------
trunk/stripes/src/net/sourceforge/stripes/format/DefaultFormatterFactory.java
trunk/stripes/src/net/sourceforge/stripes/util/TypeHandlerCache.java
trunk/stripes/src/net/sourceforge/stripes/validation/DefaultTypeConverterFactory.java
Added Paths:
-----------
trunk/stripes/src/net/sourceforge/stripes/util/ConcurrentHashSet.java
Modified:
trunk/stripes/src/net/sourceforge/stripes/format/DefaultFormatterFactory.java
===================================================================
---
trunk/stripes/src/net/sourceforge/stripes/format/DefaultFormatterFactory.java
2009-02-25 22:01:10 UTC (rev 1053)
+++
trunk/stripes/src/net/sourceforge/stripes/format/DefaultFormatterFactory.java
2009-02-26 05:26:51 UTC (rev 1054)
@@ -14,14 +14,13 @@
*/
package net.sourceforge.stripes.format;
-import java.lang.annotation.Annotation;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
import net.sourceforge.stripes.config.Configuration;
import net.sourceforge.stripes.util.Log;
+import net.sourceforge.stripes.util.TypeHandlerCache;
/**
* Implementation of {...@link FormatterFactory} that contains a set of
built-in formatters. Additional
@@ -35,18 +34,17 @@
public class DefaultFormatterFactory implements FormatterFactory {
private static final Log log =
Log.getInstance(DefaultFormatterFactory.class);
- /** A rather generic-heavy Map that maps target type to Formatter. */
- private Map<Class<?>, Class<? extends Formatter<?>>> formatters = new
ConcurrentHashMap<Class<?>, Class<? extends Formatter<?>>>();
+ /** Cache target type to Formatter class mappings. */
+ private TypeHandlerCache<Class<? extends Formatter<?>>> cache;
- /** Cache of indirect formatter results. */
- private Map<Class<?>, Class<? extends Formatter<?>>> classCache = new
ConcurrentHashMap<Class<?>, Class<? extends Formatter<?>>>();
-
/** Stores a reference to the Configuration passed in at initialization
time. */
private Configuration configuration;
/** Stores a reference to the configuration and configures the default
formatters. */
public void init(Configuration configuration) throws Exception {
this.configuration = configuration;
+ this.cache = new TypeHandlerCache<Class<? extends Formatter<?>>>();
+ this.cache.setDefaultHandler(ObjectFormatter.class);
add(Date.class, DateFormatter.class);
add(Number.class, NumberFormatter.class);
@@ -65,7 +63,7 @@
* @return the Map of Formatter classes
*/
protected Map<Class<?>,Class<? extends Formatter<?>>> getFormatters() {
- return this.formatters;
+ return cache.getHandlers();
}
/**
@@ -76,16 +74,9 @@
* @param formatterClass the implementation class that will handle the
formatting
*/
public void add(Class<?> targetType, Class<? extends Formatter<?>>
formatterClass) {
- this.formatters.put(targetType, formatterClass);
- clearCache();
+ cache.add(targetType, formatterClass);
}
- /** Clear the class and instance caches. This is called by {...@link
#add(Class, Class)}. */
- protected void clearCache() {
- log.debug("Clearing formatter cache");
- classCache.clear();
- }
-
/**
* Check to see if the there is a Formatter for the specified clazz. If a
Formatter is found an
* instance is created, configured and returned. Otherwise returns null.
@@ -97,7 +88,7 @@
* @return Formatter an instance of a Formatter, or null
*/
public Formatter<?> getFormatter(Class<?> clazz, Locale locale, String
formatType, String formatPattern) {
- Class<? extends Formatter<?>> formatterClass =
findFormatterClass(clazz);
+ Class<? extends Formatter<?>> formatterClass = cache.getHandler(clazz);
if (formatterClass != null) {
try {
return getInstance(formatterClass, formatType, formatPattern,
locale);
@@ -114,113 +105,6 @@
}
/**
- * Search for a formatter class that best matches the requested class,
first checking the
- * specified class, then all the interfaces it implements, then all its
superclasses and the
- * interfaces they implement, and finally all the superclasses of the
interfaces implemented by
- * {...@code targetClass}.
- *
- * @param targetClass the class of the object that needs to be formatted
- * @return the best applicable formatter
- */
- protected Class<? extends Formatter<?>> findFormatterClass(Class<?>
targetClass) {
- Class<? extends Formatter<?>> formatterClass =
findInSuperclasses(targetClass);
- if (formatterClass != null)
- return formatterClass;
-
- formatterClass = findInInterfaces(targetClass,
targetClass.getInterfaces());
- if (formatterClass != null)
- return formatterClass;
-
- return cacheFormatterClass(targetClass, ObjectFormatter.class);
- }
-
- /**
- * Called first by {...@link #findFormatterClass(Class)}. Search for a
formatter class that best
- * matches the requested class, first checking the specified class, second
all the interfaces it
- * implements, third annotations. If no match is found, repeat the process
for each superclass.
- *
- * @param targetClass the class of the object that needs to be formatted
- * @return the first applicable formatter found or null if no match could
be found
- */
- protected Class<? extends Formatter<?>> findInSuperclasses(Class<?>
targetClass) {
- // Check for a known formatter for the class
- Class<? extends Formatter<?>> formatterClass;
- if ((formatterClass = formatters.get(targetClass)) != null)
- return formatterClass;
- else if ((formatterClass = classCache.get(targetClass)) != null)
- return formatterClass;
-
- // Check directly implemented interfaces
- for (Class<?> iface : targetClass.getInterfaces()) {
- if ((formatterClass = formatters.get(iface)) != null)
- return cacheFormatterClass(targetClass, formatterClass);
- else if ((formatterClass = classCache.get(iface)) != null)
- return cacheFormatterClass(targetClass, formatterClass);
- }
-
- // Check for annotations
- for (Annotation annotation : targetClass.getAnnotations()) {
- Class<? extends Annotation> annotationType =
annotation.annotationType();
- if (formatters.containsKey(annotationType))
- return cacheFormatterClass(targetClass,
formatters.get(annotationType));
- }
-
- // Check superclasses
- Class<?> parent = targetClass.getSuperclass();
- if (parent != null) {
- if ((formatterClass = findInSuperclasses(parent)) != null) {
- return cacheFormatterClass(targetClass, formatterClass);
- }
- }
-
- // Nothing found, so return null
- return null;
- }
-
- /**
- * Called second by {...@link #findFormatterClass(Class)}, after
- * {...@link #findInSuperclasses(Class)}. Search for a formatter class
that best matches the
- * requested class by checking the superclasses of every interface
implemented by
- * {...@code targetClass}.
- *
- * @param targetClass the class of the object that needs to be formatted
- * @param ifaces an array of interfaces to search
- * @return the first applicable formatter found or null if no match could
be found
- */
- protected Class<? extends Formatter<?>> findInInterfaces(Class<?>
targetClass,
- Class<?>... ifaces) {
- Class<? extends Formatter<?>> formatterClass = null;
- for (Class<?> iface : ifaces) {
- if ((formatterClass = formatters.get(iface)) != null) {
- return cacheFormatterClass(targetClass, formatterClass);
- }
- else if ((formatterClass = classCache.get(iface)) != null) {
- return cacheFormatterClass(targetClass, formatterClass);
- }
- else if ((formatterClass = findInInterfaces(targetClass,
iface.getInterfaces())) != null) {
- return cacheFormatterClass(targetClass, formatterClass);
- }
- }
-
- // Nothing found, so return null
- return null;
- }
-
- /**
- * Add formatter class {...@code formatterClass} for formatting objects of
type {...@code clazz}.
- *
- * @param clazz the type of object being formatted
- * @param formatterClass the class of the formatter
- * @return the {...@code targetType} parameter
- */
- protected Class<? extends Formatter<?>> cacheFormatterClass(Class<?> clazz,
- Class<? extends Formatter<?>> formatterClass) {
- log.debug("Caching Formatter for ", clazz, " => ", formatterClass);
- classCache.put(clazz, formatterClass);
- return formatterClass;
- }
-
- /**
* Gets an instance of the Formatter class specified.
*
* @param clazz the Formatter type that is desired
Added: trunk/stripes/src/net/sourceforge/stripes/util/ConcurrentHashSet.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/util/ConcurrentHashSet.java
(rev 0)
+++ trunk/stripes/src/net/sourceforge/stripes/util/ConcurrentHashSet.java
2009-02-26 05:26:51 UTC (rev 1054)
@@ -0,0 +1,142 @@
+/* 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.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * A set based on {...@link ConcurrentHashMap}. The Javadoc for the
constructors in this class were
+ * copied from the Java 1.5 Javadoc for {...@link ConcurrentHashMap} and
changed to reflect that this
+ * is a Set and not a Map. See the Javadoc for {...@link ConcurrentHashMap}
for information on
+ * performance characteristics, etc.
+ *
+ * @author Ben Gunter
+ */
+public class ConcurrentHashSet<T> implements Set<T> {
+ /** The value object that will be put in the map since it does not accept
null values. */
+ private static final Object VALUE = new Object();
+
+ /** The map that backs this set. */
+ private ConcurrentMap<T, Object> map;
+
+ /**
+ * Creates a new, empty map with a default initial capacity, load factor,
and concurrencyLevel.
+ */
+ public ConcurrentHashSet() {
+ map = new ConcurrentHashMap<T, Object>();
+ }
+
+ /**
+ * Creates a new, empty map with the specified initial capacity, and with
default load factor
+ * and concurrencyLevel.
+ *
+ * @param initialCapacity the initial capacity. The implementation
performs internal sizing to
+ * accommodate this many elements.
+ * @throws IllegalArgumentException if the initial capacity of elements is
negative.
+ */
+ public ConcurrentHashSet(int initialCapacity) {
+ map = new ConcurrentHashMap<T, Object>(initialCapacity);
+ }
+
+ /**
+ * Creates a new, empty map with the specified initial capacity, load
factor, and concurrency
+ * level.
+ *
+ * @param initialCapacity the initial capacity. The implementation
performs internal sizing to
+ * accommodate this many elements.
+ * @param loadFactor the load factor threshold, used to control resizing.
Resizing may be
+ * performed when the average number of elements per bin
exceeds this threshold.
+ * @param concurrencyLevel - the estimated number of concurrently updating
threads. The
+ * implementation performs internal sizing to try to
accommodate this many threads.
+ * @throws IllegalArgumentException if the initial capacity is negative or
the load factor or
+ * concurrencyLevel are nonpositive.
+ */
+ public ConcurrentHashSet(int initialCapacity, float loadFactor, int
concurrencyLevel) {
+ map = new ConcurrentHashMap<T, Object>(initialCapacity, loadFactor,
concurrencyLevel);
+ }
+
+ /**
+ * Creates a new set with the same elements as the given set. The set is
created with a capacity
+ * of twice the number of elements in the given set or 11 (whichever is
greater), and a default
+ * load factor and concurrencyLevel.
+ *
+ * @param set The set
+ */
+ public ConcurrentHashSet(Set<? extends T> set) {
+ this(Math.max(set.size() * 2, 11));
+ addAll(set);
+ }
+
+ public boolean add(T e) {
+ return map.putIfAbsent(e, VALUE) == null;
+ }
+
+ public boolean addAll(Collection<? extends T> c) {
+ boolean b = false;
+ for (T t : c) {
+ b = b || map.putIfAbsent(t, VALUE) == null;
+ }
+ return b;
+ }
+
+ public void clear() {
+ map.clear();
+ }
+
+ public boolean contains(Object o) {
+ return map.keySet().contains(o);
+ }
+
+ public boolean containsAll(Collection<?> c) {
+ return map.keySet().containsAll(c);
+ }
+
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ public Iterator<T> iterator() {
+ return map.keySet().iterator();
+ }
+
+ public boolean remove(Object o) {
+ return map.remove(o) != null;
+ }
+
+ public boolean removeAll(Collection<?> c) {
+ return map.keySet().removeAll(c);
+ }
+
+ public boolean retainAll(Collection<?> c) {
+ return map.keySet().retainAll(c);
+ }
+
+ public int size() {
+ return map.size();
+ }
+
+ public Object[] toArray() {
+ return new ArrayList<T>(map.keySet()).toArray();
+ }
+
+ public <E> E[] toArray(E[] a) {
+ return new ArrayList<T>(map.keySet()).toArray(a);
+ }
+}
Modified: trunk/stripes/src/net/sourceforge/stripes/util/TypeHandlerCache.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/util/TypeHandlerCache.java
2009-02-25 22:01:10 UTC (rev 1053)
+++ trunk/stripes/src/net/sourceforge/stripes/util/TypeHandlerCache.java
2009-02-26 05:26:51 UTC (rev 1054)
@@ -16,16 +16,34 @@
import java.lang.annotation.Annotation;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import net.sourceforge.stripes.controller.ObjectPostProcessor;
+import net.sourceforge.stripes.format.Formatter;
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.
+ * <p>
+ * Provides an efficient way to map "handlers" to classes. There are two types
of mappings: direct
+ * and indirect. A direct mapping is one that is explicitly created by a call
to
+ * {...@link #add(Class, Object)}. An indirect mapping is one that is
discovered by examining a target
+ * type's implemented interfaces and superclasses. If {...@code
searchHierarchy} is set to false, then
+ * only direct mappings will be considered and the class hierarchy will not be
searched.
+ * </p>
+ * <p>
+ * For example, let's assume a direct mapping is created for type {...@code A}
to handler {...@code H}. A
+ * request for a handler for type {...@code A} returns {...@code H} due to the
direct mapping. If {...@code
+ * searchHierarchy} is true and a handler is requested later for type
{...@code B}, which implements
+ * {...@code A}, then an indirect mapping will be created that maps the
handler {...@code H} to type
+ * {...@code B}. (If {...@code A} were a superclass of {...@code B}, it would
behave likewise.) However, if
+ * {...@code searchHierarchy} is false then a request for a handler for type
{...@code B} would return
+ * {...@link #getDefaultHandler()}.
+ * </p>
+ * <p>
+ * This class is used within Stripes to map {...@link Formatter}s, {...@link
TypeConverter}s and
+ * {...@link ObjectPostProcessor}s to specific classes and interfaces.
+ * </p>
*
* @author Ben Gunter
*/
@@ -33,97 +51,219 @@
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>>();
+ private Map<Class<?>, T> handlers = new ConcurrentHashMap<Class<?>, 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>>();
+ private Map<Class<?>, T> indirectCache = new ConcurrentHashMap<Class<?>,
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.
+ * Cache of classes that have been searched, yet no handler (besides the
default one) could be
+ * found for them.
+ */
+ private Set<Class<?>> negativeCache = new ConcurrentHashSet<Class<?>>();
+
+ private T defaultHandler;
+ private boolean searchHierarchy = true;
+
+ /** Get the default handler to return if no handler is found for a
requested target type. */
+ public T getDefaultHandler() {
+ return defaultHandler;
+ }
+
+ /** Set the default handler to return if no handler is found for a
requested target type. */
+ public void setDefaultHandler(T defaultHandler) {
+ this.defaultHandler = defaultHandler;
+ }
+
+ /**
+ * Indicates if the class hierarchy will be searched to find the best
available handler in case
+ * a direct mapping is not available for a given target type.
+ */
+ public boolean isSearchHierarchy() {
+ return searchHierarchy;
+ }
+
+ /**
+ * Set the flag that enables or disables searching of the class hierarchy
to find the best
+ * available handler in case a direct mapping is not available for a given
target type.
*
- * @return the Map of classes to their handlers
+ * @param searchHierarchy True to enable hierarchy search; false to
disable it.
*/
- public Map<Class<?>, Class<? extends T>> getHandlers() {
+ public void setSearchHierarchy(boolean searchHierarchy) {
+ this.searchHierarchy = searchHierarchy;
+ }
+
+ /**
+ * Gets the (rather confusing) map of handlers. The map uses the target
type as the key in the
+ * map, and the handler as the value.
+ *
+ * @return the map of classes to their handlers
+ */
+ public Map<Class<?>, 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.
+ * already registered for the target type. Calls {...@link #clearCache()}
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
+ * @param targetType The type for which a handler is requested.
+ * @param handler The handler for the target type.
*/
- public void add(Class<?> targetType, Class<? extends T> handlerClass) {
- handlers.put(targetType, handlerClass);
- clearIndirectCache();
+ public void add(Class<?> targetType, T handler) {
+ handlers.put(targetType, handler);
+ clearCache();
}
/**
- * Gets the applicable type handler for the class passed in.
+ * Check to see if the there is a handler for the specified target type.
*
- * @param forType The target type
- * @return The handler class associated with the target type, or null if
none is found.
+ * @param targetType The type for which a handler is requested.
+ * @return An appropriate handler, if one is found. Otherwise, whatever is
returned from a call
+ * to {...@link #getDefaultHandler()}.
*/
- public Class<? extends T> getHandlerClass(Class<?> forType) {
- Class<? extends T> handlerClass = findHandlerClass(forType);
- if (handlerClass != null) {
- return handlerClass;
+ public T getHandler(Class<?> targetType) {
+ T handler = findHandler(targetType);
+
+ if (handler == null) {
+ handler = getDefaultHandler();
+ log.trace("Couldn't find a handler for ", targetType, ". Using
default handler ",
+ getDefaultHandler(), " instead.");
}
- else {
- log.trace("Couldn't find a type handler for ", forType);
- return null;
+
+ return handler;
+ }
+
+ /**
+ * Search for a handler class that best matches the requested class, first
checking the
+ * specified class, then all the interfaces it implements, then all its
superclasses and the
+ * interfaces they implement, and finally all the superclasses of the
interfaces implemented by
+ * {...@code targetClass}.
+ *
+ * @param targetType The type for which a handler is requested.
+ * @return the best applicable handler
+ */
+ protected T findHandler(Class<?> targetType) {
+ T handler = findInSuperclasses(targetType);
+
+ if (handler == null && isSearchHierarchy()) {
+ handler = findInInterfaces(targetType, targetType.getInterfaces());
}
+
+ return handler;
}
/**
- * Search for a type handler class that best matches the requested class.
+ * Called first by {...@link #findHandler(Class)}. Search for a handler
class that best matches the
+ * requested class, first checking the specified class, second all the
interfaces it implements,
+ * third annotations. If no match is found, repeat the process for each
superclass.
*
- * @param targetType The target type
- * @return The first applicable type handler found or null if no match
could be found
+ * @param targetType The type for which a handler is requested.
+ * @return the first applicable 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);
+ protected T findInSuperclasses(Class<?> targetType) {
+ // Check for a known handler for the class
+ T handler;
+ if ((handler = handlers.get(targetType)) != null) {
+ return handler;
+ }
+ else if ((handler = indirectCache.get(targetType)) != null) {
+ return handler;
+ }
+ else if (negativeCache.contains(targetType)) {
+ return null;
+ }
else if (targetType.isEnum()) {
- Class<? extends T> handlerClass = findHandlerClass(Enum.class);
- if (handlerClass != null)
- return cacheHandlerClass(targetType, handlerClass);
+ handler = findInSuperclasses(Enum.class);
+ if (handler != null)
+ return cacheHandler(targetType, handler);
}
- else {
- for (Annotation annotation : targetType.getAnnotations()) {
- Class<? extends Annotation> annotationType =
annotation.annotationType();
- if (handlers.containsKey(annotationType))
- return cacheHandlerClass(targetType,
handlers.get(annotationType));
+ else if (!isSearchHierarchy()) {
+ return cacheHandler(targetType, null);
+ }
+
+ // Check directly implemented interfaces
+ for (Class<?> iface : targetType.getInterfaces()) {
+ if ((handler = handlers.get(iface)) != null)
+ return cacheHandler(targetType, handler);
+ else if ((handler = indirectCache.get(iface)) != null)
+ return cacheHandler(targetType, handler);
+ }
+
+ // Check for annotations
+ for (Annotation annotation : targetType.getAnnotations()) {
+ Class<? extends Annotation> annotationType =
annotation.annotationType();
+ if (handlers.containsKey(annotationType))
+ return cacheHandler(targetType, handlers.get(annotationType));
+ }
+
+ // Check superclasses
+ Class<?> parent = targetType.getSuperclass();
+ if (parent != null) {
+ if ((handler = findInSuperclasses(parent)) != null) {
+ return cacheHandler(targetType, handler);
}
}
+ // Nothing found, so return null
return null;
}
/**
- * Add handler class {...@code handlerClass} for converting objects of
type {...@code clazz}.
+ * Called second by {...@link #findHandler(Class)}, after {...@link
#findInSuperclasses(Class)} .
+ * Search for a handler that best matches the requested class by checking
the superclasses of
+ * every interface implemented by {...@code targetClass}.
*
- * @param clazz The type of object being converted
- * @param handlerClass The class of the handler
+ * @param targetType The type for which a handler is requested.
+ * @param ifaces An array of interfaces to search
+ * @return The first applicable handler found or null if no match could be
found
+ */
+ protected T findInInterfaces(Class<?> targetType, Class<?>... ifaces) {
+ T handler = null;
+ for (Class<?> iface : ifaces) {
+ if ((handler = handlers.get(iface)) != null) {
+ return cacheHandler(targetType, handler);
+ }
+ else if ((handler = indirectCache.get(iface)) != null) {
+ return cacheHandler(targetType, handler);
+ }
+ else if ((handler = findInInterfaces(targetType,
iface.getInterfaces())) != null) {
+ return cacheHandler(targetType, handler);
+ }
+ }
+
+ // Nothing found, so return null
+ return null;
+ }
+
+ /**
+ * Cache an indirect handler mapping for the given target type.
+ *
+ * @param targetType The type for which a handler is requested.
+ * @param handler 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;
+ protected T cacheHandler(Class<?> targetType, T handler) {
+ if (handler == null) {
+ log.debug("Caching no handler for ", targetType);
+ negativeCache.add(targetType);
+ }
+ else {
+ log.debug("Caching handler for ", targetType, " => ", handler);
+ indirectCache.put(targetType, handler);
+ }
+
+ return handler;
}
/** Clear the indirect cache. This is called by {...@link #add(Class,
Class)}. */
- public void clearIndirectCache() {
- log.debug("Clearing indirect type handler cache");
+ public void clearCache() {
+ log.debug("Clearing indirect cache and negative cache");
indirectCache.clear();
+ negativeCache.clear();
}
}
Modified:
trunk/stripes/src/net/sourceforge/stripes/validation/DefaultTypeConverterFactory.java
===================================================================
---
trunk/stripes/src/net/sourceforge/stripes/validation/DefaultTypeConverterFactory.java
2009-02-25 22:01:10 UTC (rev 1053)
+++
trunk/stripes/src/net/sourceforge/stripes/validation/DefaultTypeConverterFactory.java
2009-02-26 05:26:51 UTC (rev 1054)
@@ -35,7 +35,7 @@
private static final Log log =
Log.getInstance(DefaultTypeConverterFactory.class);
/** Caches {...@link TypeConverter} to {...@link Class} mappings. */
- private TypeHandlerCache<TypeConverter<?>> cache = new
TypeHandlerCache<TypeConverter<?>>();
+ private TypeHandlerCache<Class<? extends TypeConverter<?>>> cache;
/** Stores a reference to the Configuration passed in at initialization
time. */
private Configuration configuration;
@@ -45,6 +45,8 @@
*/
public void init(final Configuration configuration) {
this.configuration = configuration;
+ this.cache = new TypeHandlerCache<Class<? extends TypeConverter<?>>>();
+ this.cache.setSearchHierarchy(false);
cache.add(Boolean.class, BooleanTypeConverter.class);
cache.add(Boolean.TYPE, BooleanTypeConverter.class);
@@ -96,7 +98,6 @@
*/
public void add(Class<?> targetType, Class<? extends TypeConverter<?>>
converterClass) {
cache.add(targetType, converterClass);
- cache.clearIndirectCache();
}
/**
@@ -112,7 +113,7 @@
*/
@SuppressWarnings("unchecked")
public TypeConverter getTypeConverter(Class forType, Locale locale) throws
Exception {
- Class<? extends TypeConverter<?>> converterClass =
cache.getHandlerClass(forType);
+ Class<? extends TypeConverter<?>> converterClass =
cache.getHandler(forType);
if (converterClass != null) {
try {
return getInstance(converterClass, locale);
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