001 /* 002 @license.text@ 003 */ 004 package biz.hammurapi.sql; 005 006 import java.io.Serializable; 007 import java.sql.PreparedStatement; 008 import java.sql.SQLException; 009 import java.sql.Types; 010 import java.util.ArrayList; 011 import java.util.Collection; 012 import java.util.HashMap; 013 import java.util.HashSet; 014 import java.util.Iterator; 015 import java.util.Map; 016 import java.util.Properties; 017 import java.util.Set; 018 import java.util.Map.Entry; 019 020 import org.w3c.dom.Element; 021 import org.w3c.dom.Node; 022 import org.xml.sax.Attributes; 023 import org.xml.sax.ContentHandler; 024 import org.xml.sax.SAXException; 025 import org.xml.sax.helpers.DefaultHandler; 026 027 import biz.hammurapi.config.ConfigurationException; 028 import biz.hammurapi.config.Context; 029 import biz.hammurapi.config.ContextConfigurable; 030 import biz.hammurapi.config.DomConfigFactory; 031 import biz.hammurapi.config.DomConfigurable; 032 import biz.hammurapi.convert.Converter; 033 import biz.hammurapi.convert.ConvertingService; 034 import biz.hammurapi.sql.columns.Column; 035 import biz.hammurapi.sql.columns.ColumnChangeListener; 036 import biz.hammurapi.util.Attributable; 037 import biz.hammurapi.util.ClassResourceLoader; 038 import biz.hammurapi.util.Observable; 039 import biz.hammurapi.util.Observer; 040 import biz.hammurapi.util.Versioned; 041 import biz.hammurapi.xml.dom.AbstractDomObject; 042 import biz.hammurapi.xml.dom.DOMUtils; 043 import biz.hammurapi.xml.dom.DomSerializable; 044 045 046 /** 047 * SQLC-generated interface implementations implement this method to achieve 048 * differential update functionality - inserting and updating only modified fields. 049 * @author Pavel Vlasov 050 * @version $Revision: 1.11 $ 051 */ 052 public class DatabaseObject 053 implements 054 DomSerializable, 055 ColumnChangeListener, 056 Cloneable, 057 ContextConfigurable, 058 Context, 059 DomConfigurable, 060 Attributable, 061 Versioned, 062 Observable, 063 IDatabaseObject, 064 Serializable { 065 066 protected Collection columns=new ArrayList(); 067 private Map columnMap=new HashMap(); 068 private Map columnXmlMap=new HashMap(); 069 private boolean force; 070 private boolean isDeleted; 071 072 protected Column getColumn(String name) { 073 return (Column) columnMap.get(name); 074 } 075 076 /** 077 * Default constructor 078 */ 079 public DatabaseObject() { 080 // Default constructor 081 } 082 083 /** 084 * 085 * @param force Forces columns to be marked as 086 * modified if setter method is invoked even if value being set equals to existing column 087 * value. Useful during inserts with non-nullable columns which map to primitive 088 * types and as such have default values. 089 */ 090 public DatabaseObject(boolean force) { 091 this.force=force; 092 } 093 094 protected void addColumn(Column column) { 095 column.setForce(force); 096 columns.add(column); 097 columnMap.put(column.getName(), column); 098 columnXmlMap.put(column.getXmlName(), column); 099 column.setListener(this); 100 } 101 102 /* (non-Javadoc) 103 * @see biz.hammurapi.sql.IDatabaseObject#update(biz.hammurapi.sql.SQLProcessor, java.lang.String) 104 */ 105 public int update(SQLProcessor processor, String tableName) throws SQLException { 106 StringBuffer sb=new StringBuffer("UPDATE "); 107 sb.append(tableName); 108 sb.append(" SET "); 109 boolean hasColumns=false; 110 Iterator it=columns.iterator(); 111 while (it.hasNext()) { 112 Column column = (Column) it.next(); 113 String colName=(column).listName(); 114 if (colName!=null) { 115 if (hasColumns) { 116 sb.append(", "); 117 } 118 sb.append(colName); 119 sb.append("=?"); 120 hasColumns=true; 121 122 } 123 } 124 125 if (!hasColumns) { 126 return 0; 127 } 128 129 sb.append(" WHERE "); 130 131 boolean hasPkColumns=false; 132 it=columns.iterator(); 133 while (it.hasNext()) { 134 Column column = (Column) it.next(); 135 if (column.isPrimaryKey()) { 136 String colName=(column).getName(); 137 if (hasPkColumns) { 138 sb.append(" AND "); 139 } 140 sb.append(colName); 141 sb.append("=?"); 142 hasPkColumns=true; 143 } 144 } 145 146 int ret=processor.processUpdate(sb.toString(), new Parameterizer() { 147 public int parameterize(PreparedStatement ps, int idx) throws SQLException { 148 Iterator it=columns.iterator(); 149 while (it.hasNext()) { 150 Column column = (Column) it.next(); 151 idx=column.parameterize(ps, idx, false); 152 } 153 154 it=columns.iterator(); 155 while (it.hasNext()) { 156 Column column = (Column) it.next(); 157 if (column.isPrimaryKey()) { 158 column.parameterizeOriginal(ps, idx++); 159 } 160 } 161 return idx; 162 } 163 }); 164 165 it=columns.iterator(); 166 while (it.hasNext()) { 167 ((Column) it.next()).clearModified(); 168 } 169 170 originalVersion=objectVersion; 171 isModified=false; 172 173 return ret; 174 } 175 176 /* (non-Javadoc) 177 * @see biz.hammurapi.sql.IDatabaseObject#delete(biz.hammurapi.sql.SQLProcessor, java.lang.String) 178 */ 179 public int delete(SQLProcessor processor, String tableName) throws SQLException { 180 181 StringBuffer sb=new StringBuffer("DELETE FROM "); 182 sb.append(tableName); 183 sb.append(" WHERE "); 184 185 boolean hasPkColumns=false; 186 Iterator it=columns.iterator(); 187 while (it.hasNext()) { 188 Column column = (Column) it.next(); 189 if (column.isPrimaryKey()) { 190 String colName=(column).getName(); 191 if (hasPkColumns) { 192 sb.append(" AND "); 193 } 194 sb.append(colName); 195 sb.append("=?"); 196 hasPkColumns=true; 197 } 198 } 199 200 int ret=processor.processUpdate(sb.toString(), new Parameterizer() { 201 public int parameterize(PreparedStatement ps, int idx) throws SQLException { 202 Iterator it=columns.iterator(); 203 while (it.hasNext()) { 204 Column column = (Column) it.next(); 205 if (!column.isPrimaryKey()) { 206 idx=column.parameterize(ps, idx, false); 207 } 208 } 209 210 it=columns.iterator(); 211 while (it.hasNext()) { 212 Column column = (Column) it.next(); 213 if (column.isPrimaryKey()) { 214 idx=column.parameterize(ps, idx, true); 215 } 216 } 217 return idx; 218 } 219 }); 220 221 it=columns.iterator(); 222 while (it.hasNext()) { 223 ((Column) it.next()).clearModified(); 224 } 225 226 originalVersion=objectVersion; 227 isDeleted=true; 228 return ret; 229 } 230 231 /* (non-Javadoc) 232 * @see biz.hammurapi.sql.IDatabaseObject#insert(biz.hammurapi.sql.SQLProcessor, java.lang.String) 233 */ 234 public int insert(SQLProcessor processor, String tableName) throws SQLException { 235 StringBuffer sb=new StringBuffer("INSERT INTO "); 236 sb.append(tableName); 237 sb.append(" ("); 238 int columnsCounter=0; 239 Iterator it=columns.iterator(); 240 while (it.hasNext()) { 241 String colName=((Column) it.next()).listName(); 242 if (colName!=null) { 243 if (columnsCounter>0) { 244 sb.append(", "); 245 } 246 sb.append(colName); 247 columnsCounter++; 248 249 } 250 } 251 252 if (columnsCounter==0) { 253 return 0; 254 } 255 256 sb.append(") VALUES ("); 257 for (int i=0; i<columnsCounter; i++) { 258 if (i>0) { 259 sb.append(", "); 260 } 261 sb.append("?"); 262 } 263 sb.append(")"); 264 265 int ret=processor.processUpdate(sb.toString(), new Parameterizer() { 266 public int parameterize(PreparedStatement ps, int idx) throws SQLException { 267 Iterator it=columns.iterator(); 268 while (it.hasNext()) { 269 idx = ((Column) it.next()).parameterize(ps, idx, false); 270 } 271 return idx; 272 } 273 }); 274 275 it=columns.iterator(); 276 while (it.hasNext()) { 277 ((Column) it.next()).clearModified(); 278 } 279 280 originalVersion=objectVersion; 281 isModified=false; 282 283 return ret; 284 } 285 286 /* (non-Javadoc) 287 * @see biz.hammurapi.sql.IDatabaseObject#fromDom(org.w3c.dom.Element) 288 */ 289 public void fromDom(Element holder) throws ConfigurationException { 290 fromDom(holder, getNameMap(getClass())); 291 } 292 293 private void loadAttributes(Element holder) throws ConfigurationException { 294 DomConfigFactory dcf=new DomConfigFactory(); 295 attributes.clear(); 296 try { 297 Node attributesNode = DOMUtils.selectSingleNode(holder, "object-attributes"); 298 if (attributesNode!=null) { 299 attributes.putAll((Map) dcf.create(attributesNode)); 300 } 301 } catch (Exception e) { 302 throw new ConfigurationException("Cannot load database object attributes: "+e, e); 303 } 304 305 } 306 307 /* (non-Javadoc) 308 * @see biz.hammurapi.sql.IDatabaseObject#fromDom(org.w3c.dom.Element, java.util.Properties) 309 */ 310 public void fromDom(Element holder, Properties nameMap) throws ConfigurationException { 311 Iterator it=columns.iterator(); 312 while (it.hasNext()) { 313 Column column=(Column) it.next(); 314 String nodeName = nameMap.getProperty(column.getName(), column.getName()).trim(); 315 316 if (nodeName.length()==0) { 317 // Zero mapping 318 continue; 319 } else if (nodeName.startsWith("@")) { 320 // Attribute mapping 321 if (holder.hasAttribute(nodeName.substring(1))) { 322 column.load(holder.getAttribute(nodeName.substring(1))); 323 } 324 } else if (nodeName.equals(".")) { 325 // Text content mapping 326 column.load(AbstractDomObject.getElementText(holder)); 327 } else if (nodeName.startsWith("!")) { 328 // Simple node mapping 329 try { 330 Node cNode=DOMUtils.selectSingleNode(holder, nodeName.substring(1)); 331 if (cNode!=null) { 332 column.load(AbstractDomObject.getElementText(cNode)); 333 } 334 } catch (Exception e) { 335 throw new ConfigurationException("Cannot load column "+column.getName()); 336 } 337 } else { 338 // Regular node mapping 339 try { 340 Node cNode=DOMUtils.selectSingleNode(holder, nodeName); 341 if (cNode!=null) { 342 column.configure(cNode, null, getClass().getClassLoader()); 343 } 344 } catch (Exception e) { 345 throw new ConfigurationException("Cannot load column "+column.getName()); 346 } 347 } 348 } 349 350 loadAttributes(holder); 351 } 352 353 public void toDom(Element holder) { 354 toDom(holder, getNameMap(getClass()), false); 355 } 356 357 /** 358 * XML namespace. 359 */ 360 protected String xmlNamespace; 361 362 /** 363 * Name of object XML Element. 364 */ 365 protected String xmlElement; 366 367 /** 368 * Converts fully qualified class name to namespace and element name. 369 * @param className 370 * @return String array with two elements. First - namespace, second - element name 371 */ 372 public static String[] xmlNames(String className) { 373 String[] ret={null, null}; 374 className = className.replace('$', '.'); 375 int idx = className.lastIndexOf('.'); 376 if (idx!=-1) { 377 String[] pName = className.substring(0, idx).split("\\."); 378 StringBuffer sb = new StringBuffer("http://www."); 379 if (pName.length>1) { 380 sb.append(pName[1]); 381 sb.append("."); 382 sb.append(pName[0]); 383 for (int i=2; i<pName.length; ++i) { 384 sb.append("/"); 385 sb.append(pName[i]); 386 } 387 ret[0] = sb.toString(); 388 } else { 389 sb.append(pName[0]); 390 } 391 className = className.substring(idx+1); 392 } 393 394 StringBuffer sb = new StringBuffer(); 395 for (int i=0, j=0, l=className.length(); i<=l; i++) { 396 if (i==l && j<i) { 397 if (sb.length()>0) { 398 sb.append("-"); 399 } 400 sb.append(className.substring(j, i)); 401 } else if (Character.isUpperCase(className.charAt(i)) && j<i && Character.isLowerCase(className.charAt(i-1))) { 402 if (sb.length()>0) { 403 sb.append("-"); 404 } 405 sb.append(className.substring(j, i)); 406 j=i; 407 } 408 } 409 410 ret[1]=sb.toString().toLowerCase(); 411 return ret; 412 } 413 414 /** 415 * Serializes object to XML events 416 * @param contentHandler Events receiver 417 * @param withHolder If true outputs object XML element, otherwise only nested elements. 418 * @throws SAXException 419 */ 420 public void toSax(ContentHandler contentHandler, boolean withHolder) throws SAXException { 421 if (xmlElement==null) { 422 String[] xmlNames = xmlNames(getClass().getName()); 423 xmlElement=xmlNames[1]; 424 xmlNamespace=xmlNames[0]; 425 } 426 if (withHolder) { 427 contentHandler.startElement(xmlNamespace, xmlElement, xmlElement, null); 428 } 429 430 childrenToSax(contentHandler); 431 if (withHolder) { 432 contentHandler.endElement(xmlNamespace, xmlElement, xmlElement); 433 } 434 } 435 436 /** 437 * Outputs child objects to SAX. 438 * @param contentHandler 439 * @throws SAXException 440 */ 441 protected void childrenToSax(ContentHandler contentHandler) throws SAXException { 442 if (xmlElement==null) { 443 String[] xmlNames = xmlNames(getClass().getName()); 444 xmlElement=xmlNames[1]; 445 xmlNamespace=xmlNames[0]; 446 } 447 448 Iterator it=columns.iterator(); 449 while (it.hasNext()) { 450 Column column = (Column) it.next(); 451 String colName=(column).listName(); 452 if (colName!=null) { 453 column.toSax(xmlNamespace, contentHandler); 454 } 455 } 456 } 457 458 /* (non-Javadoc) 459 * @see biz.hammurapi.sql.IDatabaseObject#toDom(org.w3c.dom.Element, java.util.Properties, boolean) 460 */ 461 public void toDom(Element holder, Properties nameMap, boolean originals) { 462 if (nameMap==null) { 463 nameMap=new Properties(); 464 } 465 466 String cna = nameMap.getProperty("@type", "type").trim(); 467 if (!"".equals(cna)) { 468 holder.setAttribute(cna, getClass().getName()); 469 } 470 471 Iterator it=columns.iterator(); 472 while (it.hasNext()) { 473 Column column = (Column) it.next(); 474 column.toDom(holder, nameMap.getProperty(column.getName(), column.getName()).trim(), originals); 475 } 476 477 if (!attributes.isEmpty()) { 478 DomSerializable ads = (DomSerializable) ConvertingService.convert(attributes, DomSerializable.class); 479 ads.toDom(AbstractDomObject.addElement(holder, "object-attributes")); 480 } 481 } 482 483 public String toString() { 484 StringBuffer ret=new StringBuffer(getClass().getName()); 485 ret.append("["); 486 Iterator it=columns.iterator(); 487 while (it.hasNext()) { 488 ret.append(it.next()); 489 if (it.hasNext()) { 490 ret.append(", "); 491 } 492 } 493 494 ret.append("]"); 495 return ret.toString(); 496 } 497 498 /** 499 * Sets modified flag to true and increments version number. 500 * Also broadcasts the change to observers. Changed column is 501 * passed as second argument of update() method. 502 * Override this method in subclasses to react on 503 * change events, but don't forget to invoke 504 * super.onChange(). 505 * @param column Changed column 506 */ 507 public void onChange(Column column) { 508 isModified=true; 509 510 ++objectVersion; 511 512 if (column.isPrimaryKey()) { 513 isDeleted=false; 514 } 515 516 synchronized (observers) { 517 Iterator it=observers.iterator(); 518 while (it.hasNext()) { 519 ((Observer) it.next()).update(this, column); 520 } 521 } 522 523 } 524 525 /* (non-Javadoc) 526 * @see biz.hammurapi.sql.IDatabaseObject#setOriginal() 527 */ 528 public void setOriginal() { 529 objectVersion=originalVersion; 530 Iterator it=columns.iterator(); 531 while (it.hasNext()) { 532 ((Column) it.next()).setOriginal(); 533 } 534 } 535 536 private boolean isModified=false; 537 538 /* (non-Javadoc) 539 * @see biz.hammurapi.sql.IDatabaseObject#isModified() 540 */ 541 public boolean isModified() { 542 return isModified; 543 } 544 545 /* (non-Javadoc) 546 * @see biz.hammurapi.sql.IDatabaseObject#isDeleted() 547 */ 548 public boolean isDeleted() { 549 return isDeleted; 550 } 551 552 /** 553 * Two objects are considered equal and all their fields are equal. 554 * @param otherBean Other object 555 * @return true if object classes are equal and all member column values are 556 * equal. 557 */ 558 public boolean equals(Object otherBean) { 559 if (otherBean==null) { 560 return false; 561 } else if (getClass().equals(otherBean.getClass())) { 562 Collection myColumns=new ArrayList(columns); 563 Collection otherColumns=new ArrayList(((DatabaseObject) otherBean).columns); 564 Iterator mcit=myColumns.iterator(); 565 Z: 566 while (mcit.hasNext()) { 567 Column mc=(Column) mcit.next(); 568 Iterator ocit=otherColumns.iterator(); 569 while (ocit.hasNext()) { 570 Column oc=(Column) ocit.next(); 571 if (mc.getName().equals(oc.getName())) { 572 if (mc.equals(oc)) { 573 ocit.remove(); 574 mcit.remove(); 575 continue Z; 576 } 577 578 return false; 579 } 580 } 581 } 582 583 return myColumns.isEmpty() && otherColumns.isEmpty(); 584 } else { 585 return false; 586 } 587 } 588 589 public int hashCode() { 590 int ret=0; 591 Iterator cit=columns.iterator(); 592 while (cit.hasNext()) { 593 ret^=cit.next().hashCode(); 594 } 595 return ret; 596 } 597 598 /** 599 * Clones object, clears columns collection, clears isDeleted and isModified flags. 600 * Subclasses shall add cloned columns. 601 */ 602 public Object clone() throws CloneNotSupportedException { 603 DatabaseObject ret = (DatabaseObject) super.clone(); 604 ret.columns.clear(); 605 ret.isDeleted=false; 606 ret.isModified=false; 607 return ret; 608 } 609 610 /* (non-Javadoc) 611 * @see biz.hammurapi.sql.IDatabaseObject#clear() 612 */ 613 public void clear() { 614 Iterator it = columns.iterator(); 615 while (it.hasNext()) { 616 ((Column) it.next()).clear(); 617 } 618 isModified=false; 619 isDeleted=false; 620 } 621 622 public void configure(Context context, Converter converter) throws ConfigurationException { 623 Iterator it = columns.iterator(); 624 while (it.hasNext()) { 625 ((Column) it.next()).configure(context, converter); 626 } 627 } 628 629 public Object get(String name) { 630 Column col=(Column) columnMap.get(name); 631 return col==null ? null : col.getObjectValue(false); 632 } 633 634 public void configure(Node configNode, Context context, ClassLoader classLoader) throws ConfigurationException { 635 fromDom((Element) configNode); 636 } 637 638 private static Map nnMap=new HashMap(); 639 640 private static Properties getNameMap(Class clazz) { 641 synchronized (nnMap) { 642 Properties ret=(Properties) nnMap.get(clazz.getName()); 643 if (ret==null) { 644 ret=new ClassResourceLoader(clazz).getProperties(null, "namemap"); 645 nnMap.put(clazz.getName(), ret); 646 } 647 return ret; 648 } 649 } 650 651 /* (non-Javadoc) 652 * @see biz.hammurapi.sql.IDatabaseObject#copy(biz.hammurapi.sql.DatabaseObject) 653 */ 654 public void copy(DatabaseObject source) { 655 Iterator it=columns.iterator(); 656 while (it.hasNext()) { 657 Column targetColumn=(Column) it.next(); 658 Column sourceColumn=(Column) source.columnMap.get(targetColumn.getName()); 659 if (targetColumn.getClass().isInstance(sourceColumn)) { // Copy values only from compatible columns 660 targetColumn.set(sourceColumn); 661 if (targetColumn.isPrimaryKey()) { 662 targetColumn.clearModified(); 663 } 664 } 665 } 666 } 667 668 private Map attributes=new HashMap(); 669 670 public void setAttribute(Object key, Object value) { 671 attributes.put(key, value); 672 } 673 674 public Object getAttribute(Object key) { 675 return attributes.get(key); 676 } 677 678 public Object removeAttribute(Object key) { 679 return attributes.remove(key); 680 } 681 682 /* (non-Javadoc) 683 * @see biz.hammurapi.sql.IDatabaseObject#setColumnAttribute(java.lang.String, java.lang.Object, java.lang.Object) 684 */ 685 public void setColumnAttribute(String columnName, Object key, Object value) { 686 Column column=(Column) columnMap.get(columnName); 687 if (column==null) { 688 throw new IllegalArgumentException("Column not found: "+columnName); 689 } 690 column.setAttribute(key, value); 691 } 692 693 /* (non-Javadoc) 694 * @see biz.hammurapi.sql.IDatabaseObject#getColumnAttribute(java.lang.String, java.lang.Object) 695 */ 696 public Object getColumnAttribute(String columnName, Object key) { 697 Column column=(Column) columnMap.get(columnName); 698 if (column==null) { 699 throw new IllegalArgumentException("Column not found: "+columnName); 700 } 701 return column.getAttribute(key); 702 } 703 704 /* (non-Javadoc) 705 * @see biz.hammurapi.sql.IDatabaseObject#removeColumnAttribute(java.lang.String, java.lang.Object) 706 */ 707 public Object removeColumnAttribute(String columnName, Object key) { 708 Column column=(Column) columnMap.get(columnName); 709 if (column==null) { 710 throw new IllegalArgumentException("Column not found: "+columnName); 711 } 712 return column.removeAttribute(key); 713 } 714 715 private static Map sqlTypes=new HashMap(); 716 717 /** 718 * Allows to override generated column types with <class name>.sqltypes resource. 719 * Subclasses shall use this method when dealing with Object columns. 720 * @param columnName 721 * @param generatedType 722 * @return 723 */ 724 protected int getSqlType(String columnName, int generatedType) { 725 Map typeMap; 726 synchronized (sqlTypes) { 727 String className = getClass().getName(); 728 if (sqlTypes.containsKey(className)) { 729 typeMap=(Map) sqlTypes.get(className); 730 } else { 731 Properties literalMap=new ClassResourceLoader(getClass()).getProperties(null, "sqltypes"); 732 typeMap=new HashMap(); 733 sqlTypes.put(className, typeMap); 734 Iterator it=literalMap.entrySet().iterator(); 735 while (it.hasNext()) { 736 Entry entry=(Entry) it.next(); 737 try { 738 Integer type = (Integer) Types.class.getDeclaredField((String) entry.getValue()).get(null) ; 739 typeMap.put(entry.getKey(), type); 740 } catch (IllegalArgumentException e) { 741 System.err.println("Invalid SQL Type "+entry.getValue()+", ignored. Cause: "+e); 742 e.printStackTrace(); 743 } catch (SecurityException e) { 744 System.err.println("Invalid SQL Type "+entry.getValue()+", ignored. Cause: "+e); 745 e.printStackTrace(); 746 } catch (IllegalAccessException e) { 747 System.err.println("Invalid SQL Type "+entry.getValue()+", ignored. Cause: "+e); 748 e.printStackTrace(); 749 } catch (NoSuchFieldException e) { 750 System.err.println("Invalid SQL Type "+entry.getValue()+", ignored. Cause: "+e); 751 e.printStackTrace(); 752 } 753 } 754 } 755 } 756 757 Integer st=(Integer) typeMap.get(columnName); 758 return st==null ? generatedType : st.intValue(); 759 } 760 761 /** 762 * Subclasses can choose to read object version from the database. 763 */ 764 protected int objectVersion; 765 protected int originalVersion; 766 767 public int getObjectVersion() { 768 return objectVersion; 769 } 770 771 private Set observers = new HashSet(); 772 773 public void addObserver(Observer observer) { 774 synchronized (observers) { 775 observers.add(observer); 776 } 777 } 778 779 public void removeObserver(Observer observer) { 780 synchronized (observers) { 781 observers.remove(observer); 782 } 783 } 784 785 private boolean equal(String str1, String str2) { 786 return str1==null ? str2==null : str1.equals(str2); 787 } 788 789 /** 790 * @param withHolder True if XML events stream contains start/endElement for 791 * this object. 792 * @return Content handler to populate database object by XML events. 793 */ 794 public ContentHandler getContentHandler(final boolean withHolder) { 795 return new DefaultHandler() { 796 int level = withHolder ? -1 : 0; 797 private ContentHandler currentDelegate; 798 private Column currentColumn; 799 private StringBuffer buffer; 800 801 public void characters(char[] ch, int start, int length) throws SAXException { 802 if (currentDelegate==null) { 803 if (buffer!=null) { 804 buffer.append(ch, start, length); 805 } 806 } else { 807 currentDelegate.characters(ch, start, length); 808 } 809 } 810 811 public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { 812 813 ++level; 814 if (level==1) { 815 if (xmlElement==null) { 816 String[] xmlNames = xmlNames(getClass().getName()); 817 xmlElement=xmlNames[1]; 818 xmlNamespace=xmlNames[0]; 819 } 820 821 if (equal(xmlNamespace, uri)) { 822 currentColumn = (Column) columnXmlMap.get(localName); 823 if (currentColumn!=null) { 824 buffer = new StringBuffer(); 825 return; 826 } 827 } 828 829 currentDelegate = getSubHandler(uri, localName, name); 830 } else if (currentDelegate!=null) { 831 currentDelegate.startElement(uri, localName, name, attributes); 832 } 833 } 834 835 public void endElement(String uri, String localName, String name) throws SAXException { 836 --level; 837 if (level==0) { 838 if (buffer!=null && currentColumn!=null) { 839 currentColumn.load(buffer.toString()); 840 } 841 842 currentColumn=null; 843 buffer=null; 844 currentDelegate=null; 845 } else if (currentDelegate!=null) { 846 currentDelegate.endElement(uri, localName, name); 847 } 848 } 849 850 }; 851 } 852 853 /** 854 * If database object receives XML event which doesn't map to any 855 * column, it invokes this method to obtain sub-handler for an element. 856 * Subclasses can override this method to populate relationships. 857 * This implementation returns default handler. 858 * @param name element local name 859 * @param qName element qualified name 860 * @return Sub-handler. 861 */ 862 protected ContentHandler getSubHandler(String uri, String name, String qName) { 863 return null; 864 } 865 866 // TODO - Auditor, thread-local instance to listen for inserts, deletes and updates. 867 868 }