All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.databene.commons.converter.ConverterManager Maven / Gradle / Ivy

Go to download

'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.

There is a newer version: 1.0.11
Show newest version
/*
 * Copyright (C) 2004-2014 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 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 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