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