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.
The newest version!
/*
* 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