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