/* Copyright 2005-2006 Tim Fennell
*
* 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.validation;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.util.Locale;
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
* all the primitive and wrapper types as well as the rich types for which type converters exist.
*
* @author Tim Fennell
*/
public class DefaultTypeConverterFactory implements TypeConverterFactory {
private static final Log log = Log.getInstance(DefaultTypeConverterFactory.class);
/** Caches {@link TypeConverter} to {@link Class} mappings. */
private TypeHandlerCache<Class<? extends TypeConverter<?>>> cache;
/** Stores a reference to the Configuration passed in at initialization time. */
private Configuration configuration;
/**
* Places all the known convertible types and type converters into an instance level Map.
*/
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);
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
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. */
protected Configuration getConfiguration() {
return this.configuration;
}
/**
* Gets the (rather confusing) Map of TypeConverter objects. The Map uses the target class
* as the key in the Map, and the Class object representing the TypeConverter as the value.
*
* @return the Map of TypeConverter classes
*/
protected Map<Class<?>,Class<? extends TypeConverter<?>>> getTypeConverters() {
return this.cache.getHandlers();
}
/**
* Adds a TypeConverter to the set of registered TypeConverters, overriding an existing
* converter if one was registered for the type.
*
* @param targetType the type for which the converter will handle conversions
* @param converterClass the implementation class that will handle the conversions
*/
public void add(Class<?> targetType, Class<? extends TypeConverter<?>> converterClass) {
cache.add(targetType, converterClass);
}
/**
* Gets the applicable type converter for the class passed in. This is based on the default
* set of type converters which are stored in a Map on this class. Enums are a special case,
* whereby if there is no converter in the map, the EnumeratedTypeConverter will be returned.
*
* @param forType the type/Class that is the target type of the conversion. It is assumed that
* the input type is String, so to convert a String to a Date object you would supply
* java.util.Date.class.
* @return an instance of a TypeConverter which will convert Strings to the desired type
* @throws Exception if the TypeConverter cannot be instantiated
*/
@SuppressWarnings("unchecked")
public TypeConverter getTypeConverter(Class forType, Locale locale) throws Exception {
Class<? extends TypeConverter<?>> converterClass = cache.getHandler(forType);
if (converterClass != null) {
try {
return getInstance(converterClass, locale);
}
catch (Exception e) {
log.error(e, "Unable to instantiate type converter ", converterClass);
return null;
}
}
else {
log.trace("Couldn't find a type converter for ", forType);
return null;
}
}
/**
* Gets an instance of the TypeConverter class specified.
*
* @param clazz the TypeConverter type that is desired
* @return an instance of the TypeConverter specified
* @throws Exception if there is a problem instantiating the TypeConverter
*/
@SuppressWarnings("unchecked")
public TypeConverter getInstance(Class<? extends TypeConverter> clazz, Locale locale) throws Exception {
TypeConverter converter = getConfiguration().getObjectFactory().newInstance(clazz);
converter.setLocale(locale);
return converter;
}
}