001package io.ebean.bean;
002
003import io.ebean.DB;
004import io.ebean.Database;
005import io.ebean.ValuePair;
006
007import javax.persistence.EntityNotFoundException;
008import javax.persistence.PersistenceException;
009import java.io.Serializable;
010import java.math.BigDecimal;
011import java.net.URL;
012import java.util.*;
013import java.util.concurrent.locks.Lock;
014import java.util.concurrent.locks.ReentrantLock;
015
016/**
017 * This is the object added to every entity bean using byte code enhancement.
018 * <p>
019 * This provides the mechanisms to support deferred fetching of reference beans
020 * and oldValues generation for concurrency checking.
021 * </p>
022 */
023public final class EntityBeanIntercept implements Serializable {
024
025  private static final long serialVersionUID = -3664031775464862649L;
026
027  private static final int STATE_NEW = 0;
028  private static final int STATE_REFERENCE = 1;
029  private static final int STATE_LOADED = 2;
030
031  /**
032   * Used when a bean is partially filled.
033   */
034  private static final byte FLAG_LOADED_PROP = 1;
035  private static final byte FLAG_CHANGED_PROP = 2;
036  private static final byte FLAG_CHANGEDLOADED_PROP = 3;
037  /**
038   * Flags indicating if a property is a dirty embedded bean. Used to distinguish
039   * between an embedded bean being completely overwritten and one of its
040   * embedded properties being made dirty.
041   */
042  private static final byte FLAG_EMBEDDED_DIRTY = 4;
043  /**
044   * Flags indicating if a property is a dirty embedded bean. Used to distinguish
045   * between an embedded bean being completely overwritten and one of its
046   * embedded properties being made dirty.
047   */
048  private static final byte FLAG_ORIG_VALUE_SET = 8;
049
050  /**
051   * Flags indicating if the mutable hash is set.
052   */
053  private static final byte FLAG_MUTABLE_HASH_SET = 16;
054
055  private transient final ReentrantLock lock = new ReentrantLock();
056  private transient NodeUsageCollector nodeUsageCollector;
057  private transient PersistenceContext persistenceContext;
058  private transient BeanLoader beanLoader;
059  private transient PreGetterCallback preGetterCallback;
060
061  private String ebeanServerName;
062  private boolean deletedFromCollection;
063
064  /**
065   * The actual entity bean that 'owns' this intercept.
066   */
067  private final EntityBean owner;
068  private EntityBean embeddedOwner;
069  private int embeddedOwnerIndex;
070  /**
071   * One of NEW, REF, UPD.
072   */
073  private int state;
074  private boolean forceUpdate;
075  private boolean readOnly;
076  private boolean dirty;
077  /**
078   * Flag set to disable lazy loading - typically for SQL "report" type entity beans.
079   */
080  private boolean disableLazyLoad;
081
082  /**
083   * Flag set when lazy loading failed due to the underlying bean being deleted in the DB.
084   */
085  private boolean lazyLoadFailure;
086  private boolean fullyLoadedBean;
087  private boolean loadedFromCache;
088  private final byte[] flags;
089  private Object[] origValues;
090  private Exception[] loadErrors;
091  private int lazyLoadProperty = -1;
092  private Object ownerId;
093  private int sortOrder;
094
095  /**
096   * Holds information of json loaded jackson beans (e.g. the original json or checksum).
097   */
098  private MutableValueInfo[] mutableInfo;
099
100  /**
101   * Holds json content determined at point of dirty check.
102   * Stored here on dirty check such that we only convert to json once.
103   */
104  private MutableValueNext[] mutableNext;
105
106  /**
107   * Create a intercept with a given entity.
108   */
109  public EntityBeanIntercept(Object ownerBean) {
110    this.owner = (EntityBean) ownerBean;
111    this.flags = new byte[owner._ebean_getPropertyNames().length];
112  }
113
114  /**
115   * EXPERIMENTAL - Constructor only for use by serialization frameworks.
116   */
117  public EntityBeanIntercept() {
118    this.owner = null;
119    this.flags = null;
120  }
121
122  /**
123   * Return the 'owning' entity bean.
124   */
125  public EntityBean getOwner() {
126    return owner;
127  }
128
129  /**
130   * Return the persistenceContext.
131   */
132  public PersistenceContext getPersistenceContext() {
133    return persistenceContext;
134  }
135
136  /**
137   * Set the persistenceContext.
138   */
139  public void setPersistenceContext(PersistenceContext persistenceContext) {
140    this.persistenceContext = persistenceContext;
141  }
142
143  /**
144   * Turn on profile collection.
145   */
146  public void setNodeUsageCollector(NodeUsageCollector usageCollector) {
147    this.nodeUsageCollector = usageCollector;
148  }
149
150  /**
151   * Return the ownerId (IdClass).
152   */
153  public Object getOwnerId() {
154    return ownerId;
155  }
156
157  /**
158   * Set the ownerId (IdClass).
159   */
160  public void setOwnerId(Object ownerId) {
161    this.ownerId = ownerId;
162  }
163
164  /**
165   * Return the owning bean for an embedded bean.
166   */
167  public Object getEmbeddedOwner() {
168    return embeddedOwner;
169  }
170
171  /**
172   * Return the property index (for the parent) of this embedded bean.
173   */
174  public int getEmbeddedOwnerIndex() {
175    return embeddedOwnerIndex;
176  }
177
178  /**
179   * Clear the getter callback.
180   */
181  public void clearGetterCallback() {
182    this.preGetterCallback = null;
183  }
184
185  /**
186   * Register the callback to be triggered when getter is called.
187   * This is used primarily to automatically flush the JDBC batch.
188   */
189  public void registerGetterCallback(PreGetterCallback getterCallback) {
190    this.preGetterCallback = getterCallback;
191  }
192
193  /**
194   * Set the embedded beans owning bean.
195   */
196  public void setEmbeddedOwner(EntityBean parentBean, int embeddedOwnerIndex) {
197    this.embeddedOwner = parentBean;
198    this.embeddedOwnerIndex = embeddedOwnerIndex;
199  }
200
201  /**
202   * Set the BeanLoader with PersistenceContext.
203   */
204  public void setBeanLoader(BeanLoader beanLoader, PersistenceContext ctx) {
205    this.beanLoader = beanLoader;
206    this.persistenceContext = ctx;
207    this.ebeanServerName = beanLoader.getName();
208  }
209
210  /**
211   * Set the BeanLoader.
212   */
213  public void setBeanLoader(BeanLoader beanLoader) {
214    this.beanLoader = beanLoader;
215    this.ebeanServerName = beanLoader.getName();
216  }
217
218  public boolean isFullyLoadedBean() {
219    return fullyLoadedBean;
220  }
221
222  public void setFullyLoadedBean(boolean fullyLoadedBean) {
223    this.fullyLoadedBean = fullyLoadedBean;
224  }
225
226  /**
227   * Check each property to see if the bean is partially loaded.
228   */
229  public boolean isPartial() {
230    for (byte flag : flags) {
231      if ((flag & FLAG_LOADED_PROP) == 0) {
232        return true;
233      }
234    }
235    return false;
236  }
237
238  /**
239   * Return true if this bean has been directly modified (it has oldValues) or
240   * if any embedded beans are either new or dirty (and hence need saving).
241   */
242  public boolean isDirty() {
243    if (dirty) {
244      return true;
245    }
246    if (mutableInfo != null) {
247      for (int i = 0; i < mutableInfo.length; i++) {
248        if (mutableInfo[i] != null && !mutableInfo[i].isEqualToObject(owner._ebean_getField(i))) {
249          dirty = true;
250          break;
251        }
252      }
253    }
254    return dirty;
255  }
256
257  /**
258   * Called by an embedded bean onto its owner.
259   */
260  public void setEmbeddedDirty(int embeddedProperty) {
261    this.dirty = true;
262    setEmbeddedPropertyDirty(embeddedProperty);
263  }
264
265  public void setDirty(boolean dirty) {
266    this.dirty = dirty;
267  }
268
269  /**
270   * Return true if this entity bean is new and not yet saved.
271   */
272  public boolean isNew() {
273    return state == STATE_NEW;
274  }
275
276  /**
277   * Return true if the entity bean is new or dirty (and should be saved).
278   */
279  public boolean isNewOrDirty() {
280    return isNew() || isDirty();
281  }
282
283  /**
284   * Return true if only the Id property has been loaded.
285   */
286  public boolean hasIdOnly(int idIndex) {
287    for (int i = 0; i < flags.length; i++) {
288      if (i == idIndex) {
289        if ((flags[i] & FLAG_LOADED_PROP) == 0) return false;
290      } else if ((flags[i] & FLAG_LOADED_PROP) != 0) {
291        return false;
292      }
293    }
294    return true;
295  }
296
297  /**
298   * Return true if the entity is a reference.
299   */
300  public boolean isReference() {
301    return state == STATE_REFERENCE;
302  }
303
304  /**
305   * Set this as a reference object.
306   */
307  public void setReference(int idPos) {
308    state = STATE_REFERENCE;
309    if (idPos > -1) {
310      // For cases where properties are set on constructor
311      // set every non Id property to unloaded (for lazy loading)
312      for (int i = 0; i < flags.length; i++) {
313        if (i != idPos) {
314          flags[i] &= ~FLAG_LOADED_PROP;
315        }
316      }
317    }
318  }
319
320  /**
321   * Set true when the bean has been loaded from L2 bean cache.
322   * The effect of this is that we should skip the cache if there
323   * is subsequent lazy loading (bean cache partially populated).
324   */
325  public void setLoadedFromCache(boolean loadedFromCache) {
326    this.loadedFromCache = loadedFromCache;
327  }
328
329  /**
330   * Return true if this bean was loaded from L2 bean cache.
331   */
332  public boolean isLoadedFromCache() {
333    return loadedFromCache;
334  }
335
336  /**
337   * Return true if the bean should be treated as readOnly. If a setter method
338   * is called when it is readOnly an Exception is thrown.
339   */
340  public boolean isReadOnly() {
341    return readOnly;
342  }
343
344  /**
345   * Set the readOnly status. If readOnly then calls to setter methods through
346   * an exception.
347   */
348  public void setReadOnly(boolean readOnly) {
349    this.readOnly = readOnly;
350  }
351
352  /**
353   * Set the bean to be updated when persisted (for merge).
354   */
355  public void setForceUpdate(boolean forceUpdate) {
356    this.forceUpdate = forceUpdate;
357  }
358
359  /**
360   * Return true if the entity should be updated.
361   */
362  public boolean isUpdate() {
363    return forceUpdate || state == STATE_LOADED || state == STATE_REFERENCE;
364  }
365
366  /**
367   * Return true if the entity has been loaded.
368   */
369  public boolean isLoaded() {
370    return state == STATE_LOADED;
371  }
372
373  /**
374   * Set the bean into NEW state.
375   */
376  public void setNew() {
377    this.state = STATE_NEW;
378  }
379
380  /**
381   * Set the loaded state to true.
382   * <p>
383   * Calls to setter methods after the bean is loaded can result in
384   * 'Old Values' being created.
385   * <p>
386   * Worth noting that this is also set after a insert/update. By doing so it
387   * 'resets' the bean for making further changes and saving again.
388   */
389  public void setLoaded() {
390    this.state = STATE_LOADED;
391    this.owner._ebean_setEmbeddedLoaded();
392    this.lazyLoadProperty = -1;
393    this.origValues = null;
394    // after save, transfer the mutable next values back to mutable info
395    if (mutableNext != null) {
396      for (int i = 0; i < mutableNext.length; i++) {
397        MutableValueNext next = mutableNext[i];
398        if (next != null) {
399          mutableInfo(i, next.info());
400        }
401      }
402    }
403    this.mutableNext = null;
404    for (int i = 0; i < flags.length; i++) {
405      flags[i] &= ~(FLAG_CHANGED_PROP | FLAG_ORIG_VALUE_SET);
406    }
407    this.dirty = false;
408  }
409
410  /**
411   * When finished loading for lazy or refresh on an already partially populated bean.
412   */
413  public void setLoadedLazy() {
414    this.state = STATE_LOADED;
415    this.lazyLoadProperty = -1;
416  }
417
418  /**
419   * Set lazy load failure flag.
420   */
421  public void setLazyLoadFailure(Object ownerId) {
422    this.lazyLoadFailure = true;
423    this.ownerId = ownerId;
424  }
425
426  /**
427   * Return true if the bean is marked as having failed lazy loading.
428   */
429  public boolean isLazyLoadFailure() {
430    return lazyLoadFailure;
431  }
432
433  /**
434   * Return true if lazy loading is disabled.
435   */
436  public boolean isDisableLazyLoad() {
437    return disableLazyLoad;
438  }
439
440  /**
441   * Set true to turn off lazy loading.
442   */
443  public void setDisableLazyLoad(boolean disableLazyLoad) {
444    this.disableLazyLoad = disableLazyLoad;
445  }
446
447  /**
448   * Set the loaded status for the embedded bean.
449   */
450  public void setEmbeddedLoaded(Object embeddedBean) {
451    if (embeddedBean instanceof EntityBean) {
452      EntityBean eb = (EntityBean) embeddedBean;
453      eb._ebean_getIntercept().setLoaded();
454    }
455  }
456
457  /**
458   * Return true if the embedded bean is new or dirty and hence needs saving.
459   */
460  public boolean isEmbeddedNewOrDirty(Object embeddedBean) {
461    if (embeddedBean == null) {
462      // if it was previously set then the owning bean would
463      // have oldValues containing the previous embedded bean
464      return false;
465    }
466    if (embeddedBean instanceof EntityBean) {
467      return ((EntityBean) embeddedBean)._ebean_getIntercept().isNewOrDirty();
468    } else {
469      // non-enhanced so must assume it is new and needs to be saved
470      return true;
471    }
472  }
473
474  /**
475   * Return the original value that was changed via an update.
476   */
477  public Object getOrigValue(int propertyIndex) {
478    if ((flags[propertyIndex] & (FLAG_ORIG_VALUE_SET | FLAG_MUTABLE_HASH_SET)) == FLAG_MUTABLE_HASH_SET) {
479      // mutable hash set, but not ORIG_VALUE
480      setOriginalValue(propertyIndex, mutableInfo[propertyIndex].get());
481    }
482    if (origValues == null) {
483      return null;
484    }
485    return origValues[propertyIndex];
486  }
487
488  /**
489   * Finds the index position of a given property. Returns -1 if the
490   * property can not be found.
491   */
492  public int findProperty(String propertyName) {
493    String[] names = owner._ebean_getPropertyNames();
494    for (int i = 0; i < names.length; i++) {
495      if (names[i].equals(propertyName)) {
496        return i;
497      }
498    }
499    return -1;
500  }
501
502  /**
503   * Return the property name for the given property.
504   */
505  public String getProperty(int propertyIndex) {
506    if (propertyIndex == -1) {
507      return null;
508    }
509    return owner._ebean_getPropertyName(propertyIndex);
510  }
511
512  /**
513   * Return the number of properties.
514   */
515  public int getPropertyLength() {
516    return flags.length;
517  }
518
519  /**
520   * Set the loaded state of the property given it's name.
521   */
522  public void setPropertyLoaded(String propertyName, boolean loaded) {
523    int position = findProperty(propertyName);
524    if (position == -1) {
525      throw new IllegalArgumentException("Property " + propertyName + " not found");
526    }
527    if (loaded) {
528      flags[position] |= FLAG_LOADED_PROP;
529    } else {
530      flags[position] &= ~FLAG_LOADED_PROP;
531    }
532  }
533
534  /**
535   * Set the property to be treated as unloaded. Used for properties initialised in default constructor.
536   */
537  public void setPropertyUnloaded(int propertyIndex) {
538    flags[propertyIndex] &= ~FLAG_LOADED_PROP;
539  }
540
541  /**
542   * Set the property to be loaded.
543   */
544  public void setLoadedProperty(int propertyIndex) {
545    flags[propertyIndex] |= FLAG_LOADED_PROP;
546  }
547
548  /**
549   * Set all properties to be loaded (post insert).
550   */
551  public void setLoadedPropertyAll() {
552    for (int i = 0; i < flags.length; i++) {
553      flags[i] |= FLAG_LOADED_PROP;
554    }
555  }
556
557  /**
558   * Return true if the property is loaded.
559   */
560  public boolean isLoadedProperty(int propertyIndex) {
561    return (flags[propertyIndex] & FLAG_LOADED_PROP) != 0;
562  }
563
564  /**
565   * Return true if the property is considered changed.
566   */
567  public boolean isChangedProperty(int propertyIndex) {
568    return (flags[propertyIndex] & FLAG_CHANGED_PROP) != 0;
569  }
570
571  /**
572   * Return true if the property was changed or if it is embedded and one of its
573   * embedded properties is dirty.
574   */
575  public boolean isDirtyProperty(int propertyIndex) {
576    return (flags[propertyIndex] & (FLAG_CHANGED_PROP + FLAG_EMBEDDED_DIRTY)) != 0;
577  }
578
579  /**
580   * Explicitly mark a property as having been changed.
581   */
582  public void markPropertyAsChanged(int propertyIndex) {
583    setChangedProperty(propertyIndex);
584    setDirty(true);
585  }
586
587  public void setChangedProperty(int propertyIndex) {
588    flags[propertyIndex] |= FLAG_CHANGED_PROP;
589  }
590
591  private void setChangeLoaded(int propertyIndex) {
592    flags[propertyIndex] |= FLAG_CHANGEDLOADED_PROP;
593  }
594
595  /**
596   * Set that an embedded bean has had one of its properties changed.
597   */
598  private void setEmbeddedPropertyDirty(int propertyIndex) {
599    flags[propertyIndex] |= FLAG_EMBEDDED_DIRTY;
600  }
601
602  private void setOriginalValue(int propertyIndex, Object value) {
603    if (origValues == null) {
604      origValues = new Object[flags.length];
605    }
606    if ((flags[propertyIndex] & FLAG_ORIG_VALUE_SET) == 0) {
607      flags[propertyIndex] |= FLAG_ORIG_VALUE_SET;
608      origValues[propertyIndex] = value;
609    }
610  }
611
612  /**
613   * Set old value but force it to be set regardless if it already has a value.
614   */
615  private void setOriginalValueForce(int propertyIndex, Object value) {
616    if (origValues == null) {
617      origValues = new Object[flags.length];
618    }
619    origValues[propertyIndex] = value;
620  }
621
622  /**
623   * For forced update on a 'New' bean set all the loaded properties to changed.
624   */
625  public void setNewBeanForUpdate() {
626    for (int i = 0; i < flags.length; i++) {
627      if ((flags[i] & FLAG_LOADED_PROP) != 0) {
628        flags[i] |= FLAG_CHANGED_PROP;
629      }
630    }
631    setDirty(true);
632  }
633
634  /**
635   * Return the set of property names for a partially loaded bean.
636   */
637  public Set<String> getLoadedPropertyNames() {
638    if (fullyLoadedBean) {
639      return null;
640    }
641    Set<String> props = new LinkedHashSet<>();
642    for (int i = 0; i < flags.length; i++) {
643      if ((flags[i] & FLAG_LOADED_PROP) != 0) {
644        props.add(getProperty(i));
645      }
646    }
647    return props;
648  }
649
650  /**
651   * Return the array of flags indicating the dirty properties.
652   */
653  public boolean[] getDirtyProperties() {
654    int len = getPropertyLength();
655    boolean[] dirties = new boolean[len];
656    for (int i = 0; i < len; i++) {
657      // this, or an embedded property has been changed - recurse
658      dirties[i] = (flags[i] & (FLAG_CHANGED_PROP + FLAG_EMBEDDED_DIRTY)) != 0;
659    }
660    return dirties;
661  }
662
663  /**
664   * Return the set of dirty properties.
665   */
666  public Set<String> getDirtyPropertyNames() {
667    Set<String> props = new LinkedHashSet<>();
668    addDirtyPropertyNames(props, null);
669    return props;
670  }
671
672  /**
673   * Recursively add dirty properties.
674   */
675  public void addDirtyPropertyNames(Set<String> props, String prefix) {
676    int len = getPropertyLength();
677    for (int i = 0; i < len; i++) {
678      if (isChangedProp(i)) {
679        // the property has been changed on this bean
680        props.add((prefix == null ? getProperty(i) : prefix + getProperty(i)));
681      } else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
682        // an embedded property has been changed - recurse
683        EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
684        embeddedBean._ebean_getIntercept().addDirtyPropertyNames(props, getProperty(i) + ".");
685      }
686    }
687  }
688
689  /**
690   * Return true if any of the given property names are dirty.
691   */
692  public boolean hasDirtyProperty(Set<String> propertyNames) {
693    String[] names = owner._ebean_getPropertyNames();
694    int len = getPropertyLength();
695    for (int i = 0; i < len; i++) {
696      if (isChangedProp(i)) {
697        if (propertyNames.contains(names[i])) {
698          return true;
699        }
700      } else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
701        if (propertyNames.contains(names[i])) {
702          return true;
703        }
704      }
705    }
706    return false;
707  }
708
709  /**
710   * Return a map of dirty properties with their new and old values.
711   */
712  public Map<String, ValuePair> getDirtyValues() {
713    Map<String, ValuePair> dirtyValues = new LinkedHashMap<>();
714    addDirtyPropertyValues(dirtyValues, null);
715    return dirtyValues;
716  }
717
718  /**
719   * Recursively add dirty properties.
720   */
721  public void addDirtyPropertyValues(Map<String, ValuePair> dirtyValues, String prefix) {
722    int len = getPropertyLength();
723    for (int i = 0; i < len; i++) {
724      if (isChangedProp(i)) {
725        // the property has been changed on this bean
726        String propName = (prefix == null ? getProperty(i) : prefix + getProperty(i));
727        Object newVal = owner._ebean_getField(i);
728        Object oldVal = getOrigValue(i);
729        if (notEqual(oldVal, newVal)) {
730          dirtyValues.put(propName, new ValuePair(newVal, oldVal));
731        }
732      } else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
733        // an embedded property has been changed - recurse
734        EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
735        embeddedBean._ebean_getIntercept().addDirtyPropertyValues(dirtyValues, getProperty(i) + ".");
736      }
737    }
738  }
739
740  /**
741   * Recursively add dirty properties.
742   */
743  public void addDirtyPropertyValues(BeanDiffVisitor visitor) {
744    int len = getPropertyLength();
745    for (int i = 0; i < len; i++) {
746      if (isChangedProp(i)) {
747        // the property has been changed on this bean
748        Object newVal = owner._ebean_getField(i);
749        Object oldVal = getOrigValue(i);
750        if (notEqual(oldVal, newVal)) {
751          visitor.visit(i, newVal, oldVal);
752        }
753      } else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
754        // an embedded property has been changed - recurse
755        EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
756        visitor.visitPush(i);
757        embeddedBean._ebean_getIntercept().addDirtyPropertyValues(visitor);
758        visitor.visitPop();
759      }
760    }
761  }
762
763  /**
764   * Return a dirty property hash taking into account embedded beans.
765   */
766  public StringBuilder getDirtyPropertyKey() {
767    StringBuilder sb = new StringBuilder();
768    addDirtyPropertyKey(sb);
769    return sb;
770  }
771
772  /**
773   * Add and return a dirty property hash.
774   */
775  private void addDirtyPropertyKey(StringBuilder sb) {
776    if (sortOrder > 0) {
777      sb.append("s,");
778    }
779    int len = getPropertyLength();
780    for (int i = 0; i < len; i++) {
781      if ((flags[i] & FLAG_CHANGED_PROP) != 0) { // we do not check against mutablecontent here.
782        sb.append(i).append(',');
783      } else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
784        // an embedded property has been changed - recurse
785        EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
786        sb.append(i).append('[');
787        embeddedBean._ebean_getIntercept().addDirtyPropertyKey(sb);
788        sb.append(']');
789      }
790    }
791  }
792
793  /**
794   * Return a loaded property hash.
795   */
796  public StringBuilder getLoadedPropertyKey() {
797    StringBuilder sb = new StringBuilder();
798    int len = getPropertyLength();
799    for (int i = 0; i < len; i++) {
800      if (isLoadedProperty(i)) {
801        sb.append(i).append(',');
802      }
803    }
804    return sb;
805  }
806
807  public boolean[] getLoaded() {
808    boolean[] ret = new boolean[flags.length];
809    for (int i = 0; i < ret.length; i++) {
810      ret[i] = (flags[i] & FLAG_LOADED_PROP) != 0;
811    }
812    return ret;
813  }
814
815  /**
816   * Return the index of the property that triggered the lazy load.
817   */
818  public int getLazyLoadPropertyIndex() {
819    return lazyLoadProperty;
820  }
821
822  /**
823   * Return the property that triggered the lazy load.
824   */
825  public String getLazyLoadProperty() {
826    return getProperty(lazyLoadProperty);
827  }
828
829  /**
830   * Load the bean when it is a reference.
831   */
832  void loadBean(int loadProperty) {
833    lock.lock();
834    try {
835      if (beanLoader == null) {
836        final Database database = DB.byName(ebeanServerName);
837        if (database == null) {
838          throw new PersistenceException("Database [" + ebeanServerName + "] was not found?");
839        }
840        // For stand alone reference bean or after deserialisation lazy load
841        // using the ebeanServer. Synchronise only on the bean.
842        loadBeanInternal(loadProperty, database.pluginApi().beanLoader());
843        return;
844      }
845    } finally {
846      lock.unlock();
847    }
848    final Lock lock = beanLoader.lock();
849    try {
850      // Lazy loading using LoadBeanContext which supports batch loading
851      // Synchronise on the beanLoader (a 'node' of the LoadBeanContext 'tree')
852      loadBeanInternal(loadProperty, beanLoader);
853    } finally {
854      lock.unlock();
855    }
856  }
857
858  /**
859   * Invoke the lazy loading. This method is synchronised externally.
860   */
861  private void loadBeanInternal(int loadProperty, BeanLoader loader) {
862    if ((flags[loadProperty] & FLAG_LOADED_PROP) != 0) {
863      // race condition where multiple threads calling preGetter concurrently
864      return;
865    }
866    if (lazyLoadFailure) {
867      // failed when batch lazy loaded by another bean in the batch
868      throw new EntityNotFoundException("(Lazy) loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted");
869    }
870    if (lazyLoadProperty == -1) {
871      lazyLoadProperty = loadProperty;
872      if (nodeUsageCollector != null) {
873        nodeUsageCollector.setLoadProperty(getProperty(lazyLoadProperty));
874      }
875      loader.loadBean(this);
876      if (lazyLoadFailure) {
877        // failed when lazy loading this bean
878        throw new EntityNotFoundException("Lazy loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted.");
879      }
880      // bean should be loaded and intercepting now. setLoaded() has
881      // been called by the lazy loading mechanism
882    }
883  }
884
885  /**
886   * Helper method to check if two objects are equal.
887   */
888  @SuppressWarnings({"unchecked", "rawtypes"})
889  static boolean notEqual(Object obj1, Object obj2) {
890    if (obj1 == null) {
891      return (obj2 != null);
892    }
893    if (obj2 == null) {
894      return true;
895    }
896    if (obj1 == obj2) {
897      return false;
898    }
899    if (obj1 instanceof BigDecimal) {
900      // Use comparable for BigDecimal as equals
901      // uses scale in comparison...
902      if (obj2 instanceof BigDecimal) {
903        Comparable com1 = (Comparable) obj1;
904        return (com1.compareTo(obj2) != 0);
905      } else {
906        return true;
907      }
908    }
909    if (obj1 instanceof URL) {
910      // use the string format to determine if dirty
911      return !obj1.toString().equals(obj2.toString());
912    }
913    return !obj1.equals(obj2);
914  }
915
916  /**
917   * Called when a BeanCollection is initialised automatically.
918   */
919  public void initialisedMany(int propertyIndex) {
920    flags[propertyIndex] |= FLAG_LOADED_PROP;
921  }
922
923  private void preGetterCallback(int propertyIndex) {
924    PreGetterCallback preGetterCallback = this.preGetterCallback;
925    if (preGetterCallback != null) {
926      preGetterCallback.preGetterTrigger(propertyIndex);
927    }
928  }
929
930  /**
931   * Called prior to Id property getter.
932   */
933  public void preGetId() {
934    preGetterCallback(-1);
935  }
936
937  /**
938   * Method that is called prior to a getter method on the actual entity.
939   */
940  public void preGetter(int propertyIndex) {
941    preGetterCallback(propertyIndex);
942    if (state == STATE_NEW || disableLazyLoad) {
943      return;
944    }
945    if (!isLoadedProperty(propertyIndex)) {
946      loadBean(propertyIndex);
947    }
948    if (nodeUsageCollector != null) {
949      nodeUsageCollector.addUsed(getProperty(propertyIndex));
950    }
951  }
952
953  /**
954   * OneToMany and ManyToMany only set loaded state.
955   */
956  public void preSetterMany(boolean interceptField, int propertyIndex, Object oldValue, Object newValue) {
957    if (state == STATE_NEW) {
958      setLoadedProperty(propertyIndex);
959    } else {
960      if (readOnly) {
961        throw new IllegalStateException("This bean is readOnly");
962      }
963      setChangeLoaded(propertyIndex);
964    }
965  }
966
967  private void setChangedPropertyValue(int propertyIndex, boolean setDirtyState, Object origValue) {
968    if (readOnly) {
969      throw new IllegalStateException("This bean is readOnly");
970    }
971    setChangedProperty(propertyIndex);
972    if (setDirtyState) {
973      setOriginalValue(propertyIndex, origValue);
974      setDirtyStatus();
975    }
976  }
977
978  private void setDirtyStatus() {
979    if (!dirty) {
980      dirty = true;
981      if (embeddedOwner != null) {
982        // Cascade dirty state from Embedded bean to parent bean
983        embeddedOwner._ebean_getIntercept().setEmbeddedDirty(embeddedOwnerIndex);
984      }
985      if (nodeUsageCollector != null) {
986        nodeUsageCollector.setModified();
987      }
988    }
989  }
990
991  /**
992   * Check to see if the values are not equal. If they are not equal then create
993   * the old values for use with ConcurrencyMode.ALL.
994   */
995  public void preSetter(boolean intercept, int propertyIndex, Object oldValue, Object newValue) {
996    if (state == STATE_NEW) {
997      setLoadedProperty(propertyIndex);
998    } else if (notEqual(oldValue, newValue)) {
999      setChangedPropertyValue(propertyIndex, intercept, oldValue);
1000    }
1001  }
1002
1003  /**
1004   * Check for primitive boolean.
1005   */
1006  public void preSetter(boolean intercept, int propertyIndex, boolean oldValue, boolean newValue) {
1007    if (state == STATE_NEW) {
1008      setLoadedProperty(propertyIndex);
1009    } else if (oldValue != newValue) {
1010      setChangedPropertyValue(propertyIndex, intercept, oldValue);
1011    }
1012  }
1013
1014  /**
1015   * Check for primitive int.
1016   */
1017  public void preSetter(boolean intercept, int propertyIndex, int oldValue, int newValue) {
1018    if (state == STATE_NEW) {
1019      setLoadedProperty(propertyIndex);
1020    } else if (oldValue != newValue) {
1021      setChangedPropertyValue(propertyIndex, intercept, oldValue);
1022    }
1023  }
1024
1025  /**
1026   * long.
1027   */
1028  public void preSetter(boolean intercept, int propertyIndex, long oldValue, long newValue) {
1029    if (state == STATE_NEW) {
1030      setLoadedProperty(propertyIndex);
1031    } else if (oldValue != newValue) {
1032      setChangedPropertyValue(propertyIndex, intercept, oldValue);
1033    }
1034  }
1035
1036  /**
1037   * double.
1038   */
1039  public void preSetter(boolean intercept, int propertyIndex, double oldValue, double newValue) {
1040    if (state == STATE_NEW) {
1041      setLoadedProperty(propertyIndex);
1042    } else if (Double.compare(oldValue, newValue) != 0) {
1043      setChangedPropertyValue(propertyIndex, intercept, oldValue);
1044    }
1045  }
1046
1047  /**
1048   * float.
1049   */
1050  public void preSetter(boolean intercept, int propertyIndex, float oldValue, float newValue) {
1051    if (state == STATE_NEW) {
1052      setLoadedProperty(propertyIndex);
1053    } else if (Float.compare(oldValue, newValue) != 0) {
1054      setChangedPropertyValue(propertyIndex, intercept, oldValue);
1055    }
1056  }
1057
1058  /**
1059   * short.
1060   */
1061  public void preSetter(boolean intercept, int propertyIndex, short oldValue, short newValue) {
1062    if (state == STATE_NEW) {
1063      setLoadedProperty(propertyIndex);
1064    } else if (oldValue != newValue) {
1065      setChangedPropertyValue(propertyIndex, intercept, oldValue);
1066    }
1067  }
1068
1069  /**
1070   * char.
1071   */
1072  public void preSetter(boolean intercept, int propertyIndex, char oldValue, char newValue) {
1073    if (state == STATE_NEW) {
1074      setLoadedProperty(propertyIndex);
1075    } else if (oldValue != newValue) {
1076      setChangedPropertyValue(propertyIndex, intercept, oldValue);
1077    }
1078  }
1079
1080  /**
1081   * byte.
1082   */
1083  public void preSetter(boolean intercept, int propertyIndex, byte oldValue, byte newValue) {
1084    if (state == STATE_NEW) {
1085      setLoadedProperty(propertyIndex);
1086    } else if (oldValue != newValue) {
1087      setChangedPropertyValue(propertyIndex, intercept, oldValue);
1088    }
1089  }
1090
1091  /**
1092   * char[].
1093   */
1094  public void preSetter(boolean intercept, int propertyIndex, char[] oldValue, char[] newValue) {
1095    if (state == STATE_NEW) {
1096      setLoadedProperty(propertyIndex);
1097    } else if (!Arrays.equals(oldValue, newValue)) {
1098      setChangedPropertyValue(propertyIndex, intercept, oldValue);
1099    }
1100  }
1101
1102  /**
1103   * byte[].
1104   */
1105  public void preSetter(boolean intercept, int propertyIndex, byte[] oldValue, byte[] newValue) {
1106    if (state == STATE_NEW) {
1107      setLoadedProperty(propertyIndex);
1108    } else if (!Arrays.equals(oldValue, newValue)) {
1109      setChangedPropertyValue(propertyIndex, intercept, oldValue);
1110    }
1111  }
1112
1113  /**
1114   * Explicitly set an old value with force (the old value is forced even it is already set).
1115   */
1116  public void setOldValue(int propertyIndex, Object oldValue) {
1117    setChangedProperty(propertyIndex);
1118    setOriginalValueForce(propertyIndex, oldValue);
1119    setDirtyStatus();
1120  }
1121
1122  /**
1123   * Return the sort order value for an order column.
1124   */
1125  public int getSortOrder() {
1126    return sortOrder;
1127  }
1128
1129  /**
1130   * Set the sort order value for an order column.
1131   */
1132  public void setSortOrder(int sortOrder) {
1133    this.sortOrder = sortOrder;
1134  }
1135
1136  /**
1137   * Set if the entity was deleted from a BeanCollection.
1138   */
1139  public void setDeletedFromCollection(final boolean deletedFromCollection) {
1140    this.deletedFromCollection = deletedFromCollection;
1141  }
1142
1143  public boolean isOrphanDelete() {
1144    return deletedFromCollection && !isNew();
1145  }
1146
1147  /**
1148   * Set the load error that happened on this property.
1149   */
1150  public void setLoadError(int propertyIndex, Exception t) {
1151    if (loadErrors == null) {
1152      loadErrors = new Exception[flags.length];
1153    }
1154    loadErrors[propertyIndex] = t;
1155    flags[propertyIndex] |= FLAG_LOADED_PROP;
1156  }
1157
1158  /**
1159   * Returns the loadErrors.
1160   */
1161  public Map<String, Exception> getLoadErrors() {
1162    if (loadErrors == null) {
1163      return Collections.emptyMap();
1164    }
1165    Map<String, Exception> ret = null;
1166    int len = getPropertyLength();
1167    for (int i = 0; i < len; i++) {
1168      Exception loadError = loadErrors[i];
1169      if (loadError != null) {
1170        if (ret == null) {
1171          ret = new LinkedHashMap<>();
1172        }
1173        ret.put(getProperty(i), loadError);
1174      }
1175    }
1176    return ret;
1177  }
1178
1179  private boolean isChangedProp(int i) {
1180    if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
1181      return true;
1182    } else if (mutableInfo == null || mutableInfo[i] == null || mutableInfo[i].isEqualToObject(owner._ebean_getField(i))) {
1183      return false;
1184    } else {
1185      // mark for change
1186      flags[i] |= FLAG_CHANGED_PROP;
1187      dirty = true; // this makes the bean automatically dirty!
1188      return true;
1189    }
1190  }
1191
1192  /**
1193   * Return the MutableValueInfo for the given property or null.
1194   */
1195  public MutableValueInfo mutableInfo(int propertyIndex) {
1196    return mutableInfo == null ? null : mutableInfo[propertyIndex];
1197  }
1198
1199  /**
1200   * Set the MutableValueInfo for the given property.
1201   */
1202  public void mutableInfo(int propertyIndex, MutableValueInfo info) {
1203    if (mutableInfo == null) {
1204      mutableInfo = new MutableValueInfo[flags.length];
1205    }
1206    flags[propertyIndex] |= FLAG_MUTABLE_HASH_SET;
1207    mutableInfo[propertyIndex] = info;
1208  }
1209
1210  /**
1211   * Dirty detection set the next mutable property content and info .
1212   * <p>
1213   * Set here as the mutable property dirty detection is based on json content comparison.
1214   * We only want to perform the json serialisation once so storing it here as part of
1215   * dirty detection so that we can get it back to bind in insert or update etc.
1216   */
1217  public void mutableNext(int propertyIndex, MutableValueNext next) {
1218    if (mutableNext == null) {
1219      mutableNext = new MutableValueNext[flags.length];
1220    }
1221    mutableNext[propertyIndex] = next;
1222  }
1223
1224  /**
1225   * Update the 'next' mutable info returning the content that was obtained via dirty detection.
1226   */
1227  public String mutableNext(int propertyIndex) {
1228    if (mutableNext == null) {
1229      return null;
1230    }
1231    final MutableValueNext next = mutableNext[propertyIndex];
1232    return next != null ? next.content() : null;
1233  }
1234
1235}