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}