remotingApplicationContext : removed load time weaving config
[taxeditor.git] / eu.etaxonomy.taxeditor.cdmlib / src / main / java / org / hibernate / collection / internal / AbstractPersistentCollection.java
1
2
3 /*
4 * Hibernate, Relational Persistence for Idiomatic Java
5 *
6 * Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as
7 * indicated by the @author tags or express copyright attribution
8 * statements applied by the authors. All third-party contributions are
9 * distributed under license by Red Hat Inc.
10 *
11 * This copyrighted material is made available to anyone wishing to use, modify,
12 * copy, or redistribute it subject to the terms and conditions of the GNU
13 * Lesser General Public License, as published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
18 * for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this distribution; if not, write to:
22 * Free Software Foundation, Inc.
23 * 51 Franklin Street, Fifth Floor
24 * Boston, MA 02110-1301 USA
25 */
26 package org.hibernate.collection.internal;
27
28 import java.io.Serializable;
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.HashSet;
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.ListIterator;
36
37 import javax.naming.NamingException;
38
39 import org.hibernate.AssertionFailure;
40 import org.hibernate.HibernateException;
41 import org.hibernate.LazyInitializationException;
42 import org.hibernate.Session;
43 import org.hibernate.collection.spi.PersistentCollection;
44 import org.hibernate.engine.internal.ForeignKeys;
45 import org.hibernate.engine.spi.CollectionEntry;
46 import org.hibernate.engine.spi.EntityEntry;
47 import org.hibernate.engine.spi.SessionFactoryImplementor;
48 import org.hibernate.engine.spi.SessionImplementor;
49 import org.hibernate.engine.spi.Status;
50 import org.hibernate.engine.spi.TypedValue;
51 import org.hibernate.internal.SessionFactoryRegistry;
52 import org.hibernate.internal.util.MarkerObject;
53 import org.hibernate.internal.util.collections.EmptyIterator;
54 import org.hibernate.internal.util.collections.IdentitySet;
55 import org.hibernate.persister.collection.CollectionPersister;
56 import org.hibernate.persister.entity.EntityPersister;
57 import org.hibernate.pretty.MessageHelper;
58 import org.hibernate.type.Type;
59 import org.jboss.logging.Logger;
60
61 /**
62 * Base class implementing {@link org.hibernate.collection.spi.PersistentCollection}
63 *
64 * @author Gavin King
65 */
66 public abstract class AbstractPersistentCollection implements Serializable, PersistentCollection {
67 private static final Logger log = Logger.getLogger( AbstractPersistentCollection.class );
68
69 private static final long serialVersionUID = -7238232378593030571L;
70
71 private transient SessionImplementor session;
72 private boolean initialized;
73 private transient List<DelayedOperation> operationQueue;
74 private transient boolean directlyAccessible;
75 private transient boolean initializing;
76 private Object owner;
77 private int cachedSize = -1;
78
79 private String role;
80 private Serializable key;
81 // collections detect changes made via their public interface and mark
82 // themselves as dirty as a performance optimization
83 private boolean dirty;
84 private Serializable storedSnapshot;
85
86 private String sessionFactoryUuid;
87 private boolean specjLazyLoad = false;
88
89 public final String getRole() {
90 return role;
91 }
92
93 public final Serializable getKey() {
94 return key;
95 }
96
97 public final boolean isUnreferenced() {
98 return role == null;
99 }
100
101 public final boolean isDirty() {
102 return dirty;
103 }
104
105 public final void clearDirty() {
106 dirty = false;
107 }
108
109 public final void dirty() {
110 dirty = true;
111 }
112
113 public final Serializable getStoredSnapshot() {
114 return storedSnapshot;
115 }
116
117 //Careful: these methods do not initialize the collection.
118
119 /**
120 * Is the initialized collection empty?
121 */
122 public abstract boolean empty();
123
124 /**
125 * Called by any read-only method of the collection interface
126 */
127 protected final void read() {
128 initialize( false );
129 }
130
131 /**
132 * Called by the {@link Collection#size} method
133 */
134 @SuppressWarnings({"JavaDoc"})
135 protected boolean readSize() {
136 if ( !initialized ) {
137 if ( cachedSize != -1 && !hasQueuedOperations() ) {
138 return true;
139 }
140 else {
141 boolean isExtraLazy = withTemporarySessionIfNeeded(
142 new LazyInitializationWork<Boolean>() {
143 @Override
144 public Boolean doWork() {
145 CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
146
147 if ( entry != null ) {
148 CollectionPersister persister = entry.getLoadedPersister();
149 if ( persister.isExtraLazy() ) {
150 if ( hasQueuedOperations() ) {
151 session.flush();
152 }
153 cachedSize = persister.getSize( entry.getLoadedKey(), session );
154 return true;
155 }
156 else {
157 read();
158 }
159 }
160 else{
161 throwLazyInitializationExceptionIfNotConnected();
162 }
163 return false;
164 }
165 }
166 );
167 if ( isExtraLazy ) {
168 return true;
169 }
170 }
171 }
172 return false;
173 }
174
175 public static interface LazyInitializationWork<T> {
176 public T doWork();
177 }
178
179 private <T> T withTemporarySessionIfNeeded(LazyInitializationWork<T> lazyInitializationWork) {
180 SessionImplementor originalSession = null;
181 boolean isTempSession = false;
182 boolean isJTA = false;
183
184 if ( session == null ) {
185 if ( specjLazyLoad ) {
186 session = openTemporarySessionForLoading();
187 isTempSession = true;
188 }
189 else {
190 throwLazyInitializationException( "could not initialize proxy - no Session" );
191 }
192 }
193 else if ( !session.isOpen() ) {
194 if ( specjLazyLoad ) {
195 originalSession = session;
196 session = openTemporarySessionForLoading();
197 isTempSession = true;
198 }
199 else {
200 throwLazyInitializationException( "could not initialize proxy - the owning Session was closed" );
201 }
202 }
203 else if ( !session.isConnected() ) {
204 if ( specjLazyLoad ) {
205 originalSession = session;
206 session = openTemporarySessionForLoading();
207 isTempSession = true;
208 }
209 else {
210 throwLazyInitializationException( "could not initialize proxy - the owning Session is disconnected" );
211 }
212 }
213
214 if ( isTempSession ) {
215 // TODO: On the next major release, add an
216 // 'isJTA' or 'getTransactionFactory' method to Session.
217 isJTA = session.getTransactionCoordinator()
218 .getTransactionContext().getTransactionEnvironment()
219 .getTransactionFactory()
220 .compatibleWithJtaSynchronization();
221
222 if ( !isJTA ) {
223 // Explicitly handle the transactions only if we're not in
224 // a JTA environment. A lazy loading temporary session can
225 // be created even if a current session and transaction are
226 // open (ex: session.clear() was used). We must prevent
227 // multiple transactions.
228 ( ( Session) session ).beginTransaction();
229 }
230
231 session.getPersistenceContext().addUninitializedDetachedCollection(
232 session.getFactory().getCollectionPersister( getRole() ),
233 this
234 );
235 }
236
237 try {
238 return lazyInitializationWork.doWork();
239 }
240 finally {
241 if ( isTempSession ) {
242 // make sure the just opened temp session gets closed!
243 try {
244 if ( !isJTA ) {
245 ( ( Session) session ).getTransaction().commit();
246 }
247 ( (Session) session ).close();
248 }
249 catch (Exception e) {
250 log.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
251 }
252 session = originalSession;
253 }
254 }
255 }
256
257 private SessionImplementor openTemporarySessionForLoading() {
258 if ( sessionFactoryUuid == null ) {
259 throwLazyInitializationException( "SessionFactory UUID not known to create temporary Session for loading" );
260 }
261
262 SessionFactoryImplementor sf = (SessionFactoryImplementor)
263 SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid );
264 return (SessionImplementor) sf.openSession();
265 }
266
267 protected Boolean readIndexExistence(final Object index) {
268 if ( !initialized ) {
269 Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
270 new LazyInitializationWork<Boolean>() {
271 @Override
272 public Boolean doWork() {
273 CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
274 CollectionPersister persister = entry.getLoadedPersister();
275 if ( persister.isExtraLazy() ) {
276 if ( hasQueuedOperations() ) {
277 session.flush();
278 }
279 return persister.indexExists( entry.getLoadedKey(), index, session );
280 }
281 else {
282 read();
283 }
284 return null;
285 }
286 }
287 );
288 if ( extraLazyExistenceCheck != null ) {
289 return extraLazyExistenceCheck;
290 }
291 }
292 return null;
293 }
294
295 protected Boolean readElementExistence(final Object element) {
296 if ( !initialized ) {
297 Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
298 new LazyInitializationWork<Boolean>() {
299 @Override
300 public Boolean doWork() {
301 CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
302 CollectionPersister persister = entry.getLoadedPersister();
303 if ( persister.isExtraLazy() ) {
304 if ( hasQueuedOperations() ) {
305 session.flush();
306 }
307 return persister.elementExists( entry.getLoadedKey(), element, session );
308 }
309 else {
310 read();
311 }
312 return null;
313 }
314 }
315 );
316 if ( extraLazyExistenceCheck != null ) {
317 return extraLazyExistenceCheck;
318 }
319 }
320 return null;
321 }
322
323 protected static final Object UNKNOWN = new MarkerObject( "UNKNOWN" );
324
325 protected Object readElementByIndex(final Object index) {
326 if ( !initialized ) {
327 class ExtraLazyElementByIndexReader implements LazyInitializationWork {
328 private boolean isExtraLazy;
329 private Object element;
330
331 @Override
332 public Object doWork() {
333 CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
334 CollectionPersister persister = entry.getLoadedPersister();
335 isExtraLazy = persister.isExtraLazy();
336 if ( isExtraLazy ) {
337 if ( hasQueuedOperations() ) {
338 session.flush();
339 }
340 element = persister.getElementByIndex( entry.getLoadedKey(), index, session, owner );
341 }
342 else {
343 read();
344 }
345 return null;
346 }
347 }
348
349 ExtraLazyElementByIndexReader reader = new ExtraLazyElementByIndexReader();
350 //noinspection unchecked
351 withTemporarySessionIfNeeded( reader );
352 if ( reader.isExtraLazy ) {
353 return reader.element;
354 }
355 }
356 return UNKNOWN;
357
358 }
359
360 protected int getCachedSize() {
361 return cachedSize;
362 }
363
364 private boolean isConnectedToSession() {
365 return session != null &&
366 session.isOpen() &&
367 session.getPersistenceContext().containsCollection( this );
368 }
369
370 /**
371 * Called by any writer method of the collection interface
372 */
373 protected final void write() {
374 initialize( true );
375 dirty();
376 }
377
378 /**
379 * Is this collection in a state that would allow us to
380 * "queue" operations?
381 */
382 @SuppressWarnings({"JavaDoc"})
383 protected boolean isOperationQueueEnabled() {
384 return !initialized &&
385 isConnectedToSession() &&
386 isInverseCollection();
387 }
388
389 /**
390 * Is this collection in a state that would allow us to
391 * "queue" puts? This is a special case, because of orphan
392 * delete.
393 */
394 @SuppressWarnings({"JavaDoc"})
395 protected boolean isPutQueueEnabled() {
396 return !initialized &&
397 isConnectedToSession() &&
398 isInverseOneToManyOrNoOrphanDelete();
399 }
400
401 /**
402 * Is this collection in a state that would allow us to
403 * "queue" clear? This is a special case, because of orphan
404 * delete.
405 */
406 @SuppressWarnings({"JavaDoc"})
407 protected boolean isClearQueueEnabled() {
408 return !initialized &&
409 isConnectedToSession() &&
410 isInverseCollectionNoOrphanDelete();
411 }
412
413 /**
414 * Is this the "inverse" end of a bidirectional association?
415 */
416 @SuppressWarnings({"JavaDoc"})
417 private boolean isInverseCollection() {
418 CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
419 return ce != null && ce.getLoadedPersister().isInverse();
420 }
421
422 /**
423 * Is this the "inverse" end of a bidirectional association with
424 * no orphan delete enabled?
425 */
426 @SuppressWarnings({"JavaDoc"})
427 private boolean isInverseCollectionNoOrphanDelete() {
428 CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
429 return ce != null &&
430 ce.getLoadedPersister().isInverse() &&
431 !ce.getLoadedPersister().hasOrphanDelete();
432 }
433
434 /**
435 * Is this the "inverse" end of a bidirectional one-to-many, or
436 * of a collection with no orphan delete?
437 */
438 @SuppressWarnings({"JavaDoc"})
439 private boolean isInverseOneToManyOrNoOrphanDelete() {
440 CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
441 return ce != null && ce.getLoadedPersister().isInverse() && (
442 ce.getLoadedPersister().isOneToMany() ||
443 !ce.getLoadedPersister().hasOrphanDelete()
444 );
445 }
446
447 /**
448 * Queue an addition
449 */
450 @SuppressWarnings({"JavaDoc"})
451 protected final void queueOperation(DelayedOperation operation) {
452 if ( operationQueue == null ) {
453 operationQueue = new ArrayList<DelayedOperation>( 10 );
454 }
455 operationQueue.add( operation );
456 dirty = true; //needed so that we remove this collection from the second-level cache
457 }
458
459 /**
460 * After reading all existing elements from the database,
461 * add the queued elements to the underlying collection.
462 */
463 protected final void performQueuedOperations() {
464 for ( DelayedOperation operation : operationQueue ) {
465 operation.operate();
466 }
467 }
468
469 /**
470 * After flushing, re-init snapshot state.
471 */
472 public void setSnapshot(Serializable key, String role, Serializable snapshot) {
473 this.key = key;
474 this.role = role;
475 this.storedSnapshot = snapshot;
476 }
477
478 /**
479 * After flushing, clear any "queued" additions, since the
480 * database state is now synchronized with the memory state.
481 */
482 public void postAction() {
483 operationQueue = null;
484 cachedSize = -1;
485 clearDirty();
486 }
487
488 /**
489 * Not called by Hibernate, but used by non-JDK serialization,
490 * eg. SOAP libraries.
491 */
492 public AbstractPersistentCollection() {
493 }
494
495 protected AbstractPersistentCollection(SessionImplementor session) {
496 this.session = session;
497 }
498
499 /**
500 * return the user-visible collection (or array) instance
501 */
502 public Object getValue() {
503 return this;
504 }
505
506 /**
507 * Called just before reading any rows from the JDBC result set
508 */
509 public void beginRead() {
510 // override on some subclasses
511 initializing = true;
512 }
513
514 /**
515 * Called after reading all rows from the JDBC result set
516 */
517 public boolean endRead() {
518 //override on some subclasses
519 return afterInitialize();
520 }
521
522 public boolean afterInitialize() {
523 setInitialized();
524 //do this bit after setting initialized to true or it will recurse
525 if ( operationQueue != null ) {
526 performQueuedOperations();
527 operationQueue = null;
528 cachedSize = -1;
529 return false;
530 }
531 else {
532 return true;
533 }
534 }
535
536 /**
537 * Initialize the collection, if possible, wrapping any exceptions
538 * in a runtime exception
539 *
540 * @param writing currently obsolete
541 *
542 * @throws LazyInitializationException if we cannot initialize
543 */
544 protected final void initialize(final boolean writing) {
545 if ( initialized ) {
546 return;
547 }
548
549 withTemporarySessionIfNeeded(
550 new LazyInitializationWork<Object>() {
551 @Override
552 public Object doWork() {
553 session.initializeCollection( AbstractPersistentCollection.this, writing );
554 return null;
555 }
556 }
557 );
558 }
559
560 private void throwLazyInitializationExceptionIfNotConnected() {
561 if ( !isConnectedToSession() ) {
562 throwLazyInitializationException( "no session or session was closed" );
563 }
564 if ( !session.isConnected() ) {
565 throwLazyInitializationException( "session is disconnected" );
566 }
567 }
568
569 private void throwLazyInitializationException(String message) {
570 throw new LazyInitializationException(
571 "failed to lazily initialize a collection" +
572 (role == null ? "" : " of role: " + role) +
573 ", " + message
574 );
575 }
576
577 protected final void setInitialized() {
578 this.initializing = false;
579 this.initialized = true;
580 }
581
582 protected final void setDirectlyAccessible(boolean directlyAccessible) {
583 this.directlyAccessible = directlyAccessible;
584 }
585
586 /**
587 * Could the application possibly have a direct reference to
588 * the underlying collection implementation?
589 */
590 public boolean isDirectlyAccessible() {
591 return directlyAccessible;
592 }
593
594 /**
595 * Disassociate this collection from the given session.
596 *
597 * @return true if this was currently associated with the given session
598 */
599 public final boolean unsetSession(SessionImplementor currentSession) {
600 prepareForPossibleSpecialSpecjInitialization();
601 if ( currentSession == this.session ) {
602 this.session = null;
603 return true;
604 }
605 else {
606 return false;
607 }
608 }
609
610 protected void prepareForPossibleSpecialSpecjInitialization() {
611 if ( session != null ) {
612 specjLazyLoad = session.getFactory().getSettings().isInitializeLazyStateOutsideTransactionsEnabled();
613
614 if ( specjLazyLoad && sessionFactoryUuid == null ) {
615 try {
616 sessionFactoryUuid = (String) session.getFactory().getReference().get( "uuid" ).getContent();
617 }
618 catch (NamingException e) {
619 //not much we can do if this fails...
620 }
621 }
622 }
623 }
624
625
626 /**
627 * Associate the collection with the given session.
628 *
629 * @return false if the collection was already associated with the session
630 *
631 * @throws HibernateException if the collection was already associated
632 * with another open session
633 */
634 public final boolean setCurrentSession(SessionImplementor session) throws HibernateException {
635 if ( session == this.session ) {
636 return false;
637 }
638 else {
639 if ( isConnectedToSession() ) {
640 CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
641 if ( ce == null ) {
642 throw new HibernateException(
643 "Illegal attempt to associate a collection with two open sessions"
644 );
645 }
646 else {
647 throw new HibernateException(
648 "Illegal attempt to associate a collection with two open sessions: " +
649 MessageHelper.collectionInfoString(
650 ce.getLoadedPersister(), this,
651 ce.getLoadedKey(), session
652 )
653 );
654 }
655 }
656 else {
657 this.session = session;
658 return true;
659 }
660 }
661 }
662
663 /**
664 * Do we need to completely recreate this collection when it changes?
665 */
666 public boolean needsRecreate(CollectionPersister persister) {
667 return false;
668 }
669
670 /**
671 * To be called internally by the session, forcing
672 * immediate initialization.
673 */
674 public final void forceInitialization() throws HibernateException {
675 if ( !initialized ) {
676 if ( initializing ) {
677 throw new AssertionFailure( "force initialize loading collection" );
678 }
679 if ( session == null ) {
680 throw new HibernateException( "collection is not associated with any session" );
681 }
682 if ( !session.isConnected() ) {
683 throw new HibernateException( "disconnected session" );
684 }
685 session.initializeCollection( this, false );
686 }
687 }
688
689
690 /**
691 * Get the current snapshot from the session
692 */
693 @SuppressWarnings({"JavaDoc"})
694 protected final Serializable getSnapshot() {
695 return session.getPersistenceContext().getSnapshot( this );
696 }
697
698 /**
699 * Is this instance initialized?
700 */
701 public final boolean wasInitialized() {
702 return initialized;
703 }
704
705 public boolean isRowUpdatePossible() {
706 return true;
707 }
708
709 /**
710 * Does this instance have any "queued" additions?
711 */
712 public final boolean hasQueuedOperations() {
713 return operationQueue != null;
714 }
715
716 /**
717 * Iterate the "queued" additions
718 */
719 public final Iterator queuedAdditionIterator() {
720 if ( hasQueuedOperations() ) {
721 return new Iterator() {
722 int i = 0;
723
724 public Object next() {
725 return operationQueue.get( i++ ).getAddedInstance();
726 }
727
728 public boolean hasNext() {
729 return i < operationQueue.size();
730 }
731
732 public void remove() {
733 throw new UnsupportedOperationException();
734 }
735 };
736 }
737 else {
738 return EmptyIterator.INSTANCE;
739 }
740 }
741
742 /**
743 * Iterate the "queued" additions
744 */
745 @SuppressWarnings({"unchecked"})
746 public final Collection getQueuedOrphans(String entityName) {
747 if ( hasQueuedOperations() ) {
748 Collection additions = new ArrayList( operationQueue.size() );
749 Collection removals = new ArrayList( operationQueue.size() );
750 for ( DelayedOperation operation : operationQueue ) {
751 additions.add( operation.getAddedInstance() );
752 removals.add( operation.getOrphan() );
753 }
754 return getOrphans( removals, additions, entityName, session );
755 }
756 else {
757 return Collections.EMPTY_LIST;
758 }
759 }
760
761 /**
762 * Called before inserting rows, to ensure that any surrogate keys
763 * are fully generated
764 */
765 public void preInsert(CollectionPersister persister) throws HibernateException {
766 }
767
768 /**
769 * Called after inserting a row, to fetch the natively generated id
770 */
771 public void afterRowInsert(CollectionPersister persister, Object entry, int i) throws HibernateException {
772 }
773
774 /**
775 * get all "orphaned" elements
776 */
777 public abstract Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException;
778
779 /**
780 * Get the current session
781 */
782 @SuppressWarnings({"JavaDoc"})
783 public final SessionImplementor getSession() {
784 return session;
785 }
786
787 protected final class IteratorProxy implements Iterator {
788 protected final Iterator itr;
789
790 public IteratorProxy(Iterator itr) {
791 this.itr = itr;
792 }
793
794 public boolean hasNext() {
795 return itr.hasNext();
796 }
797
798 public Object next() {
799 return itr.next();
800 }
801
802 public void remove() {
803 write();
804 itr.remove();
805 }
806
807 }
808
809 protected final class ListIteratorProxy implements ListIterator {
810 protected final ListIterator itr;
811
812 public ListIteratorProxy(ListIterator itr) {
813 this.itr = itr;
814 }
815
816 @SuppressWarnings({"unchecked"})
817 public void add(Object o) {
818 write();
819 itr.add( o );
820 }
821
822 public boolean hasNext() {
823 return itr.hasNext();
824 }
825
826 public boolean hasPrevious() {
827 return itr.hasPrevious();
828 }
829
830 public Object next() {
831 return itr.next();
832 }
833
834 public int nextIndex() {
835 return itr.nextIndex();
836 }
837
838 public Object previous() {
839 return itr.previous();
840 }
841
842 public int previousIndex() {
843 return itr.previousIndex();
844 }
845
846 public void remove() {
847 write();
848 itr.remove();
849 }
850
851 @SuppressWarnings({"unchecked"})
852 public void set(Object o) {
853 write();
854 itr.set( o );
855 }
856
857 }
858
859 protected class SetProxy implements java.util.Set {
860 protected final Collection set;
861
862 public SetProxy(Collection set) {
863 this.set = set;
864 }
865
866 @SuppressWarnings({"unchecked"})
867 public boolean add(Object o) {
868 write();
869 return set.add( o );
870 }
871
872 @SuppressWarnings({"unchecked"})
873 public boolean addAll(Collection c) {
874 write();
875 return set.addAll( c );
876 }
877
878 public void clear() {
879 write();
880 set.clear();
881 }
882
883 public boolean contains(Object o) {
884 return set.contains( o );
885 }
886
887 public boolean containsAll(Collection c) {
888 return set.containsAll( c );
889 }
890
891 public boolean isEmpty() {
892 return set.isEmpty();
893 }
894
895 public Iterator iterator() {
896 return new IteratorProxy( set.iterator() );
897 }
898
899 public boolean remove(Object o) {
900 write();
901 return set.remove( o );
902 }
903
904 public boolean removeAll(Collection c) {
905 write();
906 return set.removeAll( c );
907 }
908
909 public boolean retainAll(Collection c) {
910 write();
911 return set.retainAll( c );
912 }
913
914 public int size() {
915 return set.size();
916 }
917
918 public Object[] toArray() {
919 return set.toArray();
920 }
921
922 @SuppressWarnings({"unchecked"})
923 public Object[] toArray(Object[] array) {
924 return set.toArray( array );
925 }
926
927 }
928
929 protected final class ListProxy implements java.util.List {
930 protected final List list;
931
932 public ListProxy(List list) {
933 this.list = list;
934 }
935
936 @Override
937 @SuppressWarnings({"unchecked"})
938 public void add(int index, Object value) {
939 write();
940 list.add( index, value );
941 }
942
943 @Override
944 @SuppressWarnings({"unchecked"})
945 public boolean add(Object o) {
946 write();
947 return list.add( o );
948 }
949
950 @Override
951 @SuppressWarnings({"unchecked"})
952 public boolean addAll(Collection c) {
953 write();
954 return list.addAll( c );
955 }
956
957 @Override
958 @SuppressWarnings({"unchecked"})
959 public boolean addAll(int i, Collection c) {
960 write();
961 return list.addAll( i, c );
962 }
963
964 @Override
965 public void clear() {
966 write();
967 list.clear();
968 }
969
970 @Override
971 public boolean contains(Object o) {
972 return list.contains( o );
973 }
974
975 @Override
976 public boolean containsAll(Collection c) {
977 return list.containsAll( c );
978 }
979
980 @Override
981 public Object get(int i) {
982 return list.get( i );
983 }
984
985 @Override
986 public int indexOf(Object o) {
987 return list.indexOf( o );
988 }
989
990 @Override
991 public boolean isEmpty() {
992 return list.isEmpty();
993 }
994
995 @Override
996 public Iterator iterator() {
997 return new IteratorProxy( list.iterator() );
998 }
999
1000 @Override
1001 public int lastIndexOf(Object o) {
1002 return list.lastIndexOf( o );
1003 }
1004
1005 @Override
1006 public ListIterator listIterator() {
1007 return new ListIteratorProxy( list.listIterator() );
1008 }
1009
1010 @Override
1011 public ListIterator listIterator(int i) {
1012 return new ListIteratorProxy( list.listIterator( i ) );
1013 }
1014
1015 @Override
1016 public Object remove(int i) {
1017 write();
1018 return list.remove( i );
1019 }
1020
1021 @Override
1022 public boolean remove(Object o) {
1023 write();
1024 return list.remove( o );
1025 }
1026
1027 @Override
1028 public boolean removeAll(Collection c) {
1029 write();
1030 return list.removeAll( c );
1031 }
1032
1033 @Override
1034 public boolean retainAll(Collection c) {
1035 write();
1036 return list.retainAll( c );
1037 }
1038
1039 @Override
1040 @SuppressWarnings({"unchecked"})
1041 public Object set(int i, Object o) {
1042 write();
1043 return list.set( i, o );
1044 }
1045
1046 @Override
1047 public int size() {
1048 return list.size();
1049 }
1050
1051 @Override
1052 public List subList(int i, int j) {
1053 return list.subList( i, j );
1054 }
1055
1056 @Override
1057 public Object[] toArray() {
1058 return list.toArray();
1059 }
1060
1061 @Override
1062 @SuppressWarnings({"unchecked"})
1063 public Object[] toArray(Object[] array) {
1064 return list.toArray( array );
1065 }
1066
1067 }
1068
1069 /**
1070 * Contract for operations which are part of a collection's operation queue.
1071 */
1072 protected interface DelayedOperation {
1073 public void operate();
1074
1075 public Object getAddedInstance();
1076
1077 public Object getOrphan();
1078 }
1079
1080 /**
1081 * Given a collection of entity instances that used to
1082 * belong to the collection, and a collection of instances
1083 * that currently belong, return a collection of orphans
1084 */
1085 @SuppressWarnings({"JavaDoc", "unchecked"})
1086 protected static Collection getOrphans(
1087 Collection oldElements,
1088 Collection currentElements,
1089 String entityName,
1090 SessionImplementor session) throws HibernateException {
1091
1092 // short-circuit(s)
1093 if ( currentElements.size() == 0 ) {
1094 return oldElements; // no new elements, the old list contains only Orphans
1095 }
1096 if ( oldElements.size() == 0 ) {
1097 return oldElements; // no old elements, so no Orphans neither
1098 }
1099
1100 final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName );
1101 final Type idType = entityPersister.getIdentifierType();
1102
1103 // create the collection holding the Orphans
1104 Collection res = new ArrayList();
1105
1106 // collect EntityIdentifier(s) of the *current* elements - add them into a HashSet for fast access
1107 java.util.Set currentIds = new HashSet();
1108 java.util.Set currentSaving = new IdentitySet();
1109 for ( Object current : currentElements ) {
1110 if ( current != null && ForeignKeys.isNotTransient( entityName, current, null, session ) ) {
1111 EntityEntry ee = session.getPersistenceContext().getEntry( current );
1112 if ( ee != null && ee.getStatus() == Status.SAVING ) {
1113 currentSaving.add( current );
1114 }
1115 else {
1116 Serializable currentId = ForeignKeys.getEntityIdentifierIfNotUnsaved(
1117 entityName,
1118 current,
1119 session
1120 );
1121 currentIds.add( new TypedValue( idType, currentId, entityPersister.getEntityMode() ) );
1122 }
1123 }
1124 }
1125
1126 // iterate over the *old* list
1127 for ( Object old : oldElements ) {
1128 if ( !currentSaving.contains( old ) ) {
1129 Serializable oldId = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, old, session );
1130 if ( !currentIds.contains( new TypedValue( idType, oldId, entityPersister.getEntityMode() ) ) ) {
1131 res.add( old );
1132 }
1133 }
1134 }
1135
1136 return res;
1137 }
1138
1139 public static void identityRemove(
1140 Collection list,
1141 Object object,
1142 String entityName,
1143 SessionImplementor session) throws HibernateException {
1144
1145 if ( object != null && ForeignKeys.isNotTransient( entityName, object, null, session ) ) {
1146 final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName );
1147 Type idType = entityPersister.getIdentifierType();
1148
1149 Serializable idOfCurrent = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, object, session );
1150 Iterator itr = list.iterator();
1151 while ( itr.hasNext() ) {
1152 Serializable idOfOld = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, itr.next(), session );
1153 if ( idType.isEqual( idOfCurrent, idOfOld, session.getFactory() ) ) {
1154 itr.remove();
1155 break;
1156 }
1157 }
1158
1159 }
1160 }
1161
1162 public Object getIdentifier(Object entry, int i) {
1163 throw new UnsupportedOperationException();
1164 }
1165
1166 public Object getOwner() {
1167 return owner;
1168 }
1169
1170 public void setOwner(Object owner) {
1171 this.owner = owner;
1172 }
1173
1174 }
1175