1 /*
2 * Hibernate, Relational Persistence for Idiomatic Java
3 *
4 * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
5 * indicated by the @author tags or express copyright attribution
6 * statements applied by the authors. All third-party contributions are
7 * distributed under license by Red Hat Middleware LLC.
8 *
9 * This copyrighted material is made available to anyone wishing to use, modify,
10 * copy, or redistribute it subject to the terms and conditions of the GNU
11 * Lesser General Public License, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this distribution; if not, write to:
20 * Free Software Foundation, Inc.
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301 USA
23 *
24 */
25 package org.hibernate.collection;
26
27 import java.io.Serializable;
28 import java.sql.ResultSet;
29 import java.sql.SQLException;
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.HashMap;
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.Set;
36
37 import org.hibernate.EntityMode;
38 import org.hibernate.HibernateException;
39 import org.hibernate.engine.SessionImplementor;
40 import org.hibernate.loader.CollectionAliases;
41 import org.hibernate.persister.collection.CollectionPersister;
42 import org.hibernate.type.Type;
43
44
45 /**
46 * A persistent wrapper for a <tt>java.util.Set</tt>. The underlying
47 * collection is a <tt>HashSet</tt>.
48 *
49 * @see java.util.HashSet
50 * @author Gavin King
51 */
52 public class PersistentSet extends AbstractPersistentCollection implements java.util.Set {
53
54 protected Set set;
55 protected transient List tempList;
56
57 /**
58 * Empty constructor.
59 * <p/>
60 * Note: this form is not ever ever ever used by Hibernate; it is, however,
61 * needed for SOAP libraries and other such marshalling code.
62 */
63 public PersistentSet() {
64 // intentionally empty
65 }
66
67 /**
68 * Constructor matching super. Instantiates a lazy set (the underlying
69 * set is un-initialized).
70 *
71 * @param session The session to which this set will belong.
72 */
73 public PersistentSet(SessionImplementor session) {
74 super( session );
75 }
76
77 /**
78 * Instantiates a non-lazy set (the underlying set is constructed
79 * from the incoming set reference).
80 *
81 * @param session The session to which this set will belong.
82 * @param set The underlying set data.
83 */
84 public PersistentSet(SessionImplementor session, java.util.Set set) {
85 super(session);
86 // Sets can be just a view of a part of another collection.
87 // do we need to copy it to be sure it won't be changing
88 // underneath us?
89 // ie. this.set.addAll(set);
90 this.set = set;
91 setInitialized();
92 setDirectlyAccessible(true);
93 }
94
95
96 public Serializable getSnapshot(CollectionPersister persister)
97 throws HibernateException {
98 EntityMode entityMode = getSession().getEntityMode();
99
100 //if (set==null) return new Set(session);
101 HashMap clonedSet = new HashMap( set.size() );
102 Iterator iter = set.iterator();
103 while ( iter.hasNext() ) {
104 Object copied = persister.getElementType()
105 .deepCopy( iter.next(), entityMode, persister.getFactory() );
106 clonedSet.put(copied, copied);
107 }
108 return clonedSet;
109 }
110
111 public Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException {
112 java.util.Map sn = (java.util.Map) snapshot;
113 return getOrphans( sn.keySet(), set, entityName, getSession() );
114 }
115
116 public boolean equalsSnapshot(CollectionPersister persister) throws HibernateException {
117 Type elementType = persister.getElementType();
118 java.util.Map sn = (java.util.Map) getSnapshot();
119 if ( sn.size()!=set.size() ) {
120 return false;
121 }
122 else {
123 Iterator iter = set.iterator();
124 while ( iter.hasNext() ) {
125 Object test = iter.next();
126 Object oldValue = sn.get(test);
127 if ( oldValue==null || elementType.isDirty( oldValue, test, getSession() ) ) return false;
128 }
129 return true;
130 }
131 }
132
133 public boolean isSnapshotEmpty(Serializable snapshot) {
134 return ( (java.util.Map) snapshot ).isEmpty();
135 }
136
137 public void beforeInitialize(CollectionPersister persister, int anticipatedSize) {
138 this.set = ( Set ) persister.getCollectionType().instantiate( anticipatedSize );
139 }
140
141 public void initializeFromCache(CollectionPersister persister, Serializable disassembled, Object owner)
142 throws HibernateException {
143 Serializable[] array = ( Serializable[] ) disassembled;
144 int size = array.length;
145 beforeInitialize( persister, size );
146 for (int i = 0; i < size; i++ ) {
147 Object element = persister.getElementType().assemble( array[i], getSession(), owner );
148 if ( element != null ) {
149 set.add( element );
150 }
151 }
152 }
153
154 public boolean empty() {
155 return set.isEmpty();
156 }
157
158 /**
159 * @see java.util.Set#size()
160 */
161 public int size() {
162 return readSize() ? getCachedSize() : set.size();
163 }
164
165 /**
166 * @see java.util.Set#isEmpty()
167 */
168 public boolean isEmpty() {
169 return readSize() ? getCachedSize()==0 : set.isEmpty();
170 }
171
172 /**
173 * @see java.util.Set#contains(Object)
174 */
175 public boolean contains(Object object) {
176 Boolean exists = readElementExistence(object);
177 return exists==null ?
178 set.contains(object) :
179 exists.booleanValue();
180 }
181
182 /**
183 * @see java.util.Set#iterator()
184 */
185 public Iterator iterator() {
186 read();
187 return new IteratorProxy( set.iterator() );
188 }
189
190 /**
191 * @see java.util.Set#toArray()
192 */
193 public Object[] toArray() {
194 read();
195 return set.toArray();
196 }
197
198 /**
199 * @see java.util.Set#toArray(Object[])
200 */
201 public Object[] toArray(Object[] array) {
202 read();
203 return set.toArray(array);
204 }
205
206 /**
207 * @see java.util.Set#add(Object)
208 */
209 public boolean add(Object value) {
210 Boolean exists = isOperationQueueEnabled() ? readElementExistence( value ) : null;
211 if ( exists == null ) {
212 initialize( true );
213 if ( set.add( value ) ) {
214 dirty();
215 return true;
216 }
217 else {
218 return false;
219 }
220 }
221 else if ( exists.booleanValue() ) {
222 return false;
223 }
224 else {
225 queueOperation( new SimpleAdd(value) );
226 return true;
227 }
228 }
229
230 /**
231 * @see java.util.Set#remove(Object)
232 */
233 public boolean remove(Object value) {
234 Boolean exists = isPutQueueEnabled() ? readElementExistence( value ) : null;
235 if ( exists==null ) {
236 initialize( true );
237 if ( set.remove( value ) ) {
238 dirty();
239 return true;
240 }
241 else {
242 return false;
243 }
244 }
245 else if ( exists.booleanValue() ) {
246 queueOperation( new SimpleRemove(value) );
247 return true;
248 }
249 else {
250 return false;
251 }
252 }
253
254 /**
255 * @see java.util.Set#containsAll(Collection)
256 */
257 public boolean containsAll(Collection coll) {
258 read();
259 return set.containsAll(coll);
260 }
261
262 /**
263 * @see java.util.Set#addAll(Collection)
264 */
265 public boolean addAll(Collection coll) {
266 if ( coll.size() > 0 ) {
267 initialize( true );
268 if ( set.addAll( coll ) ) {
269 dirty();
270 return true;
271 }
272 else {
273 return false;
274 }
275 }
276 else {
277 return false;
278 }
279 }
280
281 /**
282 * @see java.util.Set#retainAll(Collection)
283 */
284 public boolean retainAll(Collection coll) {
285 initialize( true );
286 if ( set.retainAll( coll ) ) {
287 dirty();
288 return true;
289 }
290 else {
291 return false;
292 }
293 }
294
295 /**
296 * @see java.util.Set#removeAll(Collection)
297 */
298 public boolean removeAll(Collection coll) {
299 if ( coll.size() > 0 ) {
300 initialize( true );
301 if ( set.removeAll( coll ) ) {
302 dirty();
303 return true;
304 }
305 else {
306 return false;
307 }
308 }
309 else {
310 return false;
311 }
312 }
313
314 /**
315 * @see java.util.Set#clear()
316 */
317 public void clear() {
318 if ( isClearQueueEnabled() ) {
319 queueOperation( new Clear() );
320 }
321 else {
322 initialize( true );
323 if ( !set.isEmpty() ) {
324 set.clear();
325 dirty();
326 }
327 }
328 }
329
330 public String toString() {
331 //if (needLoading) return "asleep";
332 read();
333 return set.toString();
334 }
335
336 public Object readFrom(
337 ResultSet rs,
338 CollectionPersister persister,
339 CollectionAliases descriptor,
340 Object owner) throws HibernateException, SQLException {
341 Object element = persister.readElement( rs, owner, descriptor.getSuffixedElementAliases(), getSession() );
342 if (element!=null) tempList.add(element);
343 return element;
344 }
345
346 public void beginRead() {
347 super.beginRead();
348 tempList = new ArrayList();
349 }
350
351 public boolean endRead() {
352 set.addAll(tempList);
353 tempList = null;
354 setInitialized();
355 return true;
356 }
357
358 public Iterator entries(CollectionPersister persister) {
359 return set.iterator();
360 }
361
362 public Serializable disassemble(CollectionPersister persister)
363 throws HibernateException {
364
365 Serializable[] result = new Serializable[ set.size() ];
366 Iterator iter = set.iterator();
367 int i=0;
368 while ( iter.hasNext() ) {
369 result[i++] = persister.getElementType().disassemble( iter.next(), getSession(), null );
370 }
371 return result;
372
373 }
374
375 public Iterator getDeletes(CollectionPersister persister, boolean indexIsFormula) throws HibernateException {
376 Type elementType = persister.getElementType();
377 final java.util.Map sn = (java.util.Map) getSnapshot();
378 ArrayList deletes = new ArrayList( sn.size() );
379 Iterator iter = sn.keySet().iterator();
380 while ( iter.hasNext() ) {
381 Object test = iter.next();
382 if ( !set.contains(test) ) {
383 // the element has been removed from the set
384 deletes.add(test);
385 }
386 }
387 iter = set.iterator();
388 while ( iter.hasNext() ) {
389 Object test = iter.next();
390 Object oldValue = sn.get(test);
391 if ( oldValue!=null && elementType.isDirty( test, oldValue, getSession() ) ) {
392 // the element has changed
393 deletes.add(oldValue);
394 }
395 }
396 return deletes.iterator();
397 }
398
399 public boolean needsInserting(Object entry, int i, Type elemType) throws HibernateException {
400 final java.util.Map sn = (java.util.Map) getSnapshot();
401 Object oldValue = sn.get(entry);
402 // note that it might be better to iterate the snapshot but this is safe,
403 // assuming the user implements equals() properly, as required by the Set
404 // contract!
405 return oldValue==null || elemType.isDirty( oldValue, entry, getSession() );
406 }
407
408 public boolean needsUpdating(Object entry, int i, Type elemType) {
409 return false;
410 }
411
412 public boolean isRowUpdatePossible() {
413 return false;
414 }
415
416 public Object getIndex(Object entry, int i, CollectionPersister persister) {
417 throw new UnsupportedOperationException("Sets don't have indexes");
418 }
419
420 public Object getElement(Object entry) {
421 return entry;
422 }
423
424 public Object getSnapshotElement(Object entry, int i) {
425 throw new UnsupportedOperationException("Sets don't support updating by element");
426 }
427
428 public boolean equals(Object other) {
429 read();
430 return set.equals(other);
431 }
432
433 public int hashCode() {
434 read();
435 return set.hashCode();
436 }
437
438 public boolean entryExists(Object key, int i) {
439 return true;
440 }
441
442 public boolean isWrapper(Object collection) {
443 return set==collection;
444 }
445
446 final class Clear implements DelayedOperation {
447 public void operate() {
448 set.clear();
449 }
450 public Object getAddedInstance() {
451 return null;
452 }
453 public Object getOrphan() {
454 throw new UnsupportedOperationException("queued clear cannot be used with orphan delete");
455 }
456 }
457
458 final class SimpleAdd implements DelayedOperation {
459 private Object value;
460
461 public SimpleAdd(Object value) {
462 this.value = value;
463 }
464 public void operate() {
465 set.add(value);
466 }
467 public Object getAddedInstance() {
468 return value;
469 }
470 public Object getOrphan() {
471 return null;
472 }
473 }
474
475 final class SimpleRemove implements DelayedOperation {
476 private Object value;
477
478 public SimpleRemove(Object value) {
479 this.value = value;
480 }
481 public void operate() {
482 set.remove(value);
483 }
484 public Object getAddedInstance() {
485 return null;
486 }
487 public Object getOrphan() {
488 return value;
489 }
490 }
491 }