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.Iterator;
33 import java.util.List;
34 import java.util.ListIterator;
35
36 import org.hibernate.EntityMode;
37 import org.hibernate.HibernateException;
38 import org.hibernate.engine.SessionImplementor;
39 import org.hibernate.loader.CollectionAliases;
40 import org.hibernate.persister.collection.CollectionPersister;
41 import org.hibernate.type.Type;
42
43 /**
44 * A persistent wrapper for a <tt>java.util.List</tt>. Underlying
45 * collection is an <tt>ArrayList</tt>.
46 *
47 * @see java.util.ArrayList
48 * @author Gavin King
49 */
50 public class PersistentList extends AbstractPersistentCollection implements List {
51
52 protected List list;
53
54 public Serializable getSnapshot(CollectionPersister persister) throws HibernateException {
55
56 EntityMode entityMode = getSession().getEntityMode();
57
58 ArrayList clonedList = new ArrayList( list.size() );
59 Iterator iter = list.iterator();
60 while ( iter.hasNext() ) {
61 Object deepCopy = persister.getElementType()
62 .deepCopy( iter.next(), entityMode, persister.getFactory() );
63 clonedList.add( deepCopy );
64 }
65 return clonedList;
66 }
67
68 public Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException {
69 List sn = (List) snapshot;
70 return getOrphans( sn, list, entityName, getSession() );
71 }
72
73 public boolean equalsSnapshot(CollectionPersister persister) throws HibernateException {
74 Type elementType = persister.getElementType();
75 List sn = (List) getSnapshot();
76 if ( sn.size()!=this.list.size() ) return false;
77 Iterator iter = list.iterator();
78 Iterator sniter = sn.iterator();
79 while ( iter.hasNext() ) {
80 if ( elementType.isDirty( iter.next(), sniter.next(), getSession() ) ) return false;
81 }
82 return true;
83 }
84
85 public boolean isSnapshotEmpty(Serializable snapshot) {
86 return ( (Collection) snapshot ).isEmpty();
87 }
88
89 public PersistentList(SessionImplementor session) {
90 super(session);
91 }
92
93 public PersistentList(SessionImplementor session, List list) {
94 super(session);
95 this.list = list;
96 setInitialized();
97 setDirectlyAccessible(true);
98 }
99
100 public void beforeInitialize(CollectionPersister persister, int anticipatedSize) {
101 this.list = ( List ) persister.getCollectionType().instantiate( anticipatedSize );
102 }
103
104 public boolean isWrapper(Object collection) {
105 return list==collection;
106 }
107
108 public PersistentList() {} //needed for SOAP libraries, etc
109
110 /**
111 * @see java.util.List#size()
112 */
113 public int size() {
114 return readSize() ? getCachedSize() : list.size();
115 }
116
117 /**
118 * @see java.util.List#isEmpty()
119 */
120 public boolean isEmpty() {
121 return readSize() ? getCachedSize()==0 : list.isEmpty();
122 }
123
124 /**
125 * @see java.util.List#contains(Object)
126 */
127 public boolean contains(Object object) {
128 Boolean exists = readElementExistence(object);
129 return exists==null ?
130 list.contains(object) :
131 exists.booleanValue();
132 }
133
134 /**
135 * @see java.util.List#iterator()
136 */
137 public Iterator iterator() {
138 read();
139 return new IteratorProxy( list.iterator() );
140 }
141
142 /**
143 * @see java.util.List#toArray()
144 */
145 public Object[] toArray() {
146 read();
147 return list.toArray();
148 }
149
150 /**
151 * @see java.util.List#toArray(Object[])
152 */
153 public Object[] toArray(Object[] array) {
154 read();
155 return list.toArray(array);
156 }
157
158 /**
159 * @see java.util.List#add(Object)
160 */
161 public boolean add(Object object) {
162 if ( !isOperationQueueEnabled() ) {
163 write();
164 return list.add(object);
165 }
166 else {
167 queueOperation( new SimpleAdd(object) );
168 return true;
169 }
170 }
171
172 /**
173 * @see java.util.List#remove(Object)
174 */
175 public boolean remove(Object value) {
176 Boolean exists = isPutQueueEnabled() ? readElementExistence(value) : null;
177 if ( exists == null ) {
178 initialize( true );
179 if ( list.remove( value ) ) {
180 dirty();
181 return true;
182 }
183 else {
184 return false;
185 }
186 }
187 else if ( exists.booleanValue() ) {
188 queueOperation( new SimpleRemove(value) );
189 return true;
190 }
191 else {
192 return false;
193 }
194 }
195
196 /**
197 * @see java.util.List#containsAll(Collection)
198 */
199 public boolean containsAll(Collection coll) {
200 read();
201 return list.containsAll(coll);
202 }
203
204 /**
205 * @see java.util.List#addAll(Collection)
206 */
207 public boolean addAll(Collection values) {
208 if ( values.size()==0 ) {
209 return false;
210 }
211 if ( !isOperationQueueEnabled() ) {
212 write();
213 return list.addAll(values);
214 }
215 else {
216 Iterator iter = values.iterator();
217 while ( iter.hasNext() ) {
218 queueOperation( new SimpleAdd( iter.next() ) );
219 }
220 return values.size()>0;
221 }
222 }
223
224 /**
225 * @see java.util.List#addAll(int, Collection)
226 */
227 public boolean addAll(int index, Collection coll) {
228 if ( coll.size()>0 ) {
229 write();
230 return list.addAll(index, coll);
231 }
232 else {
233 return false;
234 }
235 }
236
237 /**
238 * @see java.util.List#removeAll(Collection)
239 */
240 public boolean removeAll(Collection coll) {
241 if ( coll.size()>0 ) {
242 initialize( true );
243 if ( list.removeAll( coll ) ) {
244 dirty();
245 return true;
246 }
247 else {
248 return false;
249 }
250 }
251 else {
252 return false;
253 }
254 }
255
256 /**
257 * @see java.util.List#retainAll(Collection)
258 */
259 public boolean retainAll(Collection coll) {
260 initialize( true );
261 if ( list.retainAll( coll ) ) {
262 dirty();
263 return true;
264 }
265 else {
266 return false;
267 }
268 }
269
270 /**
271 * @see java.util.List#clear()
272 */
273 public void clear() {
274 if ( isClearQueueEnabled() ) {
275 queueOperation( new Clear() );
276 }
277 else {
278 initialize( true );
279 if ( ! list.isEmpty() ) {
280 list.clear();
281 dirty();
282 }
283 }
284 }
285
286 /**
287 * @see java.util.List#get(int)
288 */
289 public Object get(int index) {
290 if (index<0) {
291 throw new ArrayIndexOutOfBoundsException("negative index");
292 }
293 Object result = readElementByIndex( new Integer(index) );
294 return result==UNKNOWN ? list.get(index) : result;
295 }
296
297 /**
298 * @see java.util.List#set(int, Object)
299 */
300 public Object set(int index, Object value) {
301 if (index<0) {
302 throw new ArrayIndexOutOfBoundsException("negative index");
303 }
304 Object old = isPutQueueEnabled() ? readElementByIndex( new Integer(index) ) : UNKNOWN;
305 if ( old==UNKNOWN ) {
306 write();
307 return list.set(index, value);
308 }
309 else {
310 queueOperation( new Set(index, value, old) );
311 return old;
312 }
313 }
314
315 /**
316 * @see java.util.List#add(int, Object)
317 */
318 public void add(int index, Object value) {
319 if (index<0) {
320 throw new ArrayIndexOutOfBoundsException("negative index");
321 }
322 if ( !isOperationQueueEnabled() ) {
323 write();
324 list.add(index, value);
325 }
326 else {
327 queueOperation( new Add(index, value) );
328 }
329 }
330
331 /**
332 * @see java.util.List#remove(int)
333 */
334 public Object remove(int index) {
335 if (index<0) {
336 throw new ArrayIndexOutOfBoundsException("negative index");
337 }
338 Object old = isPutQueueEnabled() ?
339 readElementByIndex( new Integer(index) ) : UNKNOWN;
340 if ( old==UNKNOWN ) {
341 write();
342 return list.remove(index);
343 }
344 else {
345 queueOperation( new Remove(index, old) );
346 return old;
347 }
348 }
349
350 /**
351 * @see java.util.List#indexOf(Object)
352 */
353 public int indexOf(Object value) {
354 read();
355 return list.indexOf(value);
356 }
357
358 /**
359 * @see java.util.List#lastIndexOf(Object)
360 */
361 public int lastIndexOf(Object value) {
362 read();
363 return list.lastIndexOf(value);
364 }
365
366 /**
367 * @see java.util.List#listIterator()
368 */
369 public ListIterator listIterator() {
370 read();
371 return new ListIteratorProxy( list.listIterator() );
372 }
373
374 /**
375 * @see java.util.List#listIterator(int)
376 */
377 public ListIterator listIterator(int index) {
378 read();
379 return new ListIteratorProxy( list.listIterator(index) );
380 }
381
382 /**
383 * @see java.util.List#subList(int, int)
384 */
385 public java.util.List subList(int from, int to) {
386 read();
387 return new ListProxy( list.subList(from, to) );
388 }
389
390 public boolean empty() {
391 return list.isEmpty();
392 }
393
394 public String toString() {
395 read();
396 return list.toString();
397 }
398
399 public Object readFrom(ResultSet rs, CollectionPersister persister, CollectionAliases descriptor, Object owner)
400 throws HibernateException, SQLException {
401 Object element = persister.readElement( rs, owner, descriptor.getSuffixedElementAliases(), getSession() ) ;
402 int index = ( (Integer) persister.readIndex( rs, descriptor.getSuffixedIndexAliases(), getSession() ) ).intValue();
403
404 //pad with nulls from the current last element up to the new index
405 for ( int i = list.size(); i<=index; i++) {
406 list.add(i, null);
407 }
408
409 list.set(index, element);
410 return element;
411 }
412
413 public Iterator entries(CollectionPersister persister) {
414 return list.iterator();
415 }
416
417 public void initializeFromCache(CollectionPersister persister, Serializable disassembled, Object owner)
418 throws HibernateException {
419 Serializable[] array = ( Serializable[] ) disassembled;
420 int size = array.length;
421 beforeInitialize( persister, size );
422 for ( int i = 0; i < size; i++ ) {
423 list.add( persister.getElementType().assemble( array[i], getSession(), owner ) );
424 }
425 }
426
427 public Serializable disassemble(CollectionPersister persister)
428 throws HibernateException {
429
430 int length = list.size();
431 Serializable[] result = new Serializable[length];
432 for ( int i=0; i<length; i++ ) {
433 result[i] = persister.getElementType().disassemble( list.get(i), getSession(), null );
434 }
435 return result;
436 }
437
438
439 public Iterator getDeletes(CollectionPersister persister, boolean indexIsFormula) throws HibernateException {
440 List deletes = new ArrayList();
441 List sn = (List) getSnapshot();
442 int end;
443 if ( sn.size() > list.size() ) {
444 for ( int i=list.size(); i<sn.size(); i++ ) {
445 deletes.add( indexIsFormula ? sn.get(i) : new Integer(i) );
446 }
447 end = list.size();
448 }
449 else {
450 end = sn.size();
451 }
452 for ( int i=0; i<end; i++ ) {
453 if ( list.get(i)==null && sn.get(i)!=null ) {
454 deletes.add( indexIsFormula ? sn.get(i) : new Integer(i) );
455 }
456 }
457 return deletes.iterator();
458 }
459
460 public boolean needsInserting(Object entry, int i, Type elemType) throws HibernateException {
461 final List sn = (List) getSnapshot();
462 return list.get(i)!=null && ( i >= sn.size() || sn.get(i)==null );
463 }
464
465 public boolean needsUpdating(Object entry, int i, Type elemType) throws HibernateException {
466 final List sn = (List) getSnapshot();
467 return i<sn.size() && sn.get(i)!=null && list.get(i)!=null &&
468 elemType.isDirty( list.get(i), sn.get(i), getSession() );
469 }
470
471 public Object getIndex(Object entry, int i, CollectionPersister persister) {
472 return new Integer(i);
473 }
474
475 public Object getElement(Object entry) {
476 return entry;
477 }
478
479 public Object getSnapshotElement(Object entry, int i) {
480 final List sn = (List) getSnapshot();
481 return sn.get(i);
482 }
483
484 public boolean equals(Object other) {
485 read();
486 return list.equals(other);
487 }
488
489 public int hashCode() {
490 read();
491 return list.hashCode();
492 }
493
494 public boolean entryExists(Object entry, int i) {
495 return entry!=null;
496 }
497
498 final class Clear implements DelayedOperation {
499 public void operate() {
500 list.clear();
501 }
502 public Object getAddedInstance() {
503 return null;
504 }
505 public Object getOrphan() {
506 throw new UnsupportedOperationException("queued clear cannot be used with orphan delete");
507 }
508 }
509
510 final class SimpleAdd implements DelayedOperation {
511 private Object value;
512
513 public SimpleAdd(Object value) {
514 this.value = value;
515 }
516 public void operate() {
517 list.add(value);
518 }
519 public Object getAddedInstance() {
520 return value;
521 }
522 public Object getOrphan() {
523 return null;
524 }
525 }
526
527 final class Add implements DelayedOperation {
528 private int index;
529 private Object value;
530
531 public Add(int index, Object value) {
532 this.index = index;
533 this.value = value;
534 }
535 public void operate() {
536 list.add(index, value);
537 }
538 public Object getAddedInstance() {
539 return value;
540 }
541 public Object getOrphan() {
542 return null;
543 }
544 }
545
546 final class Set implements DelayedOperation {
547 private int index;
548 private Object value;
549 private Object old;
550
551 public Set(int index, Object value, Object old) {
552 this.index = index;
553 this.value = value;
554 this.old = old;
555 }
556 public void operate() {
557 list.set(index, value);
558 }
559 public Object getAddedInstance() {
560 return value;
561 }
562 public Object getOrphan() {
563 return old;
564 }
565 }
566
567 final class Remove implements DelayedOperation {
568 private int index;
569 private Object old;
570
571 public Remove(int index, Object old) {
572 this.index = index;
573 this.old = old;
574 }
575 public void operate() {
576 list.remove(index);
577 }
578 public Object getAddedInstance() {
579 return null;
580 }
581 public Object getOrphan() {
582 return old;
583 }
584 }
585
586 final class SimpleRemove implements DelayedOperation {
587 private Object value;
588
589 public SimpleRemove(Object value) {
590 this.value = value;
591 }
592 public void operate() {
593 list.remove(value);
594 }
595 public Object getAddedInstance() {
596 return null;
597 }
598 public Object getOrphan() {
599 return value;
600 }
601 }
602 }