001 /* 002 @license.text@ 003 */ 004 package biz.hammurapi.sql.columns; 005 006 import java.io.Serializable; 007 import java.sql.PreparedStatement; 008 import java.sql.ResultSet; 009 import java.sql.ResultSetMetaData; 010 import java.sql.SQLException; 011 import java.util.HashMap; 012 import java.util.Map; 013 014 import javax.xml.transform.TransformerException; 015 016 import org.w3c.dom.Element; 017 import org.w3c.dom.Node; 018 019 import biz.hammurapi.config.ConfigurationException; 020 import biz.hammurapi.config.Context; 021 import biz.hammurapi.config.ContextConfigurable; 022 import biz.hammurapi.config.DomConfigFactory; 023 import biz.hammurapi.config.DomConfigurable; 024 import biz.hammurapi.sql.SQLExceptionEx; 025 import biz.hammurapi.util.Attributable; 026 import biz.hammurapi.xml.dom.DOMUtils; 027 import biz.hammurapi.xml.dom.AbstractDomObject; 028 import biz.hammurapi.xml.dom.CompositeDomSerializer; 029 030 031 /** 032 * Base class for different column types 033 * @author Pavel Vlasov 034 * @version $Revision: 1.14 $ 035 */ 036 public abstract class Column 037 implements 038 Cloneable, 039 ContextConfigurable, 040 DomConfigurable, 041 Attributable, 042 Serializable { 043 044 private ColumnChangeListener listener; 045 private String name; 046 private boolean isModified; 047 private boolean isPrimaryKey; 048 private String label; 049 050 public abstract void setOriginal(); 051 052 /** 053 * Clears modified flag. 054 */ 055 public void clearModified() { 056 isModified=false; 057 } 058 059 /** 060 * Sets field value to default and clears modified flag. 061 * 062 */ 063 public abstract void clear(); 064 065 /** 066 * @param listener The listener to set. 067 */ 068 public void setListener(ColumnChangeListener listener) { 069 this.listener = listener; 070 } 071 072 /** 073 * @return Returns the listener. 074 */ 075 public ColumnChangeListener getListener() { 076 return listener; 077 } 078 079 protected void onChange() { 080 isModified=true; 081 if (listener!=null) { 082 listener.onChange(this); 083 } 084 } 085 086 /** 087 * @param name Column name 088 * @param isPrimaryKey 'true' if column is part of primary key 089 */ 090 public Column(String name, boolean isPrimaryKey) { 091 super(); 092 this.name = name; 093 this.isPrimaryKey = isPrimaryKey; 094 } 095 096 /** 097 * @return Column name if value was modified, null otherwise. 098 */ 099 public String listName() { 100 return isModified ? name : null; 101 } 102 103 /** 104 * Parameterizes prepared statement. 105 * @param ps 106 * @param idx Parameter index 107 * @param force If true column is treated a modified 108 * @return idx+1 if column was modified and prepared statement was parameterized, idx otherwise. 109 */ 110 public int parameterize(PreparedStatement ps, int idx, boolean force) throws SQLException { 111 if (isModified || force) { 112 try { 113 parameterizeInternal(ps, idx); 114 } catch (SQLException e) { 115 throw new SQLExceptionEx("Could not parameterize "+getName()+": "+e, e); 116 } 117 return idx+1; 118 } 119 120 return idx; 121 } 122 123 /** 124 * Implement this method in subclasses. 125 */ 126 protected abstract void parameterizeInternal(PreparedStatement ps, int idx) throws SQLException; 127 128 /** 129 * Implement this method in subclasses. 130 */ 131 public abstract void parameterizeOriginal(PreparedStatement ps, int idx) throws SQLException; 132 133 /** 134 * @return Returns the isPrimaryKey. 135 */ 136 public boolean isPrimaryKey() { 137 return isPrimaryKey; 138 } 139 140 /** 141 * @return name 142 */ 143 public String getName() { 144 return name; 145 } 146 147 /** 148 * Returns column value as object 149 * @param originalValue if true this method returns original value instead of current value. 150 * @return 151 */ 152 public abstract Object getObjectValue(boolean originalValue); 153 154 /** 155 * @param owner 156 */ 157 public void toDom(Element owner, String nodeName, boolean originalValue) { 158 // String nodeName = domName==null ? name : domName; 159 if (nodeName==null) { 160 nodeName=getName(); 161 } 162 163 Object objectValue=getObjectValue(false); 164 165 if (nodeName.length()==0) { 166 // Zero mapping 167 return; 168 } 169 170 if (nodeName.startsWith("@")) { 171 // Attribute mapping 172 if (objectValue!=null) { 173 owner.setAttribute(nodeName.substring(1), objectValue.toString()); 174 } 175 } else if (nodeName.equals(".")) { 176 // Text mapping 177 if (objectValue!=null) { 178 owner.appendChild(owner.getOwnerDocument().createTextNode(objectValue.toString())); 179 } 180 } else if (nodeName.startsWith("!")) { 181 // Simple mapping 182 if (objectValue!=null) { 183 Element el=owner.getOwnerDocument().createElement(nodeName.substring(1)); 184 owner.appendChild(el); 185 el.appendChild(owner.getOwnerDocument().createTextNode(objectValue.toString())); 186 } 187 } else { 188 // full mapping 189 Element el=owner.getOwnerDocument().createElement(nodeName); 190 owner.appendChild(el); 191 192 CompositeDomSerializer cds = CompositeDomSerializer.getThreadInstance(); 193 194 if (objectValue==null) { 195 el.setAttribute("is-null", "yes"); 196 } else { 197 cds.toDomSerializable(objectValue).toDom(el); 198 } 199 200 if (isPrimaryKey) { 201 el.setAttribute("primary-key", "yes"); 202 } 203 if (isModified) { 204 el.setAttribute("modified", "yes"); 205 } 206 207 if (label!=null) { 208 el.setAttribute("label", label); 209 } 210 211 el.setAttribute("column-type", getType()); 212 213 el.setAttribute("align", getAlignment()); 214 215 if (!attributes.isEmpty()) { 216 cds.toDomSerializable(attributes).toDom(AbstractDomObject.addElement(el, "column-attributes")); 217 } 218 } 219 } 220 221 protected abstract String getType(); 222 223 /** 224 * @return 225 */ 226 protected String getAlignment() { 227 return "left"; 228 } 229 230 /** 231 * @param typeName 232 * @return Column type for Java type 233 */ 234 public static Class columnType(String typeName) { 235 if ("boolean".equals(typeName)) { 236 return BooleanColumn.class; 237 } else if ("byte".equals(typeName)) { 238 return ByteColumn.class; 239 } else if ("char".equals(typeName)) { 240 return CharColumn.class; 241 } else if ("double".equals(typeName)) { 242 return DoubleColumn.class; 243 } else if ("float".equals(typeName)) { 244 return FloatColumn.class; 245 } else if ("int".equals(typeName)) { 246 return IntColumn.class; 247 } else if ("long".equals(typeName)) { 248 return LongColumn.class; 249 } else if ("short".equals(typeName)) { 250 return ShortColumn.class; 251 } else { 252 return ObjectColumn.class; 253 } 254 } 255 256 /** 257 * @return Returns the isModified. 258 */ 259 public boolean isModified() { 260 return isModified; 261 } 262 263 protected boolean force; 264 // private String domName; 265 266 /** 267 * @param force 268 */ 269 public void setForce(boolean force) { 270 this.force=force; 271 } 272 273 /** 274 * @param rs 275 * @param name 276 * @return True if result set has a column with specified name. 277 * @throws SQLException 278 */ 279 public static boolean hasColumn(ResultSet rs, String name) throws SQLException { 280 ResultSetMetaData metaData = rs.getMetaData(); 281 for (int i=1, c=metaData.getColumnCount(); i<=c; i++) { 282 if (name.equals(metaData.getColumnName(i))) { 283 return true; 284 } 285 } 286 return false; 287 } 288 289 /** 290 * Loads value from XML Element 291 * @param textValue Text value 292 */ 293 public abstract void load(String textValue); 294 295 public Object clone() throws CloneNotSupportedException { 296 return super.clone(); 297 } 298 299 /** 300 * @return display label for column 301 */ 302 public String getLabel() { 303 return label; 304 } 305 306 /** 307 * Sets display label for column 308 * @param label 309 */ 310 public void setLabel(String label) { 311 this.label = label; 312 } 313 314 // /** 315 // * @param domName 316 // */ 317 // public void setDomName(String domName) { 318 // this.domName=domName; 319 // } 320 // 321 // public String getDomName() { 322 // return domName; 323 // } 324 325 /** 326 * Copies values from source column 327 * @param source 328 */ 329 public abstract void set(Column source); 330 331 private Map attributes=new HashMap(); 332 333 public void setAttribute(Object key, Object value) { 334 attributes.put(key, value); 335 } 336 337 public Object getAttribute(Object key) { 338 return attributes.get(key); 339 } 340 341 public Object removeAttribute(Object key) { 342 return attributes.remove(key); 343 } 344 345 public void configure(Node configNode, Context context) throws ConfigurationException { 346 if (!((Element) configNode).getAttribute("is-null").equals("yes")) { 347 load(AbstractDomObject.getElementText(configNode)); 348 } 349 350 DomConfigFactory dcf=new DomConfigFactory(); 351 attributes.clear(); 352 try { 353 Node attributesNode = DOMUtils.selectSingleNode(configNode, "column-attributes"); 354 if (attributesNode!=null) { 355 attributes.putAll((Map) dcf.create(attributesNode)); 356 } 357 } catch (Exception e) { 358 throw new ConfigurationException("Cannot load column attributes: "+e, e); 359 } 360 361 } 362 }