    001package com.hammurapi.convert;
    002
    003import java.lang.reflect.Constructor;
    004import java.lang.reflect.Method;
    005import java.util.ArrayList;
    006import java.util.Collection;
    007import java.util.logging.Level;
    008import java.util.logging.Logger;
    009
    010import com.hammurapi.common.Context;
    011
    012
    013/**
    014 * Converts one type to another using accessor/constructor combination.
    015 * @author Pavel
    016 *
    017 */
    018public 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}