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 }