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