001    /*
002     @license.text@
003      */
004    package biz.hammurapi.sql.columns;
005    
006    import java.lang.reflect.Method;
007    import java.sql.PreparedStatement;
008    import java.sql.ResultSet;
009    import java.sql.ResultSetMetaData;
010    import java.sql.SQLException;
011    import java.util.Collection;
012    import java.util.Collections;
013    import java.util.HashMap;
014    import java.util.Iterator;
015    import java.util.LinkedList;
016    import java.util.Map;
017    
018    import biz.hammurapi.config.Context;
019    import biz.hammurapi.convert.Converter;
020    import biz.hammurapi.convert.ConvertingService;
021    import biz.hammurapi.sql.SQLExceptionEx;
022    
023    
024    /**
025     * @author Pavel Vlasov
026     *
027     * @version $Revision: 1.13 $
028     */
029    public class ObjectColumn extends Column {
030            private Object value;
031            
032            // Original stuff       
033            private Object originalValue;
034            private boolean isOriginalValueSet;
035            
036            public Object getOriginalValue() {
037                    return isOriginalValueSet ? originalValue : value;
038            }
039            
040            public void parameterizeOriginal(PreparedStatement ps, int idx) throws SQLException {
041                    Object theOriginalValue=getOriginalValue();
042                    if (theOriginalValue==null) {
043                            ps.setNull(idx, sqlType==null ? java.sql.Types.NULL : sqlType.intValue());
044                    } else {                
045                            try {
046                                    Method setter = findSetter(theOriginalValue.getClass());
047                                    if ("setObject".equals(setter.getName())) {
048                                            if (sqlType==null) {
049                                                    ps.setObject(idx, theOriginalValue);
050                                            } else {
051                                                    ps.setObject(idx, theOriginalValue, sqlType.intValue());
052                                            }
053                                    } else {
054                                            setter.invoke(ps, new Object[] {new Integer(idx), theOriginalValue});
055                                    }
056                                    return;
057                            } catch (SQLException e) {
058                                    throw e;
059                            } catch (Exception e) {
060                                    throw new SQLExceptionEx("Caused by: "+e, e);
061                            }
062                    }
063            }
064            
065            public void setOriginal() {
066                    originalValue=value;
067                    isOriginalValueSet=true;
068            }
069            // End of original stuff
070            
071            private static final Collection ALL_SETTERS;
072    
073            private Integer sqlType;
074            
075            static {
076                    Collection allSetters=new LinkedList();
077                    for (int i=0, mc=PreparedStatement.class.getMethods().length; i<mc; i++) {
078                            Method m=PreparedStatement.class.getMethods()[i];
079                            if (m.getParameterTypes().length==2 && m.getParameterTypes()[0].equals(int.class) && m.getName().startsWith("set")) {
080                                    allSetters.add(m);
081                            }                       
082                    }
083                    ALL_SETTERS=Collections.unmodifiableCollection(allSetters);
084            }
085    
086    
087            /**
088             * @return Returns the value.
089             */
090            public Object getValue() {
091                    return value;
092            }
093            
094            /**
095             * @param value The value to set.
096             */
097            public void setValue(Object value) {
098                    if (force || (this.value!=value && (this.value==null || !this.value.equals(value)))) {
099                            if (clazz!=null && value!=null && !clazz.isInstance(value)) {
100                                    throw new ClassCastException("Invalid class "+value.getClass().getName()+", expected "+clazz.getName());
101                            }
102                            this.value = value;
103                            onChange();
104                    }
105            }
106            
107            public void setSqlType(int sqlType) {
108                    this.sqlType=new Integer(sqlType);
109            }
110            
111            /**
112             * @param name
113             * @param clazz Column class, can be null.
114             * @param isPrimaryKey
115             */
116            public ObjectColumn(String name, Class clazz, boolean isPrimaryKey) {
117                    super(name, isPrimaryKey);
118                    this.clazz=clazz;
119            }
120            
121            public ObjectColumn(String name, Class clazz, boolean isPrimaryKey, Object value) {
122                    super(name, isPrimaryKey);
123                    this.clazz=clazz;
124                    if (clazz!=null && value!=null && !clazz.isInstance(value)) {
125                            throw new ClassCastException("Invalid class "+value.getClass().getName()+", expected "+clazz.getName());
126                    }
127                    this.value=value;
128            }
129            
130            public ObjectColumn(String name, Class clazz, boolean isPrimaryKey, ResultSet rs) throws SQLException {
131                    super(name, isPrimaryKey);
132                    this.clazz=clazz;
133                    ResultSetMetaData metaData = rs.getMetaData();
134                    for (int i=1, c=metaData.getColumnCount(); i<=c; i++) {
135                            if (name.equals(metaData.getColumnName(i))) {
136                                    Object value = rs.getObject(i);
137                                    if (clazz!=null && value!=null && !clazz.isInstance(value)) {
138                                            throw new ClassCastException("Invalid class "+value.getClass().getName()+", expected "+clazz.getName());
139                                    }
140                                    this.value=value;
141                                    break;
142                            }
143                    }
144            }       
145            
146            private static Map setterMap=new HashMap();
147            
148            /**
149             * Finds parameter setter for object type
150             * @param type
151             * @return
152             */
153            public static Method findSetter(Class type) {
154                    synchronized (setterMap) {
155                            String typeName = type.getName();
156                            Method ret=(Method) setterMap.get(typeName);
157                            if (ret==null) {
158                                    int dotIdx=typeName.lastIndexOf('.');
159                                    String shortName=typeName.substring(dotIdx+1, dotIdx+2).toUpperCase()+typeName.substring(dotIdx+2);
160                                    
161                                    // Name match
162                                    Iterator it=ALL_SETTERS.iterator();
163                                    while (it.hasNext()) {
164                                            Method m=(Method) it.next();
165                                            if (m.getName().equals("set"+shortName) && m.getParameterTypes()[1].getName().equals(typeName)) {
166                                                    setterMap.put(typeName, m);
167                                                    return m;
168                                            }
169                                    }
170                                    
171                                    Method candidate=null;
172                                    // Parameter match
173                                    it=ALL_SETTERS.iterator();
174                                    while (it.hasNext()) {
175                                            Method m=(Method) it.next();
176                                            if (m.getParameterTypes()[1].isAssignableFrom(type)) {
177                                                    if (candidate==null || candidate.getParameterTypes()[1].isAssignableFrom(m.getParameterTypes()[1])) {
178                                                            candidate=m;
179                                                    }
180                                            }
181                                    }
182                                                                    
183                                    setterMap.put(typeName, candidate);
184                                    return candidate;
185                            }
186                            
187                            return ret;
188                    }
189            }
190    
191            /* (non-Javadoc)
192             * @see biz.hammurapi.sql.columns.Column#parameterize(java.sql.PreparedStatement, int)
193             */
194            protected void parameterizeInternal(PreparedStatement ps, int idx) throws SQLException {
195                    try {
196                            if (value==null) {
197                                    ps.setNull(idx, sqlType==null ? java.sql.Types.NULL : sqlType.intValue());
198                            } else {                
199                                    try {
200                                            Method setter = findSetter(value.getClass());
201                                            if ("setObject".equals(setter.getName())) {
202                                                    if (sqlType==null) {
203                                                            ps.setObject(idx, value);
204                                                    } else {
205                                                            ps.setObject(idx, value, sqlType.intValue());
206                                                    }
207                                            } else {
208                                                    setter.invoke(ps, new Object[] {new Integer(idx), value});
209                                            }
210                                            return;
211                                    } catch (SQLException e) {
212                                            throw e;
213                                    } catch (Exception e) {
214                                            throw new SQLExceptionEx("Could not parameterize "+getName()+": "+e, e);
215                                    }
216                            }
217                    } catch (SQLException e) {
218                            throw new SQLExceptionEx("Cannot parameterize. Value: "+value+", SQL Type: "+sqlType+", Cause: "+e, e);
219                    }
220            }
221            
222            /**
223             * Parameterizes prepared statement. Automatically finds proper setter.
224             * @param ps
225             * @param value
226             * @param idx
227             * @throws SQLException
228             */
229            public static void parameterize(PreparedStatement ps, Object value, int idx) throws SQLException {
230                    try {
231                            if (value==null) {
232                                    ps.setNull(idx, java.sql.Types.NULL);
233                            } else {                
234                                    try {
235                                            Method setter = findSetter(value.getClass());
236                                            if ("setObject".equals(setter.getName())) {
237                                                    ps.setObject(idx, value);
238                                            } else {
239                                                    setter.invoke(ps, new Object[] {new Integer(idx), value});
240                                            }
241                                            return;
242                                    } catch (SQLException e) {
243                                            throw e;
244                                    } catch (Exception e) {
245                                            throw new SQLExceptionEx("Could not parameterize: "+e, e);
246                                    }
247                            }
248                    } catch (SQLException e) {
249                            throw new SQLExceptionEx("Cannot parameterize. Value: "+value+", Cause: "+e, e);
250                    }
251            }
252            
253            public Object getObjectValue(boolean ov) {
254                    if (ov) {
255                            return isOriginalValueSet ? originalValue : null;
256                    }
257                    return value;
258            }
259            
260            public String toString() {
261                    return getName()+(isModified() ? "*" : "")+"("+(value==null ? "N/A" : value.getClass().getName())+")="+value;
262            }
263            
264        public boolean equals(Object otherColumn) {
265            if (!(otherColumn instanceof ObjectColumn)) {
266                return false;
267            }
268            
269                    Object otherValue=((ObjectColumn) otherColumn).value;
270                    if (value==null) {
271                            return otherValue==null;
272                    }
273                    
274                    if (otherValue==null) {
275                        return false;
276                    }
277                    
278                    return value.equals(otherValue);
279        }
280            
281            public int hashCode() {
282                    return getName().hashCode() ^ (value==null ? 0 : value.hashCode());
283            }
284    
285            public void load(String textValue) {
286                    if (clazz==null || textValue==null) {
287                            setValue(textValue);
288                    } else {
289                            setValue(ConvertingService.convert(textValue, clazz));
290                    }
291            }
292            
293            public void clear() {
294                    setValue(null);
295                    clearModified();
296            }
297            
298            private Class clazz;
299            
300            public void configure(Context context, Converter converter) {
301                    Object o=context.get(getName());
302                    if (o!=null) {
303                            setValue(clazz==null ? o : converter.convert(o, clazz, null));
304                    }               
305            }               
306    
307            protected String getType() {
308                    return clazz==null ? "java.lang.Object" : clazz.getName();
309            }       
310    
311            public void set(Column source) {
312                    setValue(((ObjectColumn) source).getValue());
313            }               
314            
315        /**
316         * Clears modified flag and sets original value to current value.
317         */
318        public void clearModified() {
319            super.clearModified();
320            originalValue=value;
321        }
322    }