001package com.avaje.ebean.common;
002
003import com.avaje.ebean.bean.BeanCollection;
004import com.avaje.ebean.bean.BeanCollectionAdd;
005import com.avaje.ebean.bean.BeanCollectionLoader;
006import com.avaje.ebean.bean.EntityBean;
007
008import java.io.Serializable;
009import java.util.ArrayList;
010import java.util.Collection;
011import java.util.Collections;
012import java.util.Iterator;
013import java.util.List;
014import java.util.ListIterator;
015
016/**
017 * List capable of lazy loading.
018 */
019public final class BeanList<E> extends AbstractBeanCollection<E> implements List<E>, BeanCollectionAdd {
020
021  private static final long serialVersionUID = 1L;
022
023  /**
024   * The underlying List implementation.
025   */
026  private List<E> list;
027
028  /**
029   * Specify the underlying List implementation.
030   */
031  public BeanList(List<E> list) {
032    super();
033    this.list = list;
034  }
035
036  /**
037   * Uses an ArrayList as the underlying List implementation.
038   */
039  public BeanList() {
040    this(new ArrayList<E>());
041  }
042
043  /**
044   * Used to create deferred fetch proxy.
045   */
046  public BeanList(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) {
047    super(loader, ownerBean, propertyName);
048  }
049
050  @Override
051  public void reset(EntityBean ownerBean, String propertyName) {
052    this.ownerBean = ownerBean;
053    this.propertyName = propertyName;
054    this.list = null;
055  }
056
057  @Override
058  public boolean isSkipSave() {
059    return list == null || (list.isEmpty() && !holdsModifications());
060  }
061
062  @SuppressWarnings("unchecked")
063  public void addEntityBean(EntityBean bean) {
064    list.add((E) bean);
065  }
066
067  @Override
068  @SuppressWarnings("unchecked")
069  public void loadFrom(BeanCollection<?> other) {
070    if (list == null) {
071      list = new ArrayList<E>();
072    }
073    list.addAll((Collection<? extends E>) other.getActualDetails());
074  }
075
076  @SuppressWarnings("unchecked")
077  public void internalAdd(Object bean) {
078    if (list == null) {
079      list = new ArrayList<E>();
080    }
081    if (bean != null) {
082      list.add((E) bean);
083    }
084  }
085
086  @Override
087  public void internalAddWithCheck(Object bean) {
088    if (list == null || !list.contains(bean)) {
089      internalAdd(bean);
090    }
091  }
092
093  public boolean checkEmptyLazyLoad() {
094    if (list == null) {
095      list = new ArrayList<E>();
096      return true;
097    } else {
098      return false;
099    }
100  }
101
102  private void initClear() {
103    synchronized (this) {
104      if (list == null) {
105        if (!disableLazyLoad && modifyListening) {
106          lazyLoadCollection(true);
107        } else {
108          list = new ArrayList<E>();
109        }
110      }
111    }
112  }
113
114  private void init() {
115    synchronized (this) {
116      if (list == null) {
117        if (disableLazyLoad) {
118          list = new ArrayList<E>();
119        } else {
120          lazyLoadCollection(false);
121        }
122      }
123    }
124  }
125
126  /**
127   * Set the actual underlying list.
128   * <p>
129   * This is primarily for the deferred fetching function.
130   * </p>
131   */
132  @SuppressWarnings("unchecked")
133  public void setActualList(List<?> list) {
134    this.list = (List<E>) list;
135  }
136
137  /**
138   * Return the actual underlying list.
139   */
140  public List<E> getActualList() {
141    return list;
142  }
143
144  public Collection<E> getActualDetails() {
145    return list;
146  }
147
148  @Override
149  public Collection<?> getActualEntries() {
150    return list;
151  }
152
153  /**
154   * Return true if the underlying list is populated.
155   */
156  public boolean isPopulated() {
157    return list != null;
158  }
159
160  /**
161   * Return true if this is a reference (lazy loading) bean collection. This is
162   * the same as !isPopulated();
163   */
164  public boolean isReference() {
165    return list == null;
166  }
167
168  public String toString() {
169    StringBuilder sb = new StringBuilder(50);
170    sb.append("BeanList ");
171    if (isReadOnly()) {
172      sb.append("readOnly ");
173    }
174    if (list == null) {
175      sb.append("deferred ");
176
177    } else {
178      sb.append("size[").append(list.size()).append("] ");
179      sb.append("list").append(list).append("");
180    }
181    return sb.toString();
182  }
183
184  /**
185   * Equal if obj is a List and equal in a list sense.
186   * <p>
187   * Specifically obj does not need to be a BeanList but any list. This does not
188   * use the FindMany, fetchedMaxRows or finishedFetch properties in the equals
189   * test.
190   * </p>
191   */
192  public boolean equals(Object obj) {
193    init();
194    return list.equals(obj);
195  }
196
197  public int hashCode() {
198    init();
199    return list.hashCode();
200  }
201
202  // -----------------------------------------------------//
203  // The additional methods are here
204  // -----------------------------------------------------//
205
206  // -----------------------------------------------------//
207  // proxy method for List
208  // -----------------------------------------------------//
209
210  public void add(int index, E element) {
211    checkReadOnly();
212    init();
213    if (modifyAddListening) {
214      modifyAddition(element);
215    }
216    list.add(index, element);
217  }
218
219  @Override
220  public void addBean(E bean) {
221    add(bean);
222  }
223
224  public boolean add(E o) {
225    checkReadOnly();
226    init();
227    if (modifyAddListening) {
228      if (list.add(o)) {
229        modifyAddition(o);
230        return true;
231      } else {
232        return false;
233      }
234    }
235    return list.add(o);
236  }
237
238  public boolean addAll(Collection<? extends E> c) {
239    checkReadOnly();
240    init();
241    if (modifyAddListening) {
242      // all elements in c are added (no contains checking)
243      getModifyHolder().modifyAdditionAll(c);
244    }
245    return list.addAll(c);
246  }
247
248  public boolean addAll(int index, Collection<? extends E> c) {
249    checkReadOnly();
250    init();
251    if (modifyAddListening) {
252      // all elements in c are added (no contains checking)
253      getModifyHolder().modifyAdditionAll(c);
254    }
255    return list.addAll(index, c);
256  }
257
258  public void clear() {
259    checkReadOnly();
260    // TODO: when clear() and not initialised could be more clever
261    // and fetch just the Id's
262    initClear();
263    if (modifyRemoveListening) {
264      for (int i = 0; i < list.size(); i++) {
265        getModifyHolder().modifyRemoval(list.get(i));
266      }
267    }
268    list.clear();
269  }
270
271  public boolean contains(Object o) {
272    init();
273    return list.contains(o);
274  }
275
276  public boolean containsAll(Collection<?> c) {
277    init();
278    return list.containsAll(c);
279  }
280
281  public E get(int index) {
282    init();
283    return list.get(index);
284  }
285
286  public int indexOf(Object o) {
287    init();
288    return list.indexOf(o);
289  }
290
291  public boolean isEmpty() {
292    init();
293    return list.isEmpty();
294  }
295
296  public Iterator<E> iterator() {
297    init();
298    if (isReadOnly()) {
299      return new ReadOnlyListIterator<E>(list.listIterator());
300    }
301    if (modifyListening) {
302      Iterator<E> it = list.iterator();
303      return new ModifyIterator<E>(this, it);
304    }
305    return list.iterator();
306  }
307
308  public int lastIndexOf(Object o) {
309    init();
310    return list.lastIndexOf(o);
311  }
312
313  public ListIterator<E> listIterator() {
314    init();
315    if (isReadOnly()) {
316      return new ReadOnlyListIterator<E>(list.listIterator());
317    }
318    if (modifyListening) {
319      ListIterator<E> it = list.listIterator();
320      return new ModifyListIterator<E>(this, it);
321    }
322    return list.listIterator();
323  }
324
325  public ListIterator<E> listIterator(int index) {
326    init();
327    if (isReadOnly()) {
328      return new ReadOnlyListIterator<E>(list.listIterator(index));
329    }
330    if (modifyListening) {
331      ListIterator<E> it = list.listIterator(index);
332      return new ModifyListIterator<E>(this, it);
333    }
334    return list.listIterator(index);
335  }
336
337  @Override
338  public void removeBean(E bean) {
339    if (list.remove(bean)) {
340      getModifyHolder().modifyRemoval(bean);
341    }
342  }
343
344  public E remove(int index) {
345    checkReadOnly();
346    init();
347    if (modifyRemoveListening) {
348      E o = list.remove(index);
349      modifyRemoval(o);
350      return o;
351    }
352    return list.remove(index);
353  }
354
355  public boolean remove(Object o) {
356    checkReadOnly();
357    init();
358    if (modifyRemoveListening) {
359      boolean isRemove = list.remove(o);
360      if (isRemove) {
361        modifyRemoval(o);
362      }
363      return isRemove;
364    }
365    return list.remove(o);
366  }
367
368  public boolean removeAll(Collection<?> beans) {
369    checkReadOnly();
370    init();
371    if (modifyRemoveListening) {
372      boolean changed = false;
373      for (Object bean : beans) {
374        if (list.remove(bean)) {
375          // register this bean as having been removed
376          modifyRemoval(bean);
377          changed = true;
378        }
379      }
380      return changed;
381    }
382    return list.removeAll(beans);
383  }
384
385  public boolean retainAll(Collection<?> retainBeans) {
386    checkReadOnly();
387    init();
388    if (modifyRemoveListening) {
389      boolean changed = false;
390      Iterator<E> it = list.iterator();
391      while (it.hasNext()) {
392        Object bean = it.next();
393        if (!retainBeans.contains(bean)) {
394          // removing this bean
395          it.remove();
396          modifyRemoval(bean);
397          changed = true;
398        }
399      }
400      return changed;
401    }
402    return list.retainAll(retainBeans);
403  }
404
405  public E set(int index, E element) {
406    checkReadOnly();
407    init();
408    if (modifyListening) {
409      E o = list.set(index, element);
410      modifyAddition(element);
411      modifyRemoval(o);
412      return o;
413    }
414    return list.set(index, element);
415  }
416
417  public int size() {
418    init();
419    return list.size();
420  }
421
422  public List<E> subList(int fromIndex, int toIndex) {
423    init();
424    if (isReadOnly()) {
425      return Collections.unmodifiableList(list.subList(fromIndex, toIndex));
426    }
427    if (modifyListening) {
428      return new ModifyList<E>(this, list.subList(fromIndex, toIndex));
429    }
430    return list.subList(fromIndex, toIndex);
431  }
432
433  public Object[] toArray() {
434    init();
435    return list.toArray();
436  }
437
438  public <T> T[] toArray(T[] a) {
439    init();
440    //noinspection SuspiciousToArrayCall
441    return list.toArray(a);
442  }
443
444  private static class ReadOnlyListIterator<E> implements ListIterator<E>, Serializable {
445
446    private static final long serialVersionUID = 3097271091406323699L;
447
448    private final ListIterator<E> i;
449
450    ReadOnlyListIterator(ListIterator<E> i) {
451      this.i = i;
452    }
453
454    public void add(E o) {
455      throw new IllegalStateException("This collection is in ReadOnly mode");
456    }
457
458    public void remove() {
459      throw new IllegalStateException("This collection is in ReadOnly mode");
460    }
461
462    public void set(E o) {
463      throw new IllegalStateException("This collection is in ReadOnly mode");
464    }
465
466    public boolean hasNext() {
467      return i.hasNext();
468    }
469
470    public boolean hasPrevious() {
471      return i.hasPrevious();
472    }
473
474    public E next() {
475      return i.next();
476    }
477
478    public int nextIndex() {
479      return i.nextIndex();
480    }
481
482    public E previous() {
483      return i.previous();
484    }
485
486    public int previousIndex() {
487      return i.previousIndex();
488    }
489
490  }
491}