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

com.foreach.common.spring.convert.EnumConverterFactory Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
/*
 * Copyright 2014 the original author or authors
 *
 * 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 com.foreach.common.spring.convert;

import com.foreach.common.spring.enums.CodeLookup;
import com.foreach.common.spring.enums.EnumUtils;
import com.foreach.common.spring.enums.IdLookup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * EnumConverterFactory is an implementation of the Spring ConverterFactory interface
 * that generates Converters from String to Enum classes implementing IdLookup or CodeLookup.
 * 

* The conversion is a two step process: first a String is converted to the parameter type of * IdLookup or CodeLookup, then that value is used to determine the enum with that id or code. *

* Each EnumConverterFactory instance must be provided with a conversion service * that can convert String to all the parameter types that require conversion. *

* When converting to an Enum class implementing both IdLookup and CodeLookup, * the IdLookup is attempted first. *

* In most cases, you will group your converters in a single conversion service. * Check the spring documentation * on how to configure a conversion service for your application. *

If an object can't be converted with the conversion service, the system falls back to property editors. * For this reason, you should keep all your converters as specific as possible. *

* To use the EnumConverterFactory in Spring 3.1+ with @Configuration annotations: *

 *         public class WebConfig extends WebMvcConfigurerAdapter
 *         {
*             {@literal @}Override
*             public void addFormatters( FormatterRegistry registry ) {
*               registry.addConverterFactory( new EnumConverterFactory() );
*             }
 *         }
 *     
*

*/ public class EnumConverterFactory implements ConverterFactory, RecursiveConverter { private Logger logger = LoggerFactory.getLogger( getClass() ); // Try to not get in an infinite loop here... private ConversionService conversionService; /** * Set the conversionService. This service must be able to convert String to all * the parameter types used. */ public final void setConversionService( ConversionService conversionService ) { this.conversionService = conversionService; } /** * Get a converter instance for the specified enum class. * * @param targetType the Enum class being converted to. * @return a converter implementing the Spring Converter interface * that converts String to the specified enum class. */ public final Converter getConverter( Class targetType ) { logger.debug( "converter requested for type " + targetType.getName() ); if ( conversionService == null ) { logger.error( "conversionService not set for EnumConverterFactory instance" ); } if ( IdLookup.class.isAssignableFrom( targetType ) || CodeLookup.class.isAssignableFrom( targetType )) { return new EnumConverter( targetType ); } return null; } private final class EnumConverter implements Converter { private Class enumType; public EnumConverter( Class enumType ) { this.enumType = enumType; } public E convert( String source ) { if ( IdLookup.class.isAssignableFrom( enumType ) ) { logger.debug( "attempting to convert " + source + " to " + enumType + " using IdLookup" ); Class intermediateType = lookupMethodParameterClass( enumType, IdLookup.class ); if ( intermediateType == null ) { logger.error( "IdLookup parameter type not specified, assuming Integer." ); intermediateType = Integer.class; } E attempt = tryConvertUsingMethod( source, intermediateType, "getById" ); if ( attempt != null ) { return attempt; } } if ( CodeLookup.class.isAssignableFrom( enumType ) ) { logger.debug( "attempting to convert " + source + " to " + enumType + " using CodeLookup" ); Class intermediateType = lookupMethodParameterClass( enumType, CodeLookup.class ); if ( intermediateType == null ) { logger.error( "CodeLookup parameter type not specified, assuming String." ); intermediateType = String.class; } E attempt = tryConvertUsingMethod( source, intermediateType, "getByCode" ); if ( attempt != null ) { return attempt; } } return null; } /** * Find the type parameter of the argument class for the specified lookupInterface, * so for Foo implements LookUp, return Bar.Class; */ private Class lookupMethodParameterClass( Class targetClass, Class lookupInterface ) { Type[] ts = targetClass.getGenericInterfaces(); for ( Type t : ts ) { if ( t instanceof ParameterizedType ) { ParameterizedType pt = (ParameterizedType) t; if ( pt.getRawType().equals( lookupInterface ) ) { return (Class) pt.getActualTypeArguments()[0]; } } } return null; } private E tryConvertUsingMethod( String source, Class intermediateType, String lookupMethodName ) { try { Object id = source; logger.debug( "performing intermediate conversion of " + source + " to " + intermediateType ); if ( !String.class.isAssignableFrom( intermediateType ) ) { id = conversionService.convert( source, TypeDescriptor.valueOf( String.class ), TypeDescriptor.valueOf( intermediateType ) ); } Method m = com.foreach.common.spring.enums.EnumUtils.class.getMethod( lookupMethodName, Class.class, Object.class ); return (E) m.invoke( EnumUtils.class, enumType, id ); } catch ( NoSuchMethodException nsme ) { logger.error( nsme.getMessage(), nsme ); } catch ( IllegalAccessException iae ) { logger.error( iae.getMessage(), iae ); } catch ( InvocationTargetException ite ) { logger.error( ite.getMessage(), ite ); } catch ( ConversionFailedException ce ) { // this is allowed if both interfaces are implemented } logger.error( "intermediate conversion failed" ); return null; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy