001package com.avaje.ebean.common;
002
003import java.util.Set;
004
005import javax.persistence.PersistenceException;
006
007import com.avaje.ebean.Ebean;
008import com.avaje.ebean.ExpressionList;
009import com.avaje.ebean.bean.BeanCollection;
010import com.avaje.ebean.bean.BeanCollectionLoader;
011import com.avaje.ebean.bean.BeanCollectionTouched;
012import com.avaje.ebean.bean.EntityBean;
013
014/**
015 * Base class for List Set and Map implementations of BeanCollection.
016 * 
017 * @author rbygrave
018 */
019public abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
020
021  private static final long serialVersionUID = 3365725236140187588L;
022
023  protected boolean readOnly;
024
025  /**
026   * The EbeanServer this is associated with. (used for lazy fetch).
027   */
028  protected transient BeanCollectionLoader loader;
029
030  protected transient ExpressionList<?> filterMany;
031
032  /**
033   * Flag set when registered with the batch loading context.
034   */
035  protected boolean registeredWithLoadContext;
036
037  protected String ebeanServerName;
038
039  protected transient BeanCollectionTouched beanCollectionTouched;
040
041  /**
042   * The owning bean (used for lazy fetch).
043   */
044  protected EntityBean ownerBean;
045
046  /**
047   * The name of this property in the owning bean (used for lazy fetch).
048   */
049  protected String propertyName;
050
051  protected ModifyHolder<E> modifyHolder;
052
053  protected ModifyListenMode modifyListenMode;
054  protected boolean modifyAddListening;
055  protected boolean modifyRemoveListening;
056  protected boolean modifyListening;
057
058  /**
059   * Flag used to tell if empty collections have been cleared etc or just
060   * uninitialised.
061   */
062  protected boolean touched;
063
064  /**
065   * Constructor not non-lazy loading collection.
066   */
067  public AbstractBeanCollection() {
068  }
069
070  /**
071   * Used to create deferred fetch proxy.
072   */
073  public AbstractBeanCollection(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) {
074    this.loader = loader;
075    this.ebeanServerName = loader.getName();
076    this.ownerBean = ownerBean;
077    this.propertyName = propertyName;
078    this.readOnly = ownerBean._ebean_getIntercept().isReadOnly();
079  }
080
081  public EntityBean getOwnerBean() {
082    return ownerBean;
083  }
084
085  public String getPropertyName() {
086    return propertyName;
087  }
088
089  public ExpressionList<?> getFilterMany() {
090    return filterMany;
091  }
092
093  public void setFilterMany(ExpressionList<?> filterMany) {
094    this.filterMany = filterMany;
095  }
096
097  protected void lazyLoadCollection(boolean onlyIds) {
098    if (loader == null) {
099      loader = (BeanCollectionLoader) Ebean.getServer(ebeanServerName);
100    }
101    if (loader == null) {
102      String msg = "Lazy loading but LazyLoadEbeanServer is null?"
103          + " The LazyLoadEbeanServer needs to be set after deserialization"
104          + " to support lazy loading.";
105      throw new PersistenceException(msg);
106    }
107
108    loader.loadMany(this, onlyIds);
109    checkEmptyLazyLoad();
110  }
111
112  /**
113   * Set touched. If setFlag is false then typically an isEmpty() call and still
114   * considering that to be untouched.
115   */
116  protected void touched(boolean setFlag) {
117    if (setFlag) {
118      touched = true;
119    }
120    if (beanCollectionTouched != null) {
121      // only call this once
122      beanCollectionTouched.notifyTouched(this);
123      beanCollectionTouched = null;
124    }
125  }
126
127  public void setBeanCollectionTouched(BeanCollectionTouched notify) {
128    this.beanCollectionTouched = notify;
129  }
130
131  public boolean isRegisteredWithLoadContext() {
132    return registeredWithLoadContext;
133  }
134
135  public void setLoader(BeanCollectionLoader loader) {
136    this.registeredWithLoadContext = true;
137    this.loader = loader;
138    this.ebeanServerName = loader.getName();
139  }
140
141  public boolean isReadOnly() {
142    return readOnly;
143  }
144
145  public void setReadOnly(boolean readOnly) {
146    this.readOnly = readOnly;
147  }
148
149  protected void checkReadOnly() {
150    if (readOnly) {
151      String msg = "This collection is in ReadOnly mode";
152      throw new IllegalStateException(msg);
153    }
154  }
155
156  // ---------------------------------------------------------
157  // Support for modify additions deletions etc - ManyToMany
158  // ---------------------------------------------------------
159
160  /**
161   * set modifyListening to be on or off.
162   */
163  public void setModifyListening(ModifyListenMode mode) {
164
165    this.modifyListenMode = mode;
166    this.modifyAddListening = ModifyListenMode.ALL.equals(mode);
167    this.modifyRemoveListening = modifyAddListening || ModifyListenMode.REMOVALS.equals(mode);
168    this.modifyListening = modifyRemoveListening || modifyAddListening;
169    if (modifyListening) {
170      // lose any existing modifications
171      modifyHolder = null;
172    }
173  }
174
175  /**
176   * Return the modify listening mode this collection is using.
177   */
178  public ModifyListenMode getModifyListenMode() {
179    return modifyListenMode;
180  }
181
182  protected ModifyHolder<E> getModifyHolder() {
183    if (modifyHolder == null) {
184      modifyHolder = new ModifyHolder<E>();
185    }
186    return modifyHolder;
187  }
188
189  public void modifyAddition(E bean) {
190    if (modifyAddListening) {
191      getModifyHolder().modifyAddition(bean);
192    }
193  }
194
195  public void modifyRemoval(Object bean) {
196    if (modifyRemoveListening) {
197      getModifyHolder().modifyRemoval(bean);
198    }
199  }
200
201  public void modifyReset() {
202    if (modifyHolder != null) {
203      modifyHolder.reset();
204    }
205  }
206
207  public Set<E> getModifyAdditions() {
208    if (modifyHolder == null) {
209      return null;
210    } else {
211      return modifyHolder.getModifyAdditions();
212    }
213  }
214
215  public Set<E> getModifyRemovals() {
216    if (modifyHolder == null) {
217      return null;
218    } else {
219      return modifyHolder.getModifyRemovals();
220    }
221  }
222}