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