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