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