io.ebean.bean.InterceptReadWrite Maven / Gradle / Ivy
package io.ebean.bean;
import io.ebean.DB;
import io.ebean.Database;
import io.ebean.ValuePair;
import javax.persistence.EntityNotFoundException;
import javax.persistence.PersistenceException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URL;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* This is the object added to every entity bean using byte code enhancement.
*
* This provides the mechanisms to support deferred fetching of reference beans
* and oldValues generation for concurrency checking.
*/
public final class InterceptReadWrite implements EntityBeanIntercept {
private static final long serialVersionUID = -3664031775464862649L;
private static final int STATE_NEW = 0;
private static final int STATE_REFERENCE = 1;
private static final int STATE_LOADED = 2;
/**
* Used when a bean is partially loaded.
*/
private static final byte FLAG_LOADED_PROP = 1;
private static final byte FLAG_CHANGED_PROP = 2;
private static final byte FLAG_CHANGEDLOADED_PROP = 3;
/**
* Flags indicating if a property is a dirty embedded bean. Used to distinguish between an
* embedded bean being completely overwritten vs one with embedded properties that are dirty.
*/
private static final byte FLAG_EMBEDDED_DIRTY = 4;
private static final byte FLAG_ORIG_VALUE_SET = 8;
/**
* Flags indicating if the mutable hash is set.
*/
private static final byte FLAG_MUTABLE_HASH_SET = 16;
private final ReentrantLock lock = new ReentrantLock();
private transient NodeUsageCollector nodeUsageCollector;
private transient PersistenceContext persistenceContext;
private transient BeanLoader beanLoader;
private transient PreGetterCallback preGetterCallback;
private String ebeanServerName;
private boolean deletedFromCollection;
/**
* The actual entity bean that 'owns' this intercept.
*/
private final EntityBean owner;
private EntityBean embeddedOwner;
private int embeddedOwnerIndex;
/**
* One of NEW, REF, UPD.
*/
private int state;
private boolean forceUpdate;
private boolean readOnly;
private boolean dirty;
/**
* Flag set to disable lazy loading.
*/
private boolean disableLazyLoad;
/**
* Flag set when lazy loading failed due to the underlying bean being deleted in the DB.
*/
private boolean lazyLoadFailure;
private boolean fullyLoadedBean;
private boolean loadedFromCache;
private final byte[] flags;
private Object[] origValues;
private Exception[] loadErrors;
private int lazyLoadProperty = -1;
private Object ownerId;
private int sortOrder;
/**
* Holds information of json loaded jackson beans (e.g. the original json or checksum).
*/
private MutableValueInfo[] mutableInfo;
/**
* Holds json content determined at point of dirty check.
* Stored here on dirty check such that we only convert to json once.
*/
private MutableValueNext[] mutableNext;
/**
* Create with a given entity.
*/
public InterceptReadWrite(Object ownerBean) {
this.owner = (EntityBean) ownerBean;
this.flags = new byte[owner._ebean_getPropertyNames().length];
}
/**
* EXPERIMENTAL - Constructor only for use by serialization frameworks.
*/
public InterceptReadWrite() {
this.owner = null;
this.flags = null;
}
@Override
public String toString() {
return "InterceptReadWrite@" + hashCode() + "{state=" + state +
(dirty ? " dirty;" : "") +
(forceUpdate ? " forceUpdate;" : "") +
(readOnly ? " readOnly;" : "") +
(disableLazyLoad ? " disableLazyLoad;" : "") +
(lazyLoadFailure ? " lazyLoadFailure;" : "") +
(fullyLoadedBean ? " fullyLoadedBean;" : "") +
(loadedFromCache ? " loadedFromCache;" : "") +
", pc=" + System.identityHashCode(persistenceContext) +
", flags=" + Arrays.toString(flags) +
(lazyLoadProperty > -1 ? (", lazyLoadProperty=" + lazyLoadProperty) : "") +
", loader=" + beanLoader +
(ownerId != null ? (", ownerId=" + ownerId) : "") +
'}';
}
@Override
public EntityBean owner() {
return owner;
}
@Override
public PersistenceContext persistenceContext() {
return persistenceContext;
}
@Override
public void setPersistenceContext(PersistenceContext persistenceContext) {
this.persistenceContext = persistenceContext;
}
@Override
public void setNodeUsageCollector(NodeUsageCollector usageCollector) {
this.nodeUsageCollector = usageCollector;
}
@Override
public Object ownerId() {
return ownerId;
}
@Override
public void setOwnerId(Object ownerId) {
this.ownerId = ownerId;
}
@Override
public Object embeddedOwner() {
return embeddedOwner;
}
@Override
public int embeddedOwnerIndex() {
return embeddedOwnerIndex;
}
@Override
public void clearGetterCallback() {
this.preGetterCallback = null;
}
@Override
public void registerGetterCallback(PreGetterCallback getterCallback) {
this.preGetterCallback = getterCallback;
}
@Override
public void setEmbeddedOwner(EntityBean parentBean, int embeddedOwnerIndex) {
this.embeddedOwner = parentBean;
this.embeddedOwnerIndex = embeddedOwnerIndex;
}
@Override
public void setBeanLoader(BeanLoader beanLoader, PersistenceContext ctx) {
this.beanLoader = beanLoader;
this.persistenceContext = ctx;
this.ebeanServerName = beanLoader.name();
}
@Override
public void setBeanLoader(BeanLoader beanLoader) {
this.beanLoader = beanLoader;
this.ebeanServerName = beanLoader.name();
}
@Override
public boolean isFullyLoadedBean() {
return fullyLoadedBean;
}
@Override
public void setFullyLoadedBean(boolean fullyLoadedBean) {
this.fullyLoadedBean = fullyLoadedBean;
}
@Override
public boolean isPartial() {
for (byte flag : flags) {
if ((flag & FLAG_LOADED_PROP) == 0) {
return true;
}
}
return false;
}
@Override
public boolean isDirty() {
if (dirty) {
return true;
}
if (mutableInfo != null) {
for (int i = 0; i < mutableInfo.length; i++) {
if (mutableInfo[i] != null && !mutableInfo[i].isEqualToObject(owner._ebean_getField(i))) {
dirty = true;
break;
}
}
}
return dirty;
}
@Override
public void setEmbeddedDirty(int embeddedProperty) {
this.dirty = true;
setEmbeddedPropertyDirty(embeddedProperty);
}
@Override
public void setDirty(boolean dirty) {
this.dirty = dirty;
}
@Override
public boolean isNew() {
return state == STATE_NEW;
}
@Override
public boolean isNewOrDirty() {
return isNew() || isDirty();
}
@Override
public boolean hasIdOnly(int idIndex) {
for (int i = 0; i < flags.length; i++) {
if (i == idIndex) {
if ((flags[i] & FLAG_LOADED_PROP) == 0) return false;
} else if ((flags[i] & FLAG_LOADED_PROP) != 0) {
return false;
}
}
return true;
}
@Override
public boolean isReference() {
return state == STATE_REFERENCE;
}
@Override
public void setReference(int idPos) {
state = STATE_REFERENCE;
if (idPos > -1) {
// For cases where properties are set on constructor
// set every non Id property to unloaded (for lazy loading)
for (int i = 0; i < flags.length; i++) {
if (i != idPos) {
flags[i] &= ~FLAG_LOADED_PROP;
}
}
}
}
@Override
public void setLoadedFromCache(boolean loadedFromCache) {
this.loadedFromCache = loadedFromCache;
}
@Override
public boolean isLoadedFromCache() {
return loadedFromCache;
}
@Override
public boolean isReadOnly() {
return readOnly;
}
@Override
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
@Override
public void setForceUpdate(boolean forceUpdate) {
this.forceUpdate = forceUpdate;
}
@Override
public boolean isUpdate() {
return forceUpdate || state == STATE_LOADED || state == STATE_REFERENCE;
}
@Override
public boolean isLoaded() {
return state == STATE_LOADED;
}
@Override
public void setNew() {
this.state = STATE_NEW;
}
@Override
public void setLoaded() {
this.state = STATE_LOADED;
this.owner._ebean_setEmbeddedLoaded();
this.lazyLoadProperty = -1;
this.origValues = null;
// after save, transfer the mutable next values back to mutable info
if (mutableNext != null) {
for (int i = 0; i < mutableNext.length; i++) {
MutableValueNext next = mutableNext[i];
if (next != null) {
mutableInfo(i, next.info());
}
}
}
this.mutableNext = null;
for (int i = 0; i < flags.length; i++) {
flags[i] &= ~(FLAG_CHANGED_PROP | FLAG_ORIG_VALUE_SET);
}
this.dirty = false;
}
@Override
public void setLoadedLazy() {
this.state = STATE_LOADED;
this.lazyLoadProperty = -1;
}
@Override
public void setLazyLoadFailure(Object ownerId) {
this.lazyLoadFailure = true;
this.ownerId = ownerId;
}
@Override
public boolean isLazyLoadFailure() {
return lazyLoadFailure;
}
@Override
public boolean isDisableLazyLoad() {
return disableLazyLoad;
}
@Override
public void setDisableLazyLoad(boolean disableLazyLoad) {
this.disableLazyLoad = disableLazyLoad;
}
@Override
public void setEmbeddedLoaded(Object embeddedBean) {
if (embeddedBean instanceof EntityBean) {
((EntityBean) embeddedBean)._ebean_getIntercept().setLoaded();
}
}
@Override
public boolean isEmbeddedNewOrDirty(Object embeddedBean) {
if (embeddedBean == null) {
// if it was previously set then the owning bean would
// have oldValues containing the previous embedded bean
return false;
}
if (embeddedBean instanceof EntityBean) {
return ((EntityBean) embeddedBean)._ebean_getIntercept().isNewOrDirty();
} else {
// non-enhanced so must assume it is new and needs to be saved
return true;
}
}
@Override
public Object origValue(int propertyIndex) {
if ((flags[propertyIndex] & (FLAG_ORIG_VALUE_SET | FLAG_MUTABLE_HASH_SET)) == FLAG_MUTABLE_HASH_SET) {
// mutable hash set, but not ORIG_VALUE
setOriginalValue(propertyIndex, mutableInfo[propertyIndex].get());
}
if (origValues == null) {
return null;
}
return origValues[propertyIndex];
}
@Override
public int findProperty(String propertyName) {
final String[] names = owner._ebean_getPropertyNames();
for (int i = 0; i < names.length; i++) {
if (names[i].equals(propertyName)) {
return i;
}
}
return -1;
}
@Override
public String property(int propertyIndex) {
if (propertyIndex == -1) {
return null;
}
return owner._ebean_getPropertyName(propertyIndex);
}
@Override
public int propertyLength() {
return flags.length;
}
@Override
public void setPropertyLoaded(String propertyName, boolean loaded) {
final int position = findProperty(propertyName);
if (position == -1) {
throw new IllegalArgumentException("Property " + propertyName + " not found");
}
if (loaded) {
flags[position] |= FLAG_LOADED_PROP;
} else {
flags[position] &= ~FLAG_LOADED_PROP;
}
}
@Override
public void setPropertyUnloaded(int propertyIndex) {
flags[propertyIndex] &= ~FLAG_LOADED_PROP;
}
@Override
public void setLoadedProperty(int propertyIndex) {
flags[propertyIndex] |= FLAG_LOADED_PROP;
}
@Override
public void setLoadedPropertyAll() {
for (int i = 0; i < flags.length; i++) {
flags[i] |= FLAG_LOADED_PROP;
}
}
@Override
public boolean isLoadedProperty(int propertyIndex) {
return (flags[propertyIndex] & FLAG_LOADED_PROP) != 0;
}
@Override
public boolean isChangedProperty(int propertyIndex) {
return (flags[propertyIndex] & FLAG_CHANGED_PROP) != 0;
}
@Override
public boolean isDirtyProperty(int propertyIndex) {
return (flags[propertyIndex] & (FLAG_CHANGED_PROP + FLAG_EMBEDDED_DIRTY)) != 0;
}
@Override
public void markPropertyAsChanged(int propertyIndex) {
setChangedProperty(propertyIndex);
setDirty(true);
}
@Override
public void setChangedProperty(int propertyIndex) {
flags[propertyIndex] |= FLAG_CHANGED_PROP;
}
@Override
public void setChangeLoaded(int propertyIndex) {
flags[propertyIndex] |= FLAG_CHANGEDLOADED_PROP;
}
@Override
public void setEmbeddedPropertyDirty(int propertyIndex) {
flags[propertyIndex] |= FLAG_EMBEDDED_DIRTY;
}
@Override
public void setOriginalValue(int propertyIndex, Object value) {
if (origValues == null) {
origValues = new Object[flags.length];
}
if ((flags[propertyIndex] & FLAG_ORIG_VALUE_SET) == 0) {
flags[propertyIndex] |= FLAG_ORIG_VALUE_SET;
origValues[propertyIndex] = value;
}
}
@Override
public void setOriginalValueForce(int propertyIndex, Object value) {
if (origValues == null) {
origValues = new Object[flags.length];
}
origValues[propertyIndex] = value;
}
@Override
public void setNewBeanForUpdate() {
for (int i = 0; i < flags.length; i++) {
if ((flags[i] & FLAG_LOADED_PROP) != 0) {
flags[i] |= FLAG_CHANGED_PROP;
}
}
setDirty(true);
}
@Override
public Set loadedPropertyNames() {
if (fullyLoadedBean) {
return null;
}
final Set props = new LinkedHashSet<>();
for (int i = 0; i < flags.length; i++) {
if ((flags[i] & FLAG_LOADED_PROP) != 0) {
props.add(property(i));
}
}
return props;
}
@Override
public boolean[] dirtyProperties() {
final int len = propertyLength();
final boolean[] dirties = new boolean[len];
for (int i = 0; i < len; i++) {
// this, or an embedded property has been changed - recurse
dirties[i] = (flags[i] & (FLAG_CHANGED_PROP + FLAG_EMBEDDED_DIRTY)) != 0;
}
return dirties;
}
@Override
public Set dirtyPropertyNames() {
final Set props = new LinkedHashSet<>();
addDirtyPropertyNames(props, null);
return props;
}
@Override
public void addDirtyPropertyNames(Set props, String prefix) {
final int len = propertyLength();
for (int i = 0; i < len; i++) {
if (isChangedProp(i)) {
// the property has been changed on this bean
props.add((prefix == null ? property(i) : prefix + property(i)));
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
// an embedded property has been changed - recurse
final EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
embeddedBean._ebean_getIntercept().addDirtyPropertyNames(props, property(i) + ".");
}
}
}
@Override
public boolean hasDirtyProperty(Set propertyNames) {
final String[] names = owner._ebean_getPropertyNames();
final int len = propertyLength();
for (int i = 0; i < len; i++) {
if (isChangedProp(i)) {
if (propertyNames.contains(names[i])) {
return true;
}
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
if (propertyNames.contains(names[i])) {
return true;
}
}
}
return false;
}
@Override
public Map dirtyValues() {
final Map dirtyValues = new LinkedHashMap<>();
addDirtyPropertyValues(dirtyValues, null);
return dirtyValues;
}
@Override
public void addDirtyPropertyValues(Map dirtyValues, String prefix) {
final int len = propertyLength();
for (int i = 0; i < len; i++) {
if (isChangedProp(i)) {
// the property has been changed on this bean
final String propName = (prefix == null ? property(i) : prefix + property(i));
final Object newVal = owner._ebean_getField(i);
final Object oldVal = origValue(i);
if (notEqual(oldVal, newVal)) {
dirtyValues.put(propName, new ValuePair(newVal, oldVal));
}
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
// an embedded property has been changed - recurse
final EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
embeddedBean._ebean_getIntercept().addDirtyPropertyValues(dirtyValues, property(i) + ".");
}
}
}
@Override
public void addDirtyPropertyValues(BeanDiffVisitor visitor) {
final int len = propertyLength();
for (int i = 0; i < len; i++) {
if (isChangedProp(i)) {
// the property has been changed on this bean
final Object newVal = owner._ebean_getField(i);
final Object oldVal = origValue(i);
if (notEqual(oldVal, newVal)) {
visitor.visit(i, newVal, oldVal);
}
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
// an embedded property has been changed - recurse
final EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
visitor.visitPush(i);
embeddedBean._ebean_getIntercept().addDirtyPropertyValues(visitor);
visitor.visitPop();
}
}
}
@Override
public StringBuilder dirtyPropertyKey() {
final StringBuilder sb = new StringBuilder();
addDirtyPropertyKey(sb);
return sb;
}
@Override
public void addDirtyPropertyKey(StringBuilder sb) {
if (sortOrder > 0) {
sb.append("s,");
}
final int len = propertyLength();
for (int i = 0; i < len; i++) {
if ((flags[i] & FLAG_CHANGED_PROP) != 0) { // we do not check against mutablecontent here.
sb.append(i).append(',');
} else if ((flags[i] & FLAG_EMBEDDED_DIRTY) != 0) {
// an embedded property has been changed - recurse
sb.append(i).append('[');
((EntityBean) owner._ebean_getField(i))._ebean_getIntercept().addDirtyPropertyKey(sb);
sb.append(']');
}
}
}
@Override
public StringBuilder loadedPropertyKey() {
final StringBuilder sb = new StringBuilder();
final int len = propertyLength();
for (int i = 0; i < len; i++) {
if (isLoadedProperty(i)) {
sb.append(i).append(',');
}
}
return sb;
}
@Override
public boolean[] loaded() {
final boolean[] ret = new boolean[flags.length];
for (int i = 0; i < ret.length; i++) {
ret[i] = (flags[i] & FLAG_LOADED_PROP) != 0;
}
return ret;
}
@Override
public int lazyLoadPropertyIndex() {
return lazyLoadProperty;
}
@Override
public String lazyLoadProperty() {
return property(lazyLoadProperty);
}
@Override
public void loadBean(int loadProperty) {
lock.lock();
try {
if (beanLoader == null) {
final Database database = DB.byName(ebeanServerName);
if (database == null) {
throw new PersistenceException(ebeanServerName == null ? "No registered default server" : "Database [" + ebeanServerName + "] is not registered");
}
// For stand alone reference bean or after deserialisation lazy load
// using the ebeanServer. Synchronise only on the bean.
loadBeanInternal(loadProperty, database.pluginApi().beanLoader());
return;
}
} finally {
lock.unlock();
}
final Lock lock = beanLoader.lock();
try {
// Lazy loading using LoadBeanContext which supports batch loading
// Synchronise on the beanLoader (a 'node' of the LoadBeanContext 'tree')
loadBeanInternal(loadProperty, beanLoader);
} finally {
lock.unlock();
}
}
@Override
public void loadBeanInternal(int loadProperty, BeanLoader loader) {
if ((flags[loadProperty] & FLAG_LOADED_PROP) != 0) {
// race condition where multiple threads calling preGetter concurrently
return;
}
if (lazyLoadFailure) {
// failed when batch lazy loaded by another bean in the batch
throw new EntityNotFoundException("(Lazy) loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted. BeanLoader: " + beanLoader);
}
if (lazyLoadProperty == -1) {
lazyLoadProperty = loadProperty;
loader.loadBean(this);
if (lazyLoadFailure) {
// failed when lazy loading this bean
throw new EntityNotFoundException("Lazy loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted. BeanLoader: " + beanLoader);
}
// bean should be loaded and intercepting now. setLoaded() has
// been called by the lazy loading mechanism
}
}
/**
* Helper method to check if two objects are equal.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
static boolean notEqual(Object obj1, Object obj2) {
if (obj1 == null) {
return (obj2 != null);
}
if (obj2 == null) {
return true;
}
if (obj1 == obj2) {
return false;
}
if (obj1 instanceof BigDecimal) {
// Use comparable for BigDecimal as equals
// uses scale in comparison...
if (obj2 instanceof BigDecimal) {
Comparable com1 = (Comparable) obj1;
return (com1.compareTo(obj2) != 0);
} else {
return true;
}
}
if (obj1 instanceof URL) {
// use the string format to determine if dirty
return !obj1.toString().equals(obj2.toString());
}
if (obj1 instanceof File && obj2 instanceof File) {
File file1 = (File) obj1;
File file2 = (File) obj2;
if (file1.exists() && file2.exists() && file1.length() == file2.length()) {
return notEqualContent(file1, file2);
}
}
return !obj1.equals(obj2);
}
private static boolean notEqualContent(File file1, File file2) {
try (InputStream is1 = new FileInputStream(file1); InputStream is2 = new FileInputStream(file2)) {
byte[] buf1 = new byte[16384];
byte[] buf2 = new byte[16384];
int len1;
int len2;
while ((len1 = is1.read(buf1)) != -1 && (len2 = is2.read(buf2)) != -1) {
if (len1 != len2) {
return true;
}
if (!Arrays.equals(buf1, buf2)) {
// it does not matter, if we compare more than len1/len2 as the remainig
// bytes in the buffers are either 0 or equals from the prev. loop.
return true;
}
}
return false;
} catch (IOException e) {
return true; // handle them as "not equals"
}
}
@Override
public void initialisedMany(int propertyIndex) {
flags[propertyIndex] |= FLAG_LOADED_PROP;
}
@Override
public void preGetterCallback(int propertyIndex) {
PreGetterCallback preGetterCallback = this.preGetterCallback;
if (preGetterCallback != null) {
preGetterCallback.preGetterTrigger(propertyIndex);
}
}
@Override
public void preGetId() {
preGetterCallback(-1);
}
@Override
public void preGetter(int propertyIndex) {
preGetterCallback(propertyIndex);
if (state == STATE_NEW || disableLazyLoad) {
return;
}
if (!isLoadedProperty(propertyIndex)) {
loadBean(propertyIndex);
}
if (nodeUsageCollector != null) {
nodeUsageCollector.addUsed(property(propertyIndex));
}
}
@Override
public void preSetterMany(boolean interceptField, int propertyIndex, Object oldValue, Object newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else {
if (readOnly) {
throw new IllegalStateException("This bean is readOnly");
}
setChangeLoaded(propertyIndex);
}
}
@Override
public void setChangedPropertyValue(int propertyIndex, boolean setDirtyState, Object origValue) {
if (readOnly) {
throw new IllegalStateException("This bean is readOnly");
}
setChangedProperty(propertyIndex);
if (setDirtyState) {
setOriginalValue(propertyIndex, origValue);
setDirtyStatus();
}
}
@Override
public void setDirtyStatus() {
if (!dirty) {
dirty = true;
if (embeddedOwner != null) {
// Cascade dirty state from Embedded bean to parent bean
embeddedOwner._ebean_getIntercept().setEmbeddedDirty(embeddedOwnerIndex);
}
if (nodeUsageCollector != null) {
nodeUsageCollector.setModified();
}
}
}
@Override
public void preSetter(boolean intercept, int propertyIndex, Object oldValue, Object newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (notEqual(oldValue, newValue)) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
}
}
@Override
public void preSetter(boolean intercept, int propertyIndex, boolean oldValue, boolean newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
}
}
@Override
public void preSetter(boolean intercept, int propertyIndex, int oldValue, int newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
}
}
@Override
public void preSetter(boolean intercept, int propertyIndex, long oldValue, long newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
}
}
@Override
public void preSetter(boolean intercept, int propertyIndex, double oldValue, double newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (Double.compare(oldValue, newValue) != 0) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
}
}
@Override
public void preSetter(boolean intercept, int propertyIndex, float oldValue, float newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (Float.compare(oldValue, newValue) != 0) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
}
}
@Override
public void preSetter(boolean intercept, int propertyIndex, short oldValue, short newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
}
}
@Override
public void preSetter(boolean intercept, int propertyIndex, char oldValue, char newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
}
}
@Override
public void preSetter(boolean intercept, int propertyIndex, byte oldValue, byte newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (oldValue != newValue) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
}
}
@Override
public void preSetter(boolean intercept, int propertyIndex, char[] oldValue, char[] newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (!Arrays.equals(oldValue, newValue)) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
}
}
@Override
public void preSetter(boolean intercept, int propertyIndex, byte[] oldValue, byte[] newValue) {
if (state == STATE_NEW) {
setLoadedProperty(propertyIndex);
} else if (!Arrays.equals(oldValue, newValue)) {
setChangedPropertyValue(propertyIndex, intercept, oldValue);
}
}
@Override
public void setOldValue(int propertyIndex, Object oldValue) {
setChangedProperty(propertyIndex);
setOriginalValueForce(propertyIndex, oldValue);
setDirtyStatus();
}
@Override
public int sortOrder() {
return sortOrder;
}
@Override
public void setSortOrder(int sortOrder) {
this.sortOrder = sortOrder;
}
@Override
public void setDeletedFromCollection(final boolean deletedFromCollection) {
this.deletedFromCollection = deletedFromCollection;
}
@Override
public boolean isOrphanDelete() {
return deletedFromCollection && !isNew();
}
@Override
public void setLoadError(int propertyIndex, Exception t) {
if (loadErrors == null) {
loadErrors = new Exception[flags.length];
}
loadErrors[propertyIndex] = t;
flags[propertyIndex] |= FLAG_LOADED_PROP;
}
@Override
public Map loadErrors() {
if (loadErrors == null) {
return Collections.emptyMap();
}
Map ret = null;
int len = propertyLength();
for (int i = 0; i < len; i++) {
final Exception loadError = loadErrors[i];
if (loadError != null) {
if (ret == null) {
ret = new LinkedHashMap<>();
}
ret.put(property(i), loadError);
}
}
return ret;
}
@Override
public boolean isChangedProp(int i) {
if ((flags[i] & FLAG_CHANGED_PROP) != 0) {
return true;
} else if (mutableInfo == null || mutableInfo[i] == null || mutableInfo[i].isEqualToObject(owner._ebean_getField(i))) {
return false;
} else {
// mark for change
flags[i] |= FLAG_CHANGED_PROP;
dirty = true; // this makes the bean automatically dirty!
return true;
}
}
@Override
public MutableValueInfo mutableInfo(int propertyIndex) {
return mutableInfo == null ? null : mutableInfo[propertyIndex];
}
@Override
public void mutableInfo(int propertyIndex, MutableValueInfo info) {
if (mutableInfo == null) {
mutableInfo = new MutableValueInfo[flags.length];
}
flags[propertyIndex] |= FLAG_MUTABLE_HASH_SET;
mutableInfo[propertyIndex] = info;
}
@Override
public void mutableNext(int propertyIndex, MutableValueNext next) {
if (mutableNext == null) {
mutableNext = new MutableValueNext[flags.length];
}
mutableNext[propertyIndex] = next;
}
@Override
public String mutableNext(int propertyIndex) {
if (mutableNext == null) {
return null;
}
final MutableValueNext next = mutableNext[propertyIndex];
return next != null ? next.content() : null;
}
}