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 }