001: //$Id: PersistentBag.java 10739 2006-11-06 22:00:41Z steve.ebersole@jboss.com $
002: package org.hibernate.collection;
003:
004: import java.io.Serializable;
005: import java.sql.ResultSet;
006: import java.sql.SQLException;
007: import java.util.ArrayList;
008: import java.util.Collection;
009: import java.util.Iterator;
010: import java.util.List;
011: import java.util.ListIterator;
012:
013: import org.hibernate.EntityMode;
014: import org.hibernate.HibernateException;
015: import org.hibernate.engine.SessionImplementor;
016: import org.hibernate.loader.CollectionAliases;
017: import org.hibernate.persister.collection.CollectionPersister;
018: import org.hibernate.type.Type;
019:
020: /**
021: * An unordered, unkeyed collection that can contain the same element
022: * multiple times. The Java collections API, curiously, has no <tt>Bag</tt>.
023: * Most developers seem to use <tt>List</tt>s to represent bag semantics,
024: * so Hibernate follows this practice.
025: *
026: * @author Gavin King
027: */
028: public class PersistentBag extends AbstractPersistentCollection
029: implements List {
030:
031: protected List bag;
032:
033: public PersistentBag(SessionImplementor session) {
034: super (session);
035: }
036:
037: public PersistentBag(SessionImplementor session, Collection coll) {
038: super (session);
039: if (coll instanceof List) {
040: bag = (List) coll;
041: } else {
042: bag = new ArrayList();
043: Iterator iter = coll.iterator();
044: while (iter.hasNext()) {
045: bag.add(iter.next());
046: }
047: }
048: setInitialized();
049: setDirectlyAccessible(true);
050: }
051:
052: public PersistentBag() {
053: } //needed for SOAP libraries, etc
054:
055: public boolean isWrapper(Object collection) {
056: return bag == collection;
057: }
058:
059: public boolean empty() {
060: return bag.isEmpty();
061: }
062:
063: public Iterator entries(CollectionPersister persister) {
064: return bag.iterator();
065: }
066:
067: public Object readFrom(ResultSet rs, CollectionPersister persister,
068: CollectionAliases descriptor, Object owner)
069: throws HibernateException, SQLException {
070: // note that if we load this collection from a cartesian product
071: // the multiplicity would be broken ... so use an idbag instead
072: Object element = persister.readElement(rs, owner, descriptor
073: .getSuffixedElementAliases(), getSession());
074: if (element != null)
075: bag.add(element);
076: return element;
077: }
078:
079: public void beforeInitialize(CollectionPersister persister,
080: int anticipatedSize) {
081: this .bag = (List) persister.getCollectionType().instantiate(
082: anticipatedSize);
083: }
084:
085: public boolean equalsSnapshot(CollectionPersister persister)
086: throws HibernateException {
087: Type elementType = persister.getElementType();
088: EntityMode entityMode = getSession().getEntityMode();
089: List sn = (List) getSnapshot();
090: if (sn.size() != bag.size())
091: return false;
092: Iterator iter = bag.iterator();
093: while (iter.hasNext()) {
094: Object elt = iter.next();
095: final boolean unequal = countOccurrences(elt, bag,
096: elementType, entityMode) != countOccurrences(elt,
097: sn, elementType, entityMode);
098: if (unequal)
099: return false;
100: }
101: return true;
102: }
103:
104: public boolean isSnapshotEmpty(Serializable snapshot) {
105: return ((Collection) snapshot).isEmpty();
106: }
107:
108: private int countOccurrences(Object element, List list,
109: Type elementType, EntityMode entityMode)
110: throws HibernateException {
111: Iterator iter = list.iterator();
112: int result = 0;
113: while (iter.hasNext()) {
114: if (elementType.isSame(element, iter.next(), entityMode))
115: result++;
116: }
117: return result;
118: }
119:
120: public Serializable getSnapshot(CollectionPersister persister)
121: throws HibernateException {
122: EntityMode entityMode = getSession().getEntityMode();
123: ArrayList clonedList = new ArrayList(bag.size());
124: Iterator iter = bag.iterator();
125: while (iter.hasNext()) {
126: clonedList.add(persister.getElementType().deepCopy(
127: iter.next(), entityMode, persister.getFactory()));
128: }
129: return clonedList;
130: }
131:
132: public Collection getOrphans(Serializable snapshot,
133: String entityName) throws HibernateException {
134: List sn = (List) snapshot;
135: return getOrphans(sn, bag, entityName, getSession());
136: }
137:
138: public Serializable disassemble(CollectionPersister persister)
139: throws HibernateException {
140:
141: int length = bag.size();
142: Serializable[] result = new Serializable[length];
143: for (int i = 0; i < length; i++) {
144: result[i] = persister.getElementType().disassemble(
145: bag.get(i), getSession(), null);
146: }
147: return result;
148: }
149:
150: public void initializeFromCache(CollectionPersister persister,
151: Serializable disassembled, Object owner)
152: throws HibernateException {
153: Serializable[] array = (Serializable[]) disassembled;
154: int size = array.length;
155: beforeInitialize(persister, size);
156: for (int i = 0; i < size; i++) {
157: Object element = persister.getElementType().assemble(
158: array[i], getSession(), owner);
159: if (element != null) {
160: bag.add(element);
161: }
162: }
163: }
164:
165: public boolean needsRecreate(CollectionPersister persister) {
166: return !persister.isOneToMany();
167: }
168:
169: // For a one-to-many, a <bag> is not really a bag;
170: // it is *really* a set, since it can't contain the
171: // same element twice. It could be considered a bug
172: // in the mapping dtd that <bag> allows <one-to-many>.
173:
174: // Anyway, here we implement <set> semantics for a
175: // <one-to-many> <bag>!
176:
177: public Iterator getDeletes(CollectionPersister persister,
178: boolean indexIsFormula) throws HibernateException {
179: //if ( !persister.isOneToMany() ) throw new AssertionFailure("Not implemented for Bags");
180: Type elementType = persister.getElementType();
181: EntityMode entityMode = getSession().getEntityMode();
182: ArrayList deletes = new ArrayList();
183: List sn = (List) getSnapshot();
184: Iterator olditer = sn.iterator();
185: int i = 0;
186: while (olditer.hasNext()) {
187: Object old = olditer.next();
188: Iterator newiter = bag.iterator();
189: boolean found = false;
190: if (bag.size() > i
191: && elementType
192: .isSame(old, bag.get(i++), entityMode)) {
193: //a shortcut if its location didn't change!
194: found = true;
195: } else {
196: //search for it
197: //note that this code is incorrect for other than one-to-many
198: while (newiter.hasNext()) {
199: if (elementType.isSame(old, newiter.next(),
200: entityMode)) {
201: found = true;
202: break;
203: }
204: }
205: }
206: if (!found)
207: deletes.add(old);
208: }
209: return deletes.iterator();
210: }
211:
212: public boolean needsInserting(Object entry, int i, Type elemType)
213: throws HibernateException {
214: //if ( !persister.isOneToMany() ) throw new AssertionFailure("Not implemented for Bags");
215: List sn = (List) getSnapshot();
216: final EntityMode entityMode = getSession().getEntityMode();
217: if (sn.size() > i
218: && elemType.isSame(sn.get(i), entry, entityMode)) {
219: //a shortcut if its location didn't change!
220: return false;
221: } else {
222: //search for it
223: //note that this code is incorrect for other than one-to-many
224: Iterator olditer = sn.iterator();
225: while (olditer.hasNext()) {
226: Object old = olditer.next();
227: if (elemType.isSame(old, entry, entityMode))
228: return false;
229: }
230: return true;
231: }
232: }
233:
234: public boolean isRowUpdatePossible() {
235: return false;
236: }
237:
238: public boolean needsUpdating(Object entry, int i, Type elemType) {
239: //if ( !persister.isOneToMany() ) throw new AssertionFailure("Not implemented for Bags");
240: return false;
241: }
242:
243: /**
244: * @see java.util.Collection#size()
245: */
246: public int size() {
247: return readSize() ? getCachedSize() : bag.size();
248: }
249:
250: /**
251: * @see java.util.Collection#isEmpty()
252: */
253: public boolean isEmpty() {
254: return readSize() ? getCachedSize() == 0 : bag.isEmpty();
255: }
256:
257: /**
258: * @see java.util.Collection#contains(Object)
259: */
260: public boolean contains(Object object) {
261: Boolean exists = readElementExistence(object);
262: return exists == null ? bag.contains(object) : exists
263: .booleanValue();
264: }
265:
266: /**
267: * @see java.util.Collection#iterator()
268: */
269: public Iterator iterator() {
270: read();
271: return new IteratorProxy(bag.iterator());
272: }
273:
274: /**
275: * @see java.util.Collection#toArray()
276: */
277: public Object[] toArray() {
278: read();
279: return bag.toArray();
280: }
281:
282: /**
283: * @see java.util.Collection#toArray(Object[])
284: */
285: public Object[] toArray(Object[] a) {
286: read();
287: return bag.toArray(a);
288: }
289:
290: /**
291: * @see java.util.Collection#add(Object)
292: */
293: public boolean add(Object object) {
294: if (!isOperationQueueEnabled()) {
295: write();
296: return bag.add(object);
297: } else {
298: queueOperation(new SimpleAdd(object));
299: return true;
300: }
301: }
302:
303: /**
304: * @see java.util.Collection#remove(Object)
305: */
306: public boolean remove(Object o) {
307: initialize(true);
308: if (bag.remove(o)) {
309: dirty();
310: return true;
311: } else {
312: return false;
313: }
314: }
315:
316: /**
317: * @see java.util.Collection#containsAll(Collection)
318: */
319: public boolean containsAll(Collection c) {
320: read();
321: return bag.containsAll(c);
322: }
323:
324: /**
325: * @see java.util.Collection#addAll(Collection)
326: */
327: public boolean addAll(Collection values) {
328: if (values.size() == 0)
329: return false;
330: if (!isOperationQueueEnabled()) {
331: write();
332: return bag.addAll(values);
333: } else {
334: Iterator iter = values.iterator();
335: while (iter.hasNext()) {
336: queueOperation(new SimpleAdd(iter.next()));
337: }
338: return values.size() > 0;
339: }
340: }
341:
342: /**
343: * @see java.util.Collection#removeAll(Collection)
344: */
345: public boolean removeAll(Collection c) {
346: if (c.size() > 0) {
347: initialize(true);
348: if (bag.removeAll(c)) {
349: dirty();
350: return true;
351: } else {
352: return false;
353: }
354: } else {
355: return false;
356: }
357: }
358:
359: /**
360: * @see java.util.Collection#retainAll(Collection)
361: */
362: public boolean retainAll(Collection c) {
363: initialize(true);
364: if (bag.retainAll(c)) {
365: dirty();
366: return true;
367: } else {
368: return false;
369: }
370: }
371:
372: /**
373: * @see java.util.Collection#clear()
374: */
375: public void clear() {
376: if (isClearQueueEnabled()) {
377: queueOperation(new Clear());
378: } else {
379: initialize(true);
380: if (!bag.isEmpty()) {
381: bag.clear();
382: dirty();
383: }
384: }
385: }
386:
387: public Object getIndex(Object entry, int i,
388: CollectionPersister persister) {
389: throw new UnsupportedOperationException(
390: "Bags don't have indexes");
391: }
392:
393: public Object getElement(Object entry) {
394: return entry;
395: }
396:
397: public Object getSnapshotElement(Object entry, int i) {
398: List sn = (List) getSnapshot();
399: return sn.get(i);
400: }
401:
402: public int occurrences(Object o) {
403: read();
404: Iterator iter = bag.iterator();
405: int result = 0;
406: while (iter.hasNext()) {
407: if (o.equals(iter.next()))
408: result++;
409: }
410: return result;
411: }
412:
413: // List OPERATIONS:
414:
415: /**
416: * @see java.util.List#add(int, Object)
417: */
418: public void add(int i, Object o) {
419: write();
420: bag.add(i, o);
421: }
422:
423: /**
424: * @see java.util.List#addAll(int, Collection)
425: */
426: public boolean addAll(int i, Collection c) {
427: if (c.size() > 0) {
428: write();
429: return bag.addAll(i, c);
430: } else {
431: return false;
432: }
433: }
434:
435: /**
436: * @see java.util.List#get(int)
437: */
438: public Object get(int i) {
439: read();
440: return bag.get(i);
441: }
442:
443: /**
444: * @see java.util.List#indexOf(Object)
445: */
446: public int indexOf(Object o) {
447: read();
448: return bag.indexOf(o);
449: }
450:
451: /**
452: * @see java.util.List#lastIndexOf(Object)
453: */
454: public int lastIndexOf(Object o) {
455: read();
456: return bag.lastIndexOf(o);
457: }
458:
459: /**
460: * @see java.util.List#listIterator()
461: */
462: public ListIterator listIterator() {
463: read();
464: return new ListIteratorProxy(bag.listIterator());
465: }
466:
467: /**
468: * @see java.util.List#listIterator(int)
469: */
470: public ListIterator listIterator(int i) {
471: read();
472: return new ListIteratorProxy(bag.listIterator(i));
473: }
474:
475: /**
476: * @see java.util.List#remove(int)
477: */
478: public Object remove(int i) {
479: write();
480: return bag.remove(i);
481: }
482:
483: /**
484: * @see java.util.List#set(int, Object)
485: */
486: public Object set(int i, Object o) {
487: write();
488: return bag.set(i, o);
489: }
490:
491: /**
492: * @see java.util.List#subList(int, int)
493: */
494: public List subList(int start, int end) {
495: read();
496: return new ListProxy(bag.subList(start, end));
497: }
498:
499: public String toString() {
500: read();
501: return bag.toString();
502: }
503:
504: /*public boolean equals(Object other) {
505: read();
506: return bag.equals(other);
507: }
508:
509: public int hashCode(Object other) {
510: read();
511: return bag.hashCode();
512: }*/
513:
514: public boolean entryExists(Object entry, int i) {
515: return entry != null;
516: }
517:
518: /**
519: * Bag does not respect the collection API and do an
520: * JVM instance comparison to do the equals.
521: * The semantic is broken not to have to initialize a
522: * collection for a simple equals() operation.
523: * @see java.lang.Object#equals(java.lang.Object)
524: */
525: public boolean equals(Object obj) {
526: return super .equals(obj);
527: }
528:
529: /**
530: * @see java.lang.Object#hashCode()
531: */
532: public int hashCode() {
533: return super .hashCode();
534: }
535:
536: final class Clear implements DelayedOperation {
537: public void operate() {
538: bag.clear();
539: }
540:
541: public Object getAddedInstance() {
542: return null;
543: }
544:
545: public Object getOrphan() {
546: throw new UnsupportedOperationException(
547: "queued clear cannot be used with orphan delete");
548: }
549: }
550:
551: final class SimpleAdd implements DelayedOperation {
552: private Object value;
553:
554: public SimpleAdd(Object value) {
555: this .value = value;
556: }
557:
558: public void operate() {
559: bag.add(value);
560: }
561:
562: public Object getAddedInstance() {
563: return value;
564: }
565:
566: public Object getOrphan() {
567: return null;
568: }
569: }
570:
571: }
|