org.databene.commons.converter.ConverterManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of databene-commons Show documentation
Show all versions of databene-commons Show documentation
'databene commons' is an open source Java library by Volker Bergmann.
It provides extensions to the Java core library by utility classes, abstract concepts
and concrete implementations.
/*
* Copyright (C) 2004-2015 Volker Bergmann ([email protected]).
* All rights reserved.
*
* 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 org.databene.commons.converter;
import org.databene.commons.ArrayFormat;
import org.databene.commons.ConfigurationError;
import org.databene.commons.Context;
import org.databene.commons.ConversionException;
import org.databene.commons.Converter;
import org.databene.commons.IOUtil;
import org.databene.commons.BeanUtil;
import org.databene.commons.LogCategories;
import org.databene.commons.OrderedMap;
import org.databene.commons.Patterns;
import org.databene.commons.ReaderLineIterator;
import org.databene.commons.Resettable;
import org.databene.commons.StringUtil;
import org.databene.commons.context.ContextAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* Manages converters. A default configuration is provided and can be overwritten by a local file 'converters.txt',
* that lists each converter's class name, one name per line, e.g.
*
* com.my.MyString2ThingConverter
* com.my.MyString2ComplexConverter
*
* Created: 04.08.2007 19:43:17
* @author Volker Bergmann
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public class ConverterManager implements ContextAware, Resettable {
private static final Logger CONFIG_LOGGER = LoggerFactory.getLogger(LogCategories.CONFIG);
private static final String DEFAULT_SETUP_FILENAME = "org/databene/commons/converter/converters.txt";
private static final String CUSTOM_SETUP_FILENAME = "converters.txt";
private static ConverterManager instance;
private Context context;
private OrderedMap> configuredConverterClasses;
private Map converterPrototypes;
private ConverterManager() {
init();
}
protected void init() {
this.configuredConverterClasses = new OrderedMap>();
this.converterPrototypes = new HashMap();
try {
if (IOUtil.isURIAvailable(CUSTOM_SETUP_FILENAME)) {
CONFIG_LOGGER.debug("Reading custom converter config: {}", CUSTOM_SETUP_FILENAME);
readConfigFile(CUSTOM_SETUP_FILENAME);
}
readConfigFile(DEFAULT_SETUP_FILENAME);
} catch (IOException e) {
throw new ConfigurationError("Error reading setup file: " + DEFAULT_SETUP_FILENAME);
}
}
public static ConverterManager getInstance() {
if (instance == null)
instance = new ConverterManager();
return instance;
}
@Override
public void setContext(Context context) {
this.context = context;
for (Converter converter : converterPrototypes.values())
injectContext(converter);
}
private void injectContext(Converter converter) {
if (converter instanceof ContextAware)
((ContextAware) converter).setContext(context);
}
public Converter createConverter(Class sourceType, Class targetType) {
// check preconditions
if (targetType == null)
throw new ConversionException("targetType must be specified");
// check if we already know how to do this conversion
ConversionTypes conversionTypes = new ConversionTypes(sourceType, targetType);
Converter result = converterPrototypes.get(conversionTypes);
if (result != null)
return cloneIfSupported(result);
// we need to investigate...
result = searchAppropriateConverter(sourceType, targetType);
// ...and cache the result for future requests
if (result != null && result.isParallelizable())
converterPrototypes.put(conversionTypes, result);
// inject context if appropriate
injectContext(result);
// done
return result;
}
private Converter searchAppropriateConverter(Class sourceType, Class targetType) {
// catch primitive types
Class> wrapperClass = BeanUtil.getWrapper(targetType.getName());
if (wrapperClass != null)
return createConverter(sourceType, wrapperClass);
Converter result;
if (targetType.isAssignableFrom(sourceType) && !targetType.isPrimitive())
return new NoOpConverter();
// to string conversion
if (String.class.equals(targetType)) {
result = createToStringConverter(sourceType);
if (result != null)
return result;
}
// from string conversion
if (String.class.equals(sourceType)) {
result = tryToCreateStringConverter(targetType);
if (result != null)
return result;
}
// from number conversion
if (Number.class.isAssignableFrom(sourceType) && Number.class.isAssignableFrom(targetType))
return new NumberToNumberConverter(sourceType, targetType);
// from boolean conversion
if (Boolean.class.isAssignableFrom(sourceType)) {
result = tryToCreateBooleanConverter(targetType);
if (result != null)
return result;
}
if (targetType.isArray())
return new ToArrayConverter(targetType.getComponentType());
if (Collection.class.isAssignableFrom(targetType))
return new ToCollectionConverter(targetType);
result = tryToCreateFactoryConverter(sourceType, targetType);
if (result != null)
return result;
else
throw new ConversionException("Cannot convert " + sourceType.getName() +
" to " + targetType.getName());
}
@SuppressWarnings("cast")
private static Converter tryToCreateStringConverter(Class targetType) {
if (targetType.getEnumConstants() != null)
return new String2EnumConverter(targetType);
else if (targetType == Boolean.class)
return new String2BooleanConverter();
else if (Number.class.isAssignableFrom(targetType)) {
if (targetType != Number.class)
return new String2NumberConverter((Class) targetType);
else
return new String2NumberConverter(Double.class);
} else if (targetType.isArray()) {
Class componentType = targetType.getComponentType();
if (componentType == byte.class)
return new String2ByteArrayConverter();
else
return new CommaSeparatedListConverter(componentType);
}
return null;
}
private static Converter, T> tryToCreateBooleanConverter(Class targetType) {
if (Number.class.isAssignableFrom(targetType))
return new Boolean2NumberConverter(targetType);
Class> wrapperClass = BeanUtil.getWrapper(targetType.getName());
if (wrapperClass != null && Number.class.isAssignableFrom(wrapperClass))
return new Boolean2NumberConverter(wrapperClass);
return null;
}
private Converter, String> createToStringConverter(Class> sourceType) throws ConversionException {
if (sourceType.isArray()) {
Class> componentType = sourceType.getComponentType();
if (componentType == byte.class)
return new ByteArrayToBase64Converter();
else if (componentType == char.class)
return new CharArray2StringConverter();
else
return new FormatFormatConverter(sourceType, new ArrayFormat(), true);
} else if (sourceType == Time.class) {
return new FormatFormatConverter