001 /* 002 @license.text@ 003 */ 004 package biz.hammurapi.sql; 005 006 import java.lang.reflect.Constructor; 007 import java.lang.reflect.InvocationTargetException; 008 import java.sql.ResultSet; 009 import java.sql.SQLException; 010 import java.util.Map; 011 012 /** 013 * This projector constructs objects using database field values 014 * @author Pavel Vlasov 015 * @version $Revision: 1.2 $ 016 */ 017 public class ConstructorProjector extends BaseReflectionProjector implements Projector { 018 public static class ColumnName { 019 private String name; 020 021 public ColumnName(String name) { 022 this.name=name; 023 } 024 } 025 026 private Object[] args; 027 private Constructor constructor; 028 029 /** 030 * Use this constructor if target constructor shall take not only values from database fields as 031 * parameter, but other objects as well. 032 * @param constructor Constructor to instantiate object. 033 * @param args Constructor arguments. Arguments of type {@link ColumnName} will be replaced 034 * with values from corresponding fields from the database. 035 * @param typeMap See {@link java.sql.ResultSet#getObject(java.lang.String, java.util.Map)}. Can be null. 036 */ 037 public ConstructorProjector(Constructor constructor, Object[] args, Map typeMap) { 038 super(typeMap); 039 if (constructor.getParameterTypes().length!=args.length) { 040 throw new IllegalArgumentException("argNames length shall be equal to the number of constructor parameters"); 041 } 042 this.constructor=constructor; 043 this.args=args; 044 } 045 046 /** 047 * Use this constructor if only database fields values are used as target constructor parameters. 048 * @param constructor Constructor to instantiate object 049 * @param columnNames Names of columns that shall be passed as parameters to constructor. 050 * @param typeMap See {@link java.sql.ResultSet#getObject(java.lang.String, java.util.Map)}. Can be null. 051 */ 052 public ConstructorProjector(Constructor constructor, String[] columnNames, Map typeMap) { 053 this(constructor, columnNamesToArgs(columnNames), typeMap); 054 } 055 056 /** 057 * Use constructor for positioned projection. Column values will be passed to as constructor 058 * arguments according to their position. 059 * @param constructor 060 * @param typeMap 061 */ 062 public ConstructorProjector(Constructor constructor, Map typeMap) { 063 super(typeMap); 064 this.constructor=constructor; 065 } 066 067 /** 068 * 069 * @param fieldNames 070 * @return 071 */ 072 private static Object[] columnNamesToArgs(String[] fieldNames) { 073 Object[] ret=new Object[fieldNames.length]; 074 for (int i=0; i<fieldNames.length; i++) { 075 ret[i]=new ColumnName(fieldNames[i]); 076 } 077 return ret; 078 } 079 080 public Object project(ResultSet rs) throws SQLException { 081 Object[] params=new Object[constructor.getParameterTypes().length]; 082 for (int i=0; i<params.length; i++) { 083 if (args==null) { 084 params[i]=getColumn(rs, i+1); 085 } else if (args[i] instanceof ColumnName) { 086 params[i]=getColumn(rs, ((ColumnName) args[i]).name); 087 } else { 088 params[i]=args[i]; 089 } 090 } 091 092 try { 093 return constructor.newInstance(params); 094 } catch (InvocationTargetException e) { 095 throw new SQLExceptionEx(e); 096 } catch (InstantiationException e) { 097 throw new SQLExceptionEx(e); 098 } catch (IllegalAccessException e) { 099 throw new SQLExceptionEx(e); 100 } 101 } 102 103 }