001    package com.hammurapi.convert;
002    
003    import java.lang.reflect.Constructor;
004    import java.lang.reflect.Method;
005    import java.util.ArrayList;
006    import java.util.Collection;
007    import java.util.logging.Level;
008    import java.util.logging.Logger;
009    
010    import com.hammurapi.util.Context;
011    
012    
013    /**
014     * Converts one type to another using accessor/constructor combination.
015     * @author Pavel
016     *
017     */
018    public class ReflectionConverter<S, T> implements AtomicConverter<S, T> {
019            private static final Logger logger = Logger.getLogger(ReflectionConverter.class.getName());
020            
021            Method accessor;
022            Constructor<? extends T> constructor;
023            private Class<? extends T> targetType;
024            private Class<S> sourceType;
025            private boolean singleArg;
026            
027            /**
028             * @param accessor Source object method to invoke to get target type or
029             * constructor parameter for the target type. If it is null, then source itself
030             * is passed to constructor. One of parameters may be null, but not both.
031             * @param constructor Target class constructor which takes one parameter, either
032             * source type or return type of source type accessor.
033             */
034            @SuppressWarnings("unchecked")
035            public ReflectionConverter(Method accessor, Constructor<? extends T> constructor) {
036                    super();
037                    this.accessor = accessor;
038                    this.constructor = constructor;
039                    singleArg = constructor!=null && constructor.getParameterTypes().length==1;
040                    if (accessor==null) {
041                            sourceType = (Class<S>) constructor.getParameterTypes()[0];
042                    } else {
043                            sourceType = (Class<S>) accessor.getDeclaringClass();
044                    }
045                    
046                    if (constructor==null) {
047                            targetType = (Class<T>) accessor.getReturnType();
048                    } else {
049                            targetType = constructor.getDeclaringClass();
050                    }               
051            }
052            
053            @SuppressWarnings("unchecked")
054            public T convert(S source, Converter master, Context context, ClassLoader classLoader) {
055                    try {
056                            Object param = accessor==null ? source : accessor.invoke(source);
057                            if (constructor==null) {
058                                    return (T) param;
059                            }
060                            
061                            if (singleArg) {
062                                    return constructor.newInstance(new Object[] {param});
063                            }
064                            
065                            return constructor.newInstance(new Object[] {param, context});
066                    } catch (Exception e) {
067                            logger.log(Level.FINE, "Cannot convert "+source.getClass().getName()+" to " + constructor.getDeclaringClass()+": "+e, e);
068                            return null;
069                    }
070            }
071    
072            public Class<S> getSourceType() {
073                    return sourceType;
074            }
075    
076            public Class<? extends T> getTargetType() {
077                    return targetType;
078            }
079            
080            /**
081             * Discovers constructor conversions.
082             * @param target Target type
083             * @return Collection of discovered converters.
084             */
085            @SuppressWarnings("unchecked")
086            public static <T> Collection<ReflectionConverter<?, T>> discoverConstructorConverters(Class<T> target) {
087                    Collection<ReflectionConverter<?, T>> ret=new ArrayList<ReflectionConverter<?, T>>();
088                    if (!target.isInterface()) {
089                            for (int i=0, cc=target.getConstructors().length; i<cc; i++) {
090                                    Constructor<?> constructor = target.getConstructors()[i];
091                                    Class<?>[] parameterTypes = constructor.getParameterTypes();
092                                    if (parameterTypes.length==1 && !target.isAssignableFrom(parameterTypes[0])) {
093                                            
094                                            ret.add(new ReflectionConverter(null, constructor));
095                                    } else if (parameterTypes.length==2
096                                                    && !target.isAssignableFrom(parameterTypes[0])
097                                                    && Context.class.equals(parameterTypes[1])) {
098                                                    
099                                            ret.add(new ReflectionConverter(null, constructor));
100                                    }
101                            }
102                    }
103                    return ret;
104            }       
105            
106            public String toString() {
107                    return this.getClass().getName() + " ("+sourceType+" -> "+targetType+", accessor: "+accessor+", constructor: "+constructor+")";
108            }
109            
110    }