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 }