001package io.ebean.common;
002
003import io.ebean.bean.BeanCollection;
004import io.ebean.bean.BeanCollectionAdd;
005import io.ebean.bean.BeanCollectionLoader;
006import io.ebean.bean.EntityBean;
007
008import java.io.Serializable;
009import java.util.Collection;
010import java.util.Iterator;
011import java.util.LinkedHashSet;
012import java.util.Set;
013
014/**
015 * Set capable of lazy loading.
016 */
017public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E>, BeanCollectionAdd {
018
019  private static final long serialVersionUID = 1L;
020
021  /**
022   * The underlying Set implementation.
023   */
024  private Set<E> set;
025
026  /**
027   * Create with a specific Set implementation.
028   */
029  public BeanSet(Set<E> set) {
030    this.set = set;
031  }
032
033  /**
034   * Create using an underlying LinkedHashSet.
035   */
036  public BeanSet() {
037    this(new LinkedHashSet<>());
038  }
039
040  public BeanSet(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) {
041    super(loader, ownerBean, propertyName);
042  }
043
044  @Override
045  public void reset(EntityBean ownerBean, String propertyName) {
046    this.ownerBean = ownerBean;
047    this.propertyName = propertyName;
048    this.set = null;
049  }
050
051  @Override
052  public boolean isSkipSave() {
053    return set == null || (set.isEmpty() && !holdsModifications());
054  }
055
056  @Override
057  @SuppressWarnings("unchecked")
058  public void addEntityBean(EntityBean bean) {
059    set.add((E) bean);
060  }
061
062  @Override
063  @SuppressWarnings("unchecked")
064  public void loadFrom(BeanCollection<?> other) {
065    if (set == null) {
066      set = new LinkedHashSet<>();
067    }
068    set.addAll((Collection<? extends E>) other.getActualDetails());
069  }
070
071  @Override
072  public void internalAddWithCheck(Object bean) {
073    if (set == null || !set.contains(bean)) {
074      internalAdd(bean);
075    }
076  }
077
078  @Override
079  @SuppressWarnings("unchecked")
080  public void internalAdd(Object bean) {
081    if (set == null) {
082      set = new LinkedHashSet<>();
083    }
084    if (bean != null) {
085      set.add((E) bean);
086    }
087  }
088
089  /**
090   * Returns true if the underlying set has its data.
091   */
092  @Override
093  public boolean isPopulated() {
094    return set != null;
095  }
096
097  /**
098   * Return true if this is a reference (lazy loading) bean collection. This is
099   * the same as !isPopulated();
100   */
101  @Override
102  public boolean isReference() {
103    return set == null;
104  }
105
106  @Override
107  public boolean checkEmptyLazyLoad() {
108    if (set == null) {
109      set = new LinkedHashSet<>();
110      return true;
111    } else {
112      return false;
113    }
114  }
115
116  private void initClear() {
117    lock.lock();
118    try {
119      if (set == null) {
120        if (!disableLazyLoad && modifyListening) {
121          lazyLoadCollection(true);
122        } else {
123          set = new LinkedHashSet<>();
124        }
125      }
126    } finally {
127      lock.unlock();
128    }
129  }
130
131  private void init() {
132    lock.lock();
133    try {
134      if (set == null) {
135        if (disableLazyLoad) {
136          set = new LinkedHashSet<>();
137        } else {
138          lazyLoadCollection(true);
139        }
140      }
141    } finally {
142      lock.unlock();
143    }
144  }
145
146  /**
147   * Set the underlying set (used for lazy fetch).
148   */
149  @SuppressWarnings("unchecked")
150  public void setActualSet(Set<?> set) {
151    this.set = (Set<E>) set;
152  }
153
154  /**
155   * Return the actual underlying set.
156   */
157  public Set<E> getActualSet() {
158    return set;
159  }
160
161  @Override
162  public Collection<E> getActualDetails() {
163    return set;
164  }
165
166  @Override
167  public Collection<?> getActualEntries() {
168    return set;
169  }
170
171  @Override
172  public String toString() {
173    StringBuilder sb = new StringBuilder(50);
174    sb.append("BeanSet ");
175    if (isReadOnly()) {
176      sb.append("readOnly ");
177    }
178    if (set == null) {
179      sb.append("deferred ");
180
181    } else {
182      sb.append("size[").append(set.size()).append("]");
183      sb.append(" set").append(set);
184    }
185    return sb.toString();
186  }
187
188  /**
189   * Equal if obj is a Set and equal in a Set sense.
190   */
191  @Override
192  public boolean equals(Object obj) {
193    init();
194    return set.equals(obj);
195  }
196
197  @Override
198  public int hashCode() {
199    init();
200    return set.hashCode();
201  }
202
203  @Override
204  public void addBean(E bean) {
205    add(bean);
206  }
207
208  @Override
209  public void removeBean(E bean) {
210    if (set.remove(bean)) {
211      getModifyHolder().modifyRemoval(bean);
212    }
213  }
214
215  // -----------------------------------------------------//
216  // proxy method for map
217  // -----------------------------------------------------//
218
219  @Override
220  public boolean add(E o) {
221    checkReadOnly();
222    init();
223    if (modifyListening) {
224      if (set.add(o)) {
225        modifyAddition(o);
226        return true;
227      } else {
228        return false;
229      }
230    }
231    return set.add(o);
232  }
233
234  @Override
235  public boolean addAll(Collection<? extends E> addCollection) {
236    checkReadOnly();
237    init();
238    if (modifyListening) {
239      boolean changed = false;
240      for (E bean : addCollection) {
241        if (set.add(bean)) {
242          // register the addition of the bean
243          modifyAddition(bean);
244          changed = true;
245        }
246      }
247      return changed;
248    }
249    return set.addAll(addCollection);
250  }
251
252  @Override
253  public void clear() {
254    checkReadOnly();
255    initClear();
256    if (modifyListening) {
257      for (E bean : set) {
258        modifyRemoval(bean);
259      }
260    }
261    set.clear();
262  }
263
264  @Override
265  public boolean contains(Object o) {
266    init();
267    return set.contains(o);
268  }
269
270  @Override
271  public boolean containsAll(Collection<?> c) {
272    init();
273    return set.containsAll(c);
274  }
275
276  @Override
277  public boolean isEmpty() {
278    init();
279    return set.isEmpty();
280  }
281
282  @Override
283  public Iterator<E> iterator() {
284    init();
285    if (isReadOnly()) {
286      return new ReadOnlyIterator<>(set.iterator());
287    }
288    if (modifyListening) {
289      return new ModifyIterator<>(this, set.iterator());
290    }
291    return set.iterator();
292  }
293
294  @Override
295  public boolean remove(Object o) {
296    checkReadOnly();
297    init();
298    if (modifyListening) {
299      if (set.remove(o)) {
300        modifyRemoval(o);
301        return true;
302      }
303      return false;
304    }
305    return set.remove(o);
306  }
307
308  @Override
309  public boolean removeAll(Collection<?> beans) {
310    checkReadOnly();
311    init();
312    if (modifyListening) {
313      boolean changed = false;
314      for (Object bean : beans) {
315        if (set.remove(bean)) {
316          modifyRemoval(bean);
317          changed = true;
318        }
319      }
320      return changed;
321    }
322    return set.removeAll(beans);
323  }
324
325  @Override
326  public boolean retainAll(Collection<?> beans) {
327    checkReadOnly();
328    init();
329    if (modifyListening) {
330      boolean changed = false;
331      Iterator<?> it = set.iterator();
332      while (it.hasNext()) {
333        Object bean = it.next();
334        if (!beans.contains(bean)) {
335          // not retaining this bean so add it to the removal list
336          it.remove();
337          modifyRemoval(bean);
338          changed = true;
339        }
340      }
341      return changed;
342    }
343    return set.retainAll(beans);
344  }
345
346  @Override
347  public int size() {
348    init();
349    return set.size();
350  }
351
352  @Override
353  public Object[] toArray() {
354    init();
355    return set.toArray();
356  }
357
358  @Override
359  public <T> T[] toArray(T[] a) {
360    init();
361    //noinspection SuspiciousToArrayCall
362    return set.toArray(a);
363  }
364
365  private static class ReadOnlyIterator<E> implements Iterator<E>, Serializable {
366
367    private static final long serialVersionUID = 2577697326745352605L;
368
369    private final Iterator<E> it;
370
371    ReadOnlyIterator(Iterator<E> it) {
372      this.it = it;
373    }
374
375    @Override
376    public boolean hasNext() {
377      return it.hasNext();
378    }
379
380    @Override
381    public E next() {
382      return it.next();
383    }
384
385    @Override
386    public void remove() {
387      throw new IllegalStateException("This collection is in ReadOnly mode");
388    }
389  }
390
391  @Override
392  public BeanCollection<E> getShallowCopy() {
393    BeanSet<E> copy = new BeanSet<>(new LinkedHashSet<>(set));
394    copy.setFromOriginal(this);
395    return copy;
396  }
397}