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