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