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    }