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}