001: /*
002: * hgcommons 7
003: * Hammurapi Group Common Library
004: * Copyright (C) 2003 Hammurapi Group
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2 of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * URL: http://www.hammurapi.biz/hammurapi-biz/ef/xmenu/hammurapi-group/products/products/hgcommons/index.html
021: * e-Mail: support@hammurapi.biz
022: */
023: package biz.hammurapi.sql;
024:
025: import java.io.Serializable;
026: import java.lang.reflect.Constructor;
027: import java.lang.reflect.InvocationTargetException;
028: import java.sql.PreparedStatement;
029: import java.sql.SQLException;
030: import java.sql.Types;
031: import java.util.AbstractList;
032: import java.util.ArrayList;
033: import java.util.Collection;
034: import java.util.HashMap;
035: import java.util.HashSet;
036: import java.util.Iterator;
037: import java.util.Map;
038: import java.util.Properties;
039: import java.util.Set;
040: import java.util.Map.Entry;
041:
042: import javax.xml.transform.TransformerException;
043:
044: import org.apache.xpath.CachedXPathAPI;
045: import org.apache.xpath.XPathAPI;
046: import org.w3c.dom.Element;
047: import org.w3c.dom.Node;
048: import org.w3c.dom.traversal.NodeIterator;
049:
050: import biz.hammurapi.config.ConfigurationException;
051: import biz.hammurapi.config.Context;
052: import biz.hammurapi.config.ContextConfigurable;
053: import biz.hammurapi.config.DomConfigFactory;
054: import biz.hammurapi.config.DomConfigurable;
055: import biz.hammurapi.convert.CompositeConverter;
056: import biz.hammurapi.sql.columns.Column;
057: import biz.hammurapi.sql.columns.ColumnChangeListener;
058: import biz.hammurapi.util.Attributable;
059: import biz.hammurapi.util.ClassResourceLoader;
060: import biz.hammurapi.util.Observable;
061: import biz.hammurapi.util.Observer;
062: import biz.hammurapi.util.Versioned;
063: import biz.hammurapi.xml.dom.AbstractDomObject;
064: import biz.hammurapi.xml.dom.CompositeDomSerializer;
065: import biz.hammurapi.xml.dom.DomSerializable;
066:
067: /**
068: * SQLC-generated interface implementations implement this method to achieve
069: * differential update functionality - inserting and updating only modified fields.
070: * @author Pavel Vlasov
071: * @version $Revision: 1.11 $
072: */
073: public class DatabaseObject implements DomSerializable,
074: ColumnChangeListener, Cloneable, ContextConfigurable, Context,
075: DomConfigurable, Attributable, Versioned, Observable,
076: IDatabaseObject, Serializable {
077:
078: protected Collection columns = new ArrayList();
079: private Map columnMap = new HashMap();
080: private boolean force;
081: private boolean isDeleted;
082:
083: protected Column getColumn(String name) {
084: return (Column) columnMap.get(name);
085: }
086:
087: /**
088: * Default constructor
089: */
090: public DatabaseObject() {
091: // Default constructor
092: }
093:
094: /**
095: *
096: * @param force Forces columns to be marked as
097: * modified if setter method is invoked even if value being set equals to existing column
098: * value. Useful during inserts with non-nullable columns which map to primitive
099: * types and as such have default values.
100: */
101: public DatabaseObject(boolean force) {
102: this .force = force;
103: }
104:
105: protected void addColumn(Column column) {
106: column.setForce(force);
107: columns.add(column);
108: columnMap.put(column.getName(), column);
109: column.setListener(this );
110: }
111:
112: /* (non-Javadoc)
113: * @see biz.hammurapi.sql.IDatabaseObject#update(biz.hammurapi.sql.SQLProcessor, java.lang.String)
114: */
115: public int update(SQLProcessor processor, String tableName)
116: throws SQLException {
117: StringBuffer sb = new StringBuffer("UPDATE ");
118: sb.append(tableName);
119: sb.append(" SET ");
120: boolean hasColumns = false;
121: Iterator it = columns.iterator();
122: while (it.hasNext()) {
123: Column column = (Column) it.next();
124: String colName = (column).listName();
125: if (colName != null) {
126: if (hasColumns) {
127: sb.append(", ");
128: }
129: sb.append(colName);
130: sb.append("=?");
131: hasColumns = true;
132:
133: }
134: }
135:
136: if (!hasColumns) {
137: return 0;
138: }
139:
140: sb.append(" WHERE ");
141:
142: boolean hasPkColumns = false;
143: it = columns.iterator();
144: while (it.hasNext()) {
145: Column column = (Column) it.next();
146: if (column.isPrimaryKey()) {
147: String colName = (column).getName();
148: if (hasPkColumns) {
149: sb.append(" AND ");
150: }
151: sb.append(colName);
152: sb.append("=?");
153: hasPkColumns = true;
154: }
155: }
156:
157: int ret = processor.processUpdate(sb.toString(),
158: new Parameterizer() {
159: public void parameterize(PreparedStatement ps)
160: throws SQLException {
161: int idx = 1;
162: Iterator it = columns.iterator();
163: while (it.hasNext()) {
164: Column column = (Column) it.next();
165: idx = column.parameterize(ps, idx, false);
166: }
167:
168: it = columns.iterator();
169: while (it.hasNext()) {
170: Column column = (Column) it.next();
171: if (column.isPrimaryKey()) {
172: column.parameterizeOriginal(ps, idx++);
173: }
174: }
175: }
176: });
177:
178: it = columns.iterator();
179: while (it.hasNext()) {
180: ((Column) it.next()).clearModified();
181: }
182:
183: originalVersion = objectVersion;
184: isModified = false;
185:
186: storeRelationships(processor);
187:
188: it = relationships.iterator();
189: while (it.hasNext()) {
190: RelationshipEntry re = (RelationshipEntry) it.next();
191: if (re.name != null) {
192: Iterator iit = re.items.iterator();
193: while (iit.hasNext()) {
194: IDatabaseObject subItem = (IDatabaseObject) iit
195: .next();
196: if (subItem.isModified()) {
197: re.items.update(processor, subItem);
198: subItem.update(processor, null);
199: }
200: }
201: }
202: }
203: return ret;
204: }
205:
206: /* (non-Javadoc)
207: * @see biz.hammurapi.sql.IDatabaseObject#delete(biz.hammurapi.sql.SQLProcessor, java.lang.String)
208: */
209: public int delete(SQLProcessor processor, String tableName)
210: throws SQLException {
211: storeRelationships(processor);
212:
213: StringBuffer sb = new StringBuffer("DELETE FROM ");
214: sb.append(tableName);
215: sb.append(" WHERE ");
216:
217: boolean hasPkColumns = false;
218: Iterator it = columns.iterator();
219: while (it.hasNext()) {
220: Column column = (Column) it.next();
221: if (column.isPrimaryKey()) {
222: String colName = (column).getName();
223: if (hasPkColumns) {
224: sb.append(" AND ");
225: }
226: sb.append(colName);
227: sb.append("=?");
228: hasPkColumns = true;
229: }
230: }
231:
232: int ret = processor.processUpdate(sb.toString(),
233: new Parameterizer() {
234: public void parameterize(PreparedStatement ps)
235: throws SQLException {
236: int idx = 1;
237: Iterator it = columns.iterator();
238: while (it.hasNext()) {
239: Column column = (Column) it.next();
240: if (!column.isPrimaryKey()) {
241: idx = column.parameterize(ps, idx,
242: false);
243: }
244: }
245:
246: it = columns.iterator();
247: while (it.hasNext()) {
248: Column column = (Column) it.next();
249: if (column.isPrimaryKey()) {
250: idx = column
251: .parameterize(ps, idx, true);
252: }
253: }
254: }
255: });
256:
257: it = columns.iterator();
258: while (it.hasNext()) {
259: ((Column) it.next()).clearModified();
260: }
261:
262: originalVersion = objectVersion;
263: isDeleted = true;
264: return ret;
265: }
266:
267: /* (non-Javadoc)
268: * @see biz.hammurapi.sql.IDatabaseObject#insert(biz.hammurapi.sql.SQLProcessor, java.lang.String)
269: */
270: public int insert(SQLProcessor processor, String tableName)
271: throws SQLException {
272: StringBuffer sb = new StringBuffer("INSERT INTO ");
273: sb.append(tableName);
274: sb.append(" (");
275: int columnsCounter = 0;
276: Iterator it = columns.iterator();
277: while (it.hasNext()) {
278: String colName = ((Column) it.next()).listName();
279: if (colName != null) {
280: if (columnsCounter > 0) {
281: sb.append(", ");
282: }
283: sb.append(colName);
284: columnsCounter++;
285:
286: }
287: }
288:
289: if (columnsCounter == 0) {
290: return 0;
291: }
292:
293: sb.append(") VALUES (");
294: for (int i = 0; i < columnsCounter; i++) {
295: if (i > 0) {
296: sb.append(", ");
297: }
298: sb.append("?");
299: }
300: sb.append(")");
301:
302: int ret = processor.processUpdate(sb.toString(),
303: new Parameterizer() {
304: public void parameterize(PreparedStatement ps)
305: throws SQLException {
306: Iterator it = columns.iterator();
307: for (int i = 1; it.hasNext(); i = ((Column) it
308: .next()).parameterize(ps, i, false))
309: ;
310: }
311: });
312:
313: it = columns.iterator();
314: while (it.hasNext()) {
315: ((Column) it.next()).clearModified();
316: }
317:
318: originalVersion = objectVersion;
319: isModified = false;
320:
321: storeRelationships(processor);
322: return ret;
323: }
324:
325: /* (non-Javadoc)
326: * @see biz.hammurapi.sql.IDatabaseObject#fromDom(org.w3c.dom.Element)
327: */
328: public void fromDom(Element holder) throws ConfigurationException {
329: fromDom(holder, getNameMap(getClass()));
330: }
331:
332: private void loadAttributes(Element holder)
333: throws ConfigurationException {
334: DomConfigFactory dcf = new DomConfigFactory();
335: attributes.clear();
336: try {
337: Node attributesNode = XPathAPI.selectSingleNode(holder,
338: "object-attributes");
339: if (attributesNode != null) {
340: attributes.putAll((Map) dcf.create(attributesNode));
341: }
342: } catch (TransformerException e) {
343: throw new ConfigurationException(
344: "Cannot load database object attributes: " + e, e);
345: }
346:
347: }
348:
349: /* (non-Javadoc)
350: * @see biz.hammurapi.sql.IDatabaseObject#fromDom(org.w3c.dom.Element, java.util.Properties)
351: */
352: public void fromDom(Element holder, Properties nameMap)
353: throws ConfigurationException {
354: CachedXPathAPI cxpa = new CachedXPathAPI();
355: Iterator it = columns.iterator();
356: while (it.hasNext()) {
357: Column column = (Column) it.next();
358: String nodeName = nameMap.getProperty(column.getName(),
359: column.getName()).trim();
360:
361: if (nodeName.length() == 0) {
362: // Zero mapping
363: continue;
364: } else if (nodeName.startsWith("@")) {
365: // Attribute mapping
366: if (holder.hasAttribute(nodeName.substring(1))) {
367: column.load(holder.getAttribute(nodeName
368: .substring(1)));
369: }
370: } else if (nodeName.equals(".")) {
371: // Text content mapping
372: column.load(AbstractDomObject.getElementText(holder));
373: } else if (nodeName.startsWith("!")) {
374: // Simple node mapping
375: try {
376: Node cNode = cxpa.selectSingleNode(holder, nodeName
377: .substring(1));
378: if (cNode != null) {
379: column.load(AbstractDomObject
380: .getElementText(cNode));
381: }
382: } catch (TransformerException e) {
383: throw new ConfigurationException(
384: "Cannot load column " + column.getName());
385: }
386: } else {
387: // Regular node mapping
388: try {
389: Node cNode = cxpa
390: .selectSingleNode(holder, nodeName);
391: if (cNode != null) {
392: column.configure(cNode, null);
393: }
394: } catch (TransformerException e) {
395: throw new ConfigurationException(
396: "Cannot load column " + column.getName());
397: }
398: }
399: }
400:
401: it = relationships.iterator();
402: while (it.hasNext()) {
403: RelationshipEntry re = (RelationshipEntry) it.next();
404: if (re.name != null) {
405: String domName = re.domName == null ? re.name
406: : re.domName;
407: try {
408: Constructor constructor = re.items.relationship
409: .getItemType().getConstructor(
410: new Class[] { Element.class,
411: boolean.class });
412: Element rel = ".".equals(domName) ? holder
413: : (Element) cxpa.selectSingleNode(holder,
414: domName);
415: String itemName = re.itemName == null ? "element"
416: : re.itemName;
417: Element itemElement;
418: NodeIterator nit = cxpa.selectNodeIterator(rel,
419: itemName);
420: while ((itemElement = (Element) nit.nextNode()) != null) {
421: re.items.add(constructor
422: .newInstance(new Object[] {
423: itemElement,
424: force ? Boolean.TRUE
425: : Boolean.FALSE }));
426: }
427: } catch (TransformerException e) {
428: throw new ConfigurationException(
429: "Cannot load relationship " + re.name, e);
430: } catch (InstantiationException e) {
431: throw new ConfigurationException(
432: "Cannot load relationship " + re.name, e);
433: } catch (IllegalAccessException e) {
434: throw new ConfigurationException(
435: "Cannot load relationship " + re.name, e);
436: } catch (InvocationTargetException e) {
437: throw new ConfigurationException(
438: "Cannot load relationship " + re.name, e);
439: } catch (SecurityException e) {
440: throw new ConfigurationException(
441: "Cannot load relationship " + re.name, e);
442: } catch (NoSuchMethodException e) {
443: throw new ConfigurationException(
444: "Cannot load relationship " + re.name, e);
445: }
446: }
447: }
448: loadAttributes(holder);
449: }
450:
451: public void toDom(Element holder) {
452: toDom(holder, getNameMap(getClass()), false);
453: }
454:
455: /* (non-Javadoc)
456: * @see biz.hammurapi.sql.IDatabaseObject#toDom(org.w3c.dom.Element, java.util.Properties, boolean)
457: */
458: public void toDom(Element holder, Properties nameMap,
459: boolean originals) {
460: if (nameMap == null) {
461: nameMap = new Properties();
462: }
463:
464: String cna = nameMap.getProperty("@type", "type").trim();
465: if (!"".equals(cna)) {
466: holder.setAttribute(cna, getClass().getName());
467: }
468:
469: Iterator it = columns.iterator();
470: while (it.hasNext()) {
471: Column column = (Column) it.next();
472: column.toDom(holder, nameMap.getProperty(column.getName(),
473: column.getName()).trim(), originals);
474: }
475:
476: it = relationships.iterator();
477: while (it.hasNext()) {
478: RelationshipEntry re = (RelationshipEntry) it.next();
479: if (re.name != null) {
480: String domName = re.domName == null ? re.name
481: : re.domName;
482: Element rel = ".".equals(domName) ? holder : holder
483: .getOwnerDocument().createElement(domName);
484: if (rel != holder) {
485: holder.appendChild(rel);
486: }
487:
488: Iterator iit = re.items.iterator();
489: while (iit.hasNext()) {
490: String itemName = re.itemName == null ? "element"
491: : re.itemName;
492: Element ie = holder.getOwnerDocument()
493: .createElement(itemName);
494: rel.appendChild(ie);
495: ((DatabaseObject) iit.next()).toDom(ie);
496: }
497: }
498: }
499:
500: if (!attributes.isEmpty()) {
501: CompositeDomSerializer.getThreadInstance()
502: .toDomSerializable(attributes).toDom(
503: AbstractDomObject.addElement(holder,
504: "object-attributes"));
505: }
506: }
507:
508: public String toString() {
509: StringBuffer ret = new StringBuffer(getClass().getName());
510: ret.append("[");
511: Iterator it = columns.iterator();
512: while (it.hasNext()) {
513: ret.append(it.next());
514: if (it.hasNext()) {
515: ret.append(", ");
516: }
517: }
518:
519: ret.append("]");
520: return ret.toString();
521: }
522:
523: /**
524: * Sets modified flag to true and increments version number.
525: * Also broadcasts the change to observers. Changed column is
526: * passed as second argument of update() method.
527: * Override this method in subclasses to react on
528: * change events, but don't forget to invoke
529: * super.onChange().
530: * @param column Changed column
531: */
532: public void onChange(Column column) {
533: isModified = true;
534:
535: ++objectVersion;
536:
537: if (column.isPrimaryKey()) {
538: isDeleted = false;
539: }
540:
541: Iterator it = relationships.iterator();
542: while (it.hasNext()) {
543: Relationship relationship = ((RelationshipEntry) it.next()).items.relationship;
544: if (relationship instanceof ColumnChangeListener) {
545: ((ColumnChangeListener) relationship).onChange(column);
546: }
547: }
548:
549: synchronized (observers) {
550: it = observers.iterator();
551: while (it.hasNext()) {
552: ((Observer) it.next()).update(this , column);
553: }
554: }
555:
556: }
557:
558: /* (non-Javadoc)
559: * @see biz.hammurapi.sql.IDatabaseObject#setOriginal()
560: */
561: public void setOriginal() {
562: objectVersion = originalVersion;
563: Iterator it = columns.iterator();
564: while (it.hasNext()) {
565: ((Column) it.next()).setOriginal();
566: }
567: }
568:
569: private boolean isModified = false;
570:
571: /* (non-Javadoc)
572: * @see biz.hammurapi.sql.IDatabaseObject#isModified()
573: */
574: public boolean isModified() {
575: return isModified;
576: }
577:
578: /* (non-Javadoc)
579: * @see biz.hammurapi.sql.IDatabaseObject#isDeleted()
580: */
581: public boolean isDeleted() {
582: return isDeleted;
583: }
584:
585: /**
586: * Two objects are considered equal and all their fields are equal.
587: * @param otherBean Other object
588: * @return true if object classes are equal and all member column values are
589: * equal.
590: */
591: public boolean equals(Object otherBean) {
592: if (otherBean == null) {
593: return false;
594: } else if (getClass().equals(otherBean.getClass())) {
595: Collection myColumns = new ArrayList(columns);
596: Collection otherColumns = new ArrayList(
597: ((DatabaseObject) otherBean).columns);
598: Iterator mcit = myColumns.iterator();
599: Z: while (mcit.hasNext()) {
600: Column mc = (Column) mcit.next();
601: Iterator ocit = otherColumns.iterator();
602: while (ocit.hasNext()) {
603: Column oc = (Column) ocit.next();
604: if (mc.getName().equals(oc.getName())) {
605: if (mc.equals(oc)) {
606: ocit.remove();
607: mcit.remove();
608: continue Z;
609: }
610:
611: return false;
612: }
613: }
614: }
615:
616: return myColumns.isEmpty() && otherColumns.isEmpty();
617: } else {
618: return false;
619: }
620: }
621:
622: public int hashCode() {
623: int ret = 0;
624: Iterator cit = columns.iterator();
625: while (cit.hasNext()) {
626: ret ^= cit.next().hashCode();
627: }
628: return ret;
629: }
630:
631: /**
632: * Clones object, clears columns collection, clears isDeleted and isModified flags.
633: * Subclasses shall add cloned columns.
634: */
635: public Object clone() throws CloneNotSupportedException {
636: DatabaseObject ret = (DatabaseObject) super .clone();
637: ret.columns.clear();
638: ret.isDeleted = false;
639: ret.isModified = false;
640: return ret;
641: }
642:
643: /* (non-Javadoc)
644: * @see biz.hammurapi.sql.IDatabaseObject#clear()
645: */
646: public void clear() {
647: Iterator it = columns.iterator();
648: while (it.hasNext()) {
649: ((Column) it.next()).clear();
650: }
651: isModified = false;
652: isDeleted = false;
653: }
654:
655: public void configure(Context context, CompositeConverter converter)
656: throws ConfigurationException {
657: Iterator it = columns.iterator();
658: while (it.hasNext()) {
659: ((Column) it.next()).configure(context, converter);
660: }
661: }
662:
663: public Object get(String name) {
664: Column col = (Column) columnMap.get(name);
665: return col == null ? null : col.getObjectValue(false);
666: }
667:
668: public void configure(Node configNode, Context context)
669: throws ConfigurationException {
670: fromDom((Element) configNode);
671: }
672:
673: private static Map nnMap = new HashMap();
674:
675: private static Properties getNameMap(Class clazz) {
676: synchronized (nnMap) {
677: Properties ret = (Properties) nnMap.get(clazz.getName());
678: if (ret == null) {
679: ret = new ClassResourceLoader(clazz).getProperties(
680: null, "namemap");
681: nnMap.put(clazz.getName(), ret);
682: }
683: return ret;
684: }
685: }
686:
687: private class RelationshipEntry {
688: String name;
689: String itemName;
690: String domName;
691: RelationshipListImpl items;
692: }
693:
694: private class RelationshipListImpl extends AbstractList implements
695: RelationshipList {
696: private ArrayList master = new ArrayList();
697: private Relationship relationship;
698: private boolean alreadyLoaded;
699:
700: private RelationshipListImpl(Relationship relationship) {
701: this .relationship = relationship;
702: relationship.setMaster(master);
703: }
704:
705: private void update(SQLProcessor processor,
706: IDatabaseObject subItem) throws SQLException {
707: relationship.update(processor, subItem);
708: }
709:
710: private void load(SQLProcessor processor) throws SQLException {
711: if (!alreadyLoaded || relationship.isModified()) {
712: master.clear();
713: relationship.load(processor, master);
714: alreadyLoaded = true;
715: }
716: }
717:
718: private void store(SQLProcessor processor) throws SQLException {
719: relationship.store(processor);
720: }
721:
722: public Object get(int index) {
723: return master.get(index);
724: }
725:
726: public int size() {
727: return master.size();
728: }
729:
730: public boolean add(Object o) {
731: relationship.add((DatabaseObject) o);
732: return master.add(o);
733: }
734:
735: public boolean remove(Object o) {
736: relationship.remove((IDatabaseObject) o);
737: return master.remove(o);
738: }
739:
740: public boolean isLazy() {
741: return relationship.isLazy();
742: }
743:
744: public boolean isModified() {
745: return relationship.isModified();
746: }
747:
748: public IDatabaseObject add() {
749: try {
750: IDatabaseObject item = (IDatabaseObject) relationship
751: .getItemType().newInstance();
752: add(item);
753: return item;
754: } catch (InstantiationException e) {
755: throw new RuntimeException("Cannot instantiate "
756: + relationship.getItemType());
757: } catch (IllegalAccessException e) {
758: throw new RuntimeException("Cannot instantiate "
759: + relationship.getItemType());
760: }
761: }
762: }
763:
764: private Collection relationships = new ArrayList();
765: private Map relationshipMap = new HashMap();
766:
767: protected RelationshipList getRelationship(String name) {
768: RelationshipEntry entry = (RelationshipEntry) relationshipMap
769: .get(name);
770: if (entry.items.isLazy() || entry.items.isModified()) {
771: try {
772: entry.items.load(((Lazy) this ).getProcessor());
773: } catch (SQLException e) {
774: throw new SQLRuntimeException(e);
775: }
776: }
777: return entry.items;
778: }
779:
780: /**
781: * @param name Name for composite relationships to render in XML, null for shared relationships.
782: * @param itemName name of item element in XML.
783: * @param itemClass item class. This class shall have constructor from Element,boolean in order to load from
784: * XML docs.
785: * @param relationship
786: */
787: protected RelationshipList addRelationship(String name,
788: String itemName, Relationship relationship) {
789: if (relationship.isLazy() && !(this instanceof Lazy)) {
790: throw new IllegalArgumentException(
791: "Cannot add lazy relationship to class which doesn't implement Lazy interface");
792: }
793:
794: if (!DatabaseObject.class.isAssignableFrom(relationship
795: .getItemType())) {
796: throw new IllegalArgumentException(
797: "Relationship can be established only between DatabaseObject subclasses");
798: }
799: RelationshipEntry entry = new RelationshipEntry();
800: relationships.add(entry);
801: entry.name = name;
802: entry.itemName = itemName;
803: entry.items = new RelationshipListImpl(relationship);
804: Properties nameMap = getNameMap(getClass());
805: String cName = name == null ? null : nameMap.getProperty(name);
806: entry.domName = cName == null ? null : cName.trim();
807: relationshipMap.put(name, entry);
808: return entry.items;
809: }
810:
811: /**
812: * Use this method to eagerly load relationships in constructors.
813: * @param processor
814: * @throws SQLException
815: */
816: protected void loadRelationships(SQLProcessor processor)
817: throws SQLException {
818: Iterator it = relationships.iterator();
819: while (it.hasNext()) {
820: RelationshipEntry relationshipEntry = (RelationshipEntry) it
821: .next();
822: if (!relationshipEntry.items.isLazy()) {
823: (relationshipEntry).items.load(processor);
824: }
825: }
826: }
827:
828: /**
829: * Use this method to eagerly load relationships in constructors.
830: * @param processor
831: * @throws SQLException
832: */
833: private void storeRelationships(SQLProcessor processor)
834: throws SQLException {
835: Iterator it = relationships.iterator();
836: while (it.hasNext()) {
837: ((RelationshipEntry) it.next()).items.store(processor);
838: }
839: }
840:
841: /* (non-Javadoc)
842: * @see biz.hammurapi.sql.IDatabaseObject#copy(biz.hammurapi.sql.DatabaseObject)
843: */
844: public void copy(DatabaseObject source) {
845: Iterator it = columns.iterator();
846: while (it.hasNext()) {
847: Column targetColumn = (Column) it.next();
848: Column sourceColumn = (Column) source.columnMap
849: .get(targetColumn.getName());
850: if (targetColumn.getClass().isInstance(sourceColumn)) { // Copy values only from compatible columns
851: targetColumn.set(sourceColumn);
852: }
853: }
854: }
855:
856: private Map attributes = new HashMap();
857:
858: public void setAttribute(Object key, Object value) {
859: attributes.put(key, value);
860: }
861:
862: public Object getAttribute(Object key) {
863: return attributes.get(key);
864: }
865:
866: public Object removeAttribute(Object key) {
867: return attributes.remove(key);
868: }
869:
870: /* (non-Javadoc)
871: * @see biz.hammurapi.sql.IDatabaseObject#setColumnAttribute(java.lang.String, java.lang.Object, java.lang.Object)
872: */
873: public void setColumnAttribute(String columnName, Object key,
874: Object value) {
875: Column column = (Column) columnMap.get(columnName);
876: if (column == null) {
877: throw new IllegalArgumentException("Column not found: "
878: + columnName);
879: }
880: column.setAttribute(key, value);
881: }
882:
883: /* (non-Javadoc)
884: * @see biz.hammurapi.sql.IDatabaseObject#getColumnAttribute(java.lang.String, java.lang.Object)
885: */
886: public Object getColumnAttribute(String columnName, Object key) {
887: Column column = (Column) columnMap.get(columnName);
888: if (column == null) {
889: throw new IllegalArgumentException("Column not found: "
890: + columnName);
891: }
892: return column.getAttribute(key);
893: }
894:
895: /* (non-Javadoc)
896: * @see biz.hammurapi.sql.IDatabaseObject#removeColumnAttribute(java.lang.String, java.lang.Object)
897: */
898: public Object removeColumnAttribute(String columnName, Object key) {
899: Column column = (Column) columnMap.get(columnName);
900: if (column == null) {
901: throw new IllegalArgumentException("Column not found: "
902: + columnName);
903: }
904: return column.removeAttribute(key);
905: }
906:
907: private static Map sqlTypes = new HashMap();
908:
909: /**
910: * Allows to override generated column types with <class name>.sqltypes resource.
911: * Subclasses shall use this method when dealing with Object columns.
912: * @param columnName
913: * @param generatedType
914: * @return
915: */
916: protected int getSqlType(String columnName, int generatedType) {
917: Map typeMap;
918: synchronized (sqlTypes) {
919: String className = getClass().getName();
920: if (sqlTypes.containsKey(className)) {
921: typeMap = (Map) sqlTypes.get(className);
922: } else {
923: Properties literalMap = new ClassResourceLoader(
924: getClass()).getProperties(null, "sqltypes");
925: typeMap = new HashMap();
926: sqlTypes.put(className, typeMap);
927: Iterator it = literalMap.entrySet().iterator();
928: while (it.hasNext()) {
929: Entry entry = (Entry) it.next();
930: try {
931: Integer type = (Integer) Types.class
932: .getDeclaredField(
933: (String) entry.getValue()).get(
934: null);
935: typeMap.put(entry.getKey(), type);
936: } catch (IllegalArgumentException e) {
937: System.err.println("Invalid SQL Type "
938: + entry.getValue()
939: + ", ignored. Cause: " + e);
940: e.printStackTrace();
941: } catch (SecurityException e) {
942: System.err.println("Invalid SQL Type "
943: + entry.getValue()
944: + ", ignored. Cause: " + e);
945: e.printStackTrace();
946: } catch (IllegalAccessException e) {
947: System.err.println("Invalid SQL Type "
948: + entry.getValue()
949: + ", ignored. Cause: " + e);
950: e.printStackTrace();
951: } catch (NoSuchFieldException e) {
952: System.err.println("Invalid SQL Type "
953: + entry.getValue()
954: + ", ignored. Cause: " + e);
955: e.printStackTrace();
956: }
957: }
958: }
959: }
960:
961: Integer st = (Integer) typeMap.get(columnName);
962: return st == null ? generatedType : st.intValue();
963: }
964:
965: /**
966: * Subclasses can choose to read object version from the database.
967: */
968: protected int objectVersion;
969: protected int originalVersion;
970:
971: public int getObjectVersion() {
972: return objectVersion;
973: }
974:
975: private Set observers = new HashSet();
976:
977: public void addObserver(Observer observer) {
978: synchronized (observers) {
979: observers.add(observer);
980: }
981: }
982:
983: public void removeObserver(Observer observer) {
984: synchronized (observers) {
985: observers.remove(observer);
986: }
987: }
988:
989: }
|