Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.granite.client.tide.data.impl.DirtyCheckContextImpl Maven / Gradle / Ivy
/*
GRANITE DATA SERVICES
Copyright (C) 2012 GRANITE DATA SERVICES S.A.S.
This file is part of Granite Data Services.
Granite Data Services is free software; you can redistribute it and/or modify
it under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
Granite Data Services is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
for more details.
You should have received a copy of the GNU Library General Public License
along with this library; if not, see .
*/
package org.granite.client.tide.data.impl;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.granite.client.persistence.LazyableCollection;
import org.granite.client.tide.PropertyHolder;
import org.granite.client.tide.data.Identifiable;
import org.granite.client.tide.data.Lazyable;
import org.granite.client.tide.data.spi.DataManager;
import org.granite.client.tide.data.spi.DataManager.ChangeKind;
import org.granite.client.tide.data.spi.DirtyCheckContext;
import org.granite.client.tide.data.spi.EntityDescriptor;
import org.granite.client.tide.data.spi.ExpressionEvaluator.Value;
import org.granite.client.tide.data.spi.MergeContext;
import org.granite.client.tide.data.spi.Wrapper;
import org.granite.client.tide.server.TrackingContext;
import org.granite.client.util.WeakIdentityHashMap;
import org.granite.logging.Logger;
/**
* @author William DRAI
*/
public class DirtyCheckContextImpl implements DirtyCheckContext {
private static Logger log = Logger.getLogger("org.granite.client.tide.data.DirtyCheckContextImpl");
private DataManager dataManager;
private TrackingContext trackingContext;
private int dirtyCount = 0;
private WeakIdentityHashMap> savedProperties = new WeakIdentityHashMap>();
private WeakIdentityHashMap unsavedEntities = new WeakIdentityHashMap();
public DirtyCheckContextImpl(DataManager dataManager, TrackingContext trackingContext) {
this.dataManager = dataManager;
this.trackingContext = trackingContext;
}
@Override
public void setTrackingContext(TrackingContext trackingContext) {
this.trackingContext = trackingContext;
}
public boolean isDirty() {
return dirtyCount > 0;
}
public void notifyDirtyChange(boolean oldDirty) {
if (isDirty() == oldDirty)
return;
dataManager.notifyDirtyChange(oldDirty, isDirty());
}
public boolean notifyEntityDirtyChange(Object entity, boolean oldDirtyEntity) {
boolean newDirtyEntity = isEntityChanged(entity);
if (newDirtyEntity != oldDirtyEntity)
dataManager.notifyEntityDirtyChange(entity, oldDirtyEntity, newDirtyEntity);
return newDirtyEntity;
}
public Map getSavedProperties(Object entity) {
return savedProperties.get(entity);
}
public boolean isSaved(Object entity) {
return savedProperties.containsKey(entity);
}
/**
* Check if the object is marked as new in the context
*
* @param object object to check
*
* @return true if the object has been newly attached
*/
public boolean isUnsaved(Object object) {
return unsavedEntities.containsKey(object);
}
public void addUnsaved(Identifiable entity) {
unsavedEntities.put(entity, true);
}
public void clear(boolean notify) {
boolean wasDirty = isDirty();
dirtyCount = 0;
savedProperties.clear();
if (notify)
notifyDirtyChange(wasDirty);
}
/**
* Check if entity property has been changed since last remote call
*
* @param entity entity to check
* @param propertyName property to check
* @param value current value to compare with saved value
*
* @return true is value has been changed
*/
public boolean isEntityPropertyChanged(Identifiable entity, String propertyName, Object value) {
Map source = savedProperties.get(entity);
if (source != null)
return source.containsKey(propertyName) && !isSame(source.get(propertyName), value);
return !isSame(dataManager.getProperty(entity, propertyName), value);
}
public boolean isEntityChanged(Object entity) {
return isEntityChanged(entity, null, null, null);
}
/**
* Check if entity has changed since last save point
*
* @param entity entity to check
* @param propName property name
* @param value
*
* @return entity is dirty
*/
@SuppressWarnings("unchecked")
public boolean isEntityChanged(Object entity, Object embedded, String propName, Object value) {
boolean saveTracking = trackingContext.isEnabled();
try {
trackingContext.setEnabled(false);
boolean dirty = false;
Map pval = dataManager.getPropertyValues(entity, false, false);
EntityDescriptor desc = entity instanceof Identifiable ? dataManager.getEntityDescriptor(entity) : null;
Map save = savedProperties.get(entity);
String versionPropertyName = desc != null ? desc.getVersionPropertyName() : null;
String dirtyPropertyName = desc != null ? desc.getDirtyPropertyName() : null;
if (embedded == null)
embedded = entity;
for (String p : pval.keySet()) {
if (p.equals(versionPropertyName) || p.equals(dirtyPropertyName))
continue;
Object val = (entity == embedded && p.equals(propName)) ? value : pval.get(p);
Object saveval = save != null ? save.get(p) : null;
if (save != null && ((val != null && (ObjectUtil.isSimple(val) || val instanceof byte[]))
|| (saveval != null && (ObjectUtil.isSimple(saveval) || saveval instanceof byte[])))) {
dirty = true;
break;
}
else if (save != null && (val instanceof Value || saveval instanceof Value || val instanceof Enum || saveval instanceof Enum)) {
if (saveval != null && ((val == null && saveval != null) || !val.equals(saveval))) {
dirty = true;
break;
}
}
else if (save != null && (val instanceof Identifiable || saveval instanceof Identifiable)) {
if (saveval != null && val != save.get(p)) {
dirty = true;
break;
}
}
else if ((val instanceof List> || val instanceof Map, ?>) && !(val instanceof LazyableCollection && !((LazyableCollection)val).isInitialized())) {
List savedArray = (List)saveval;
if (savedArray != null && !savedArray.isEmpty()) {
dirty = true;
break;
}
}
else if (val != null
&& !(val instanceof Identifiable || val instanceof Enum || val instanceof Value || val instanceof byte[])
&& isEntityChanged(val)) {
dirty = true;
break;
}
}
return dirty;
}
finally {
trackingContext.setEnabled(saveTracking);
}
}
public boolean isEntityDeepChanged(Object entity) {
return isEntityDeepChanged(entity, null, new IdentityHashMap());
}
private boolean isEntityDeepChanged(Object entity, Object embedded, IdentityHashMap cache) {
if (cache == null)
cache = new IdentityHashMap();
if (cache.containsKey(entity))
return false;
cache.put(entity, true);
boolean saveTracking = trackingContext.isEnabled();
try {
trackingContext.setEnabled(false);
Map pval = dataManager.getPropertyValues(entity, false, false);
if (embedded == null)
embedded = entity;
EntityDescriptor desc = entity instanceof Identifiable ? dataManager.getEntityDescriptor(entity) : null;
Map save = savedProperties.get(entity);
String versionPropertyName = desc != null ? desc.getVersionPropertyName() : null;
String dirtyPropertyName = desc != null ? desc.getDirtyPropertyName() : null;
for (String p : pval.keySet()) {
if (p.equals(versionPropertyName) || p.equals(dirtyPropertyName))
continue;
Object val = pval.get(p);
Object saveval = save != null ? save.get(p) : null;
if (save != null && ((val != null && (ObjectUtil.isSimple(val) || val instanceof byte[]))
|| (saveval != null && (ObjectUtil.isSimple(saveval) || saveval instanceof byte[])))) {
return true;
}
else if (save != null && (val instanceof Value || saveval instanceof Value || val instanceof Enum || saveval instanceof Enum)) {
if (saveval != null && ((val == null && saveval != null) || !val.equals(saveval))) {
return true;
}
}
else if (save != null && (val instanceof Identifiable || saveval instanceof Identifiable)) {
if (saveval != null && val != save.get(p))
return true;
if (isEntityDeepChanged(val, null, cache))
return true;
}
else if (val instanceof List> || val instanceof Map, ?>) {
if (val instanceof LazyableCollection && !((LazyableCollection)val).isInitialized())
return false;
@SuppressWarnings("unchecked")
List savedArray = (List)saveval;
if (savedArray != null && !savedArray.isEmpty())
return true;
if (val instanceof List>) {
for (Object elt : (List>)val) {
if (isEntityDeepChanged(elt, null, cache))
return true;
}
}
else if (val instanceof Map, ?>) {
for (Entry, ?> me : ((Map, ?>)val).entrySet()) {
if (isEntityDeepChanged(me.getKey(), null, cache))
return true;
if (isEntityDeepChanged(me.getValue(), null, cache))
return true;
}
}
}
else if (val != null
&& !(val instanceof Identifiable || val instanceof Enum || val instanceof Value || val instanceof byte[])
&& isEntityDeepChanged(val, embedded, cache)) {
return true;
}
}
}
finally {
trackingContext.setEnabled(saveTracking);
}
return false;
}
private boolean isSame(Object val1, Object val2) {
if (val1 == null && isEmpty(val2))
return true;
else if (val2 == null && isEmpty(val1))
return true;
else if (ObjectUtil.isSimple(val1) && ObjectUtil.isSimple(val2))
return val1.equals(val2);
else if (val1 instanceof byte[] && val2 instanceof byte[])
return Arrays.equals((byte[])val1, (byte[])val2);
else if ((val1 instanceof Value && val2 instanceof Value) || (val1 instanceof Enum && val2 instanceof Enum))
return val1.equals(val2);
Object n = val1 instanceof Wrapper ? ((Wrapper)val1).getWrappedObject() : val1;
Object o = val2 instanceof Wrapper ? ((Wrapper)val2).getWrappedObject() : val2;
if (n instanceof Identifiable && o instanceof Identifiable)
return ((Identifiable)n).getUid() != null && ((Identifiable)n).getUid().equals(((Identifiable)o).getUid());
return n == o;
}
private boolean isSameList(List save, Collection> coll) {
if (save.size() != coll.size())
return false;
if (coll instanceof List>) {
List> list = (List>)coll;
for (int i = 0; i < save.size(); i++) {
if (!isSame(save.get(i), list.get(i)))
return false;
}
}
else {
for (Object e : save) {
if (!coll.contains(e))
return false;
}
}
return true;
}
private boolean isSameMap(List save, Map, ?> map) {
if (save.size() != map.size())
return false;
for (int i = 0; i < save.size(); i++) {
Object[] entry = save.get(i);
if (!map.containsKey(entry[0]))
return false;
if (!isSame(entry[1], map.get(entry[0])))
return false;
}
return true;
}
private boolean isSameExt(Object val1, Object val2) {
if (val1 == null && isEmpty(val2))
return true;
else if (val2 == null && isEmpty(val1))
return true;
else if (ObjectUtil.isSimple(val1) && ObjectUtil.isSimple(val2))
return val1.equals(val2);
else if (val1 instanceof byte[] && val2 instanceof byte[])
return Arrays.equals((byte[])val1, (byte[])val2);
else if ((val1 instanceof Value && val2 instanceof Value) || (val1 instanceof Enum && val2 instanceof Enum))
return val1.equals(val2);
else if (val1 != null && val1.getClass().isArray() && val2 != null && val2.getClass().isArray()) {
if (Array.getLength(val1) != Array.getLength(val2))
return false;
for (int idx = 0; idx < Array.getLength(val1); idx++) {
if (!isSameExt(Array.get(val1, idx), Array.get(val2, idx)))
return false;
}
return true;
}
else if (val1 instanceof Set> && val2 instanceof Set>) {
if ((val1 instanceof LazyableCollection && !((LazyableCollection)val1).isInitialized())
|| (val2 instanceof LazyableCollection && !((LazyableCollection)val2).isInitialized()))
return false;
Collection> coll1 = (Collection>)val1;
Collection> coll2 = (Collection>)val2;
if (coll1.size() != coll2.size())
return false;
for (Object e : coll1) {
boolean found = false;
for (Object f : coll2) {
if (isSameExt(e, f)) {
found = true;
break;
}
}
if (!found)
return false;
}
for (Object e : coll2) {
boolean found = false;
for (Object f : coll1) {
if (isSameExt(e, f)) {
found = true;
break;
}
}
if (!found)
return false;
}
return true;
}
else if (val1 instanceof List> && val2 instanceof List>) {
if ((val1 instanceof LazyableCollection && !((LazyableCollection)val1).isInitialized())
|| (val2 instanceof LazyableCollection && !((LazyableCollection)val2).isInitialized()))
return false;
List> list1 = (List>)val1;
List> list2 = (List>)val2;
if (list1.size() != list2.size())
return false;
for (int idx = 0; idx < list1.size(); idx++) {
if (!isSameExt(list1.get(idx), list2.get(idx)))
return false;
}
return true;
}
else if (val1 instanceof Map, ?> && val2 instanceof Map, ?>) {
if ((val1 instanceof LazyableCollection && !((LazyableCollection)val1).isInitialized())
|| (val2 instanceof LazyableCollection && !((LazyableCollection)val2).isInitialized()))
return false;
Map, ?> map1 = (Map, ?>)val1;
Map, ?> map2 = (Map, ?>)val2;
if (map1.size() != map2.size())
return false;
for (Object e : map1.keySet()) {
Object key = null;
for (Object f : map2.keySet()) {
if (isSameExt(e, f)) {
key = f;
break;
}
}
if (key == null)
return false;
if (!isSameExt(map1.get(e), map2.get(key)))
return false;
}
for (Object f : map2.keySet()) {
Object key = null;
for (Object e : map1.keySet()) {
if (isSameExt(e, f)) {
key = e;
break;
}
}
if (key == null)
return false;
if (!isSameExt(map1.get(key), map2.get(f)))
return false;
}
return true;
}
Object n = val1 instanceof Wrapper ? ((Wrapper)val1).getWrappedObject() : val1;
Object o = val2 instanceof Wrapper ? ((Wrapper)val2).getWrappedObject() : val2;
if (n instanceof Identifiable && o instanceof Identifiable)
return ((Identifiable)n).getUid().equals(((Identifiable)o).getUid());
return n == o;
}
/**
* @private
* Interceptor for managed entity setters
*
* @param entity entity to intercept
* @param propName property name
* @param oldValue old value
* @param newValue new value
*/
public void entityPropertyChangeHandler(Object entity, Object object, String propName, Object oldValue, Object newValue) {
boolean oldDirty = isDirty();
boolean diff = !isSame(oldValue, newValue);
if (diff) {
boolean oldDirtyEntity = isEntityChanged(entity, object, propName, oldValue);
EntityDescriptor desc = dataManager.getEntityDescriptor(entity);
Map save = savedProperties.get(object);
boolean unsaved = save == null;
if (unsaved || (desc.getVersionPropertyName() != null &&
save.get(desc.getVersionPropertyName()) != dataManager.getProperty(entity, desc.getVersionPropertyName())
&& !(save.get(desc.getVersionPropertyName()) == null && dataManager.getProperty(entity, desc.getVersionPropertyName()) == null))) {
save = new HashMap();
if (desc.getVersionPropertyName() != null)
save.put(desc.getVersionPropertyName(), dataManager.getProperty(entity, desc.getVersionPropertyName()));
savedProperties.put(object, save);
save.put(propName, oldValue);
if (unsaved)
dirtyCount++;
}
if (save != null && (desc.getVersionPropertyName() == null
|| save.get(desc.getVersionPropertyName()) == dataManager.getProperty(entity, desc.getVersionPropertyName())
|| (save.get(desc.getVersionPropertyName()) == null && dataManager.getProperty(entity, desc.getVersionPropertyName()) == null))) {
if (!save.containsKey(propName))
save.put(propName, oldValue);
if (isSame(save.get(propName), newValue)) {
save.remove(propName);
int count = 0;
for (String p : save.keySet()) {
if (!p.equals(desc.getVersionPropertyName()))
count++;
}
if (count == 0) {
savedProperties.remove(object);
dirtyCount--;
}
}
}
notifyEntityDirtyChange(entity, oldDirtyEntity);
}
notifyDirtyChange(oldDirty);
}
/**
* @private
* Collection event handler to save changes on managed collections
*
* @param owner owner entity of the collection
* @param propName property name of the collection
* @param event collection event
*/
@SuppressWarnings("unchecked")
public void entityCollectionChangeHandler(Object owner, String propName, Collection> coll, ChangeKind kind, Integer location, Object[] items) {
boolean oldDirty = isDirty();
EntityDescriptor desc = dataManager.getEntityDescriptor(owner);
boolean oldDirtyEntity = isEntityChanged(owner);
Map esave = savedProperties.get(owner);
boolean unsaved = esave == null;
if (unsaved || (desc.getVersionPropertyName() != null &&
(esave.get(desc.getVersionPropertyName()) != dataManager.getProperty(owner, desc.getVersionPropertyName())
&& !(esave.get(desc.getVersionPropertyName()) == null && dataManager.getProperty(owner, desc.getVersionPropertyName()) == null)))) {
esave = new HashMap();
if (desc.getVersionPropertyName() != null)
esave.put(desc.getVersionPropertyName(), dataManager.getProperty(owner, desc.getVersionPropertyName()));
savedProperties.put(owner, esave);
if (unsaved)
dirtyCount++;
}
List save = (List)esave.get(propName);
if (save == null) {
save = new ArrayList();
esave.put(propName, save);
// Save collection snapshot
for (Object e : coll)
save.add(e);
// Adjust with last event
if (kind == ChangeKind.ADD) {
if (location != null) {
for (int i = 0; i < items.length; i++)
save.remove(location.intValue());
}
else {
for (Object item : items)
save.remove(item);
}
}
else if (kind == ChangeKind.REMOVE) {
if (location != null) {
for (int i = 0; i < items.length; i++)
save.add(location.intValue()+i, items[i]);
}
else {
for (Object item : items)
save.add(item);
}
}
else if (kind == ChangeKind.REPLACE) {
if (location != null)
save.set(location.intValue(), ((Object[])items[0])[0]);
else {
save.remove(((Object[])items[0])[1]);
save.add(((Object[])items[0])[0]);
}
}
}
else {
if (isSameList(save, coll)) {
esave.remove(propName);
int count = 0;
for (Object p : esave.keySet()) {
if (!p.equals(desc.getVersionPropertyName()))
count++;
}
if (count == 0) {
savedProperties.remove(owner);
dirtyCount--;
}
}
}
notifyEntityDirtyChange(owner, oldDirtyEntity);
notifyDirtyChange(oldDirty);
}
/**
* @private
* Collection event handler to save changes on managed maps
*
* @param owner owner entity of the collection
* @param propName property name of the collection
* @param event map event
*/
@SuppressWarnings("unchecked")
public void entityMapChangeHandler(Object owner, String propName, Map, ?> map, ChangeKind kind, Object[] items) {
boolean oldDirty = isDirty();
EntityDescriptor desc = dataManager.getEntityDescriptor(owner);
boolean oldDirtyEntity = isEntityChanged(owner);
Map esave = savedProperties.get(owner);
boolean unsaved = esave == null;
if (unsaved || (desc.getVersionPropertyName() != null &&
(esave.get(desc.getVersionPropertyName()) != dataManager.getProperty(owner, desc.getVersionPropertyName())
&& !(esave.get(desc.getVersionPropertyName()) == null && dataManager.getProperty(owner, desc.getVersionPropertyName()) == null)))) {
esave = new HashMap();
if (desc.getVersionPropertyName() != null)
esave.put(desc.getVersionPropertyName(), dataManager.getProperty(owner, desc.getVersionPropertyName()));
savedProperties.put(owner, esave);
if (unsaved)
dirtyCount++;
}
List save = (List)esave.get(propName);
if (save == null) {
save = new ArrayList();
esave.put(propName, save);
// Save map snapshot
for (Entry, ?> entry : map.entrySet()) {
boolean found = false;
if (kind == ChangeKind.ADD) {
for (Object item : items) {
if (isSame(entry.getKey(), ((Object[])item)[0])) {
found = true;
break;
}
}
}
else if (kind == ChangeKind.REPLACE) {
for (Object item : items) {
if (isSame(entry.getKey(), ((Object[])item)[0])) {
save.add(new Object[] { entry.getKey(), ((Object[])item)[1] });
found = true;
break;
}
}
}
if (!found)
save.add(new Object[] { entry.getKey(), entry.getValue() });
}
// Add removed element if needed
if (kind == ChangeKind.REMOVE) {
for (Object item : items)
save.add(new Object[] { ((Object[])item)[0], ((Object[])item)[1] });
}
}
else {
if (isSameMap(save, map)) {
esave.remove(propName);
int count = 0;
for (Object p : esave.keySet()) {
if (!p.equals(desc.getVersionPropertyName()))
count++;
}
if (count == 0) {
savedProperties.remove(owner);
dirtyCount--;
}
}
}
notifyEntityDirtyChange(owner, oldDirtyEntity);
notifyDirtyChange(oldDirty);
}
/**
* @private
* Mark an object merged from the server as not dirty
*
* @param object merged object
*/
public void markNotDirty(Object object, Identifiable entity) {
if (entity != null)
unsavedEntities.remove(entity);
if (!savedProperties.containsKey(object))
return;
boolean oldDirty = isDirty();
boolean oldDirtyEntity = false;
if (entity == null && object instanceof Identifiable)
entity = (Identifiable)object;
if (entity != null)
oldDirtyEntity = isEntityChanged(entity);
savedProperties.remove(object);
if (entity != null)
notifyEntityDirtyChange(entity, oldDirtyEntity);
dirtyCount--;
notifyDirtyChange(oldDirty);
}
/**
* @private
* Check if dirty properties of an object are the same than those of another entity
* When they are the same, unmark the dirty flag
*
* @param entity merged entity
* @param source source entity
* @param owner owner entity for embedded objects
* @return true if the entity is still dirty after comparing with incoming object
*/
public boolean checkAndMarkNotDirty(MergeContext mergeContext, Object entity, Object source, Object parent) {
if (entity != null)
unsavedEntities.remove(entity);
Map save = savedProperties.get(entity);
if (save == null)
return false;
Object owner = entity instanceof Identifiable ? (Identifiable)entity : parent;
boolean oldDirty = isDirty();
boolean oldDirtyEntity = isEntityChanged(owner);
List merged = new ArrayList();
EntityDescriptor desc = owner instanceof Identifiable ? dataManager.getEntityDescriptor(owner) : null;
String versionPropertyName = desc != null ? desc.getVersionPropertyName() : null;
if (source instanceof Identifiable && versionPropertyName != null)
save.put(versionPropertyName, dataManager.getProperty(source, versionPropertyName));
Map pval = dataManager.getPropertyValues(entity, false, false);
for (String propName : pval.keySet()) {
if (propName.equals(versionPropertyName) || propName.equals("dirty"))
continue;
// if (!source.hasOwnProperty(propName))
// continue;
Object localValue = pval.get(propName);
if (localValue instanceof PropertyHolder)
localValue = ((PropertyHolder)localValue).getObject();
Object sourceValue = dataManager.getProperty(source, propName);
if (isSameExt(sourceValue, localValue)) {
merged.add(propName);
continue;
}
if (sourceValue == null || ObjectUtil.isSimple(sourceValue) || sourceValue instanceof Value || sourceValue instanceof Enum) {
save.put(propName, sourceValue);
}
else if (sourceValue instanceof Identifiable) {
save.put(propName, mergeContext.getFromCache(sourceValue));
}
else if (sourceValue instanceof Collection> && !(sourceValue instanceof LazyableCollection && !((LazyableCollection)sourceValue).isInitialized())) {
List snapshot = new ArrayList((Collection>)sourceValue);
save.put(propName, snapshot);
}
else if (sourceValue instanceof Map, ?> && !(sourceValue instanceof LazyableCollection && !((LazyableCollection)sourceValue).isInitialized())) {
Map, ?> map = (Map, ?>)sourceValue;
List snapshot = new ArrayList(map.size());
for (Entry, ?> entry : map.entrySet())
snapshot.add(new Object[] { entry.getKey(), entry.getValue() });
save.put(propName, snapshot);
}
}
for (String propName : merged)
save.remove(propName);
int count = 0;
for (String propName : save.keySet()) {
if (!propName.equals(desc.getVersionPropertyName()))
count++;
}
if (count == 0) {
savedProperties.remove(entity);
dirtyCount--;
}
boolean newDirtyEntity = notifyEntityDirtyChange(owner, oldDirtyEntity);
notifyDirtyChange(oldDirty);
return newDirtyEntity;
}
public void fixRemovalsAndPersists(MergeContext mergeContext, List removals, List persists) {
boolean oldDirty = dirtyCount > 0;
for (Object object : savedProperties.keySet()) {
Identifiable owner = null;
if (object instanceof Identifiable)
owner = (Identifiable)object;
else {
Object[] ownerEntity = mergeContext.getOwnerEntity(object);
if (ownerEntity != null && ownerEntity[0] instanceof Identifiable)
owner = (Identifiable)ownerEntity[0];
}
EntityDescriptor desc = dataManager.getEntityDescriptor(owner);
boolean oldDirtyEntity = isEntityChanged(object);
Map save = savedProperties.get(object);
Iterator ip = save.keySet().iterator();
while (ip.hasNext()) {
String p = ip.next();
Object sn = save.get(p);
if (!(sn instanceof List>))
continue;
Object value = dataManager.getProperty(object, p);
if (value instanceof Collection>) {
@SuppressWarnings("unchecked")
List snapshot = (List)sn;
Collection> coll = (Collection>)value;
if (removals != null) {
Iterator isne = snapshot.iterator();
while (isne.hasNext()) {
Object sne = isne.next();
for (Object removal : removals) {
if (sne instanceof Identifiable && ObjectUtil.objectEquals(dataManager, sne, removal))
isne.remove();
}
}
}
if (persists != null) {
for (Object persist : persists) {
if (coll instanceof List>) {
List> list = (List>)coll;
List found = new ArrayList();
for (int j = 0; j < list.size(); j++) {
if (ObjectUtil.objectEquals(dataManager, list.get(j), persist))
found.add(j);
}
for (int j = 0; j < snapshot.size(); j++) {
if (ObjectUtil.objectEquals(dataManager, persist, snapshot.get(j))) {
snapshot.remove(j);
j--;
}
}
for (int idx : found)
snapshot.add(idx, persist);
}
else {
if (coll.contains(persist) && !snapshot.contains(persist))
snapshot.add(persist);
}
}
}
if (isSameList(snapshot, coll))
ip.remove();
}
else if (value instanceof Map, ?>) {
@SuppressWarnings("unchecked")
List snapshot = (List)sn;
Map, ?> map = (Map, ?>)value;
if (removals != null) {
Iterator isne = snapshot.iterator();
while (isne.hasNext()) {
Object[] sne = isne.next();
for (Object removal : removals) {
if (sne[0] instanceof Identifiable && ObjectUtil.objectEquals(dataManager, sne[0], removal))
isne.remove();
else if (sne[1] instanceof Identifiable && ObjectUtil.objectEquals(dataManager, sne[1], removal))
isne.remove();
}
}
}
// TODO: persist ?
// if (persists != null) {
// for (Object persist : persists) {
// boolean foundKey = false;
// List foundValues = new ArrayList();
// Iterator isne = snapshot.iterator();
// while (isne.hasNext()) {
// Object[] sne = isne.next();
// if (ObjectUtil.objectEquals(dataManager, sne[0], persist))
// foundKey = true;
// if (ObjectUtil.objectEquals(dataManager, sne[1], persist)) {
// foundValues.add(sne[0]);
// isne.remove();
// }
// }
// if (map.containsKey(persist) && !foundKey)
// snapshot.add(new Object[] { persist, map.get(persist) });
// if (map.containsValue(persist)) {
// for (Entry, ?> e : map.entrySet()) {
// if (e.getValue().equals(persist) && !e.getKey().equals(persist))
// snapshot.add(new Object[] { e.getKey(), e.getValue() });
// }
// }
// }
// }
if (isSameMap(snapshot, map))
ip.remove();
}
}
int count = 0;
for (Object p : save.keySet()) {
if (!p.equals(desc.getVersionPropertyName()))
count++;
}
if (count == 0) {
savedProperties.remove(object);
dirtyCount--;
}
notifyEntityDirtyChange(object, oldDirtyEntity);
}
notifyDirtyChange(oldDirty);
}
/**
* @private
* Internal implementation of entity reset
*/
@SuppressWarnings("unchecked")
public void resetEntity(MergeContext mergeContext, Object entity, Identifiable parent, Set cache) {
// Should not try to reset uninitialized entities
if (entity instanceof Lazyable && !((Lazyable)entity).isInitialized())
return;
if (cache.contains(entity))
return;
cache.add(entity);
Map save = savedProperties.get(entity);
EntityDescriptor desc = dataManager.getEntityDescriptor(entity);
Map pval = dataManager.getPropertyValues(entity, false, false);
for (String p : pval.keySet()) {
if (p.equals(desc.getVersionPropertyName()))
continue;
Object val = dataManager.getProperty(entity, p);
if (val instanceof Collection> && !(val instanceof LazyableCollection && !((LazyableCollection)val).isInitialized())) {
Collection coll = (Collection)val;
List savedArray = save != null ? (List)save.get(p) : null;
if (savedArray != null) {
for (Object obj : coll) {
if (obj instanceof Identifiable)
resetEntity(mergeContext, obj, parent, cache);
}
coll.clear();
for (Object e : savedArray)
coll.add(e);
// Must be here because collection reset has triggered other useless CollectionEvents
markNotDirty(val, parent);
}
for (Object o : coll) {
if (o instanceof Identifiable)
resetEntity(mergeContext, o, (Identifiable)o, cache);
}
}
else if (val instanceof Map, ?> && !(val instanceof LazyableCollection && !((LazyableCollection)val).isInitialized())) {
Map map = (Map)val;
List savedArray = save != null ? (List)save.get(p) : null;
if (savedArray != null) {
for (Entry entry : map.entrySet()) {
if (entry.getKey() instanceof Identifiable)
resetEntity(mergeContext, entry.getKey(), parent, cache);
if (entry.getValue() instanceof Identifiable)
resetEntity(mergeContext, entry.getValue(), parent, cache);
}
map.clear();
for (Object[] e : savedArray)
map.put(e[0], e[1]);
// Must be here because collection reset has triggered other useless CollectionEvents
markNotDirty(val, parent);
}
for (Entry me : map.entrySet()) {
if (me.getKey() instanceof Identifiable)
resetEntity(mergeContext, me.getKey(), (Identifiable)me.getKey(), cache);
if (me.getValue() instanceof Identifiable)
resetEntity(mergeContext, me.getValue(), (Identifiable)me.getValue(), cache);
}
}
else if (save != null && (ObjectUtil.isSimple(val) || ObjectUtil.isSimple(save.get(p)))) {
if (save.containsKey(p))
dataManager.setInternalProperty(entity, p, save.get(p));
}
else if (save != null && (val instanceof Enum || save.get(p) instanceof Enum || val instanceof Value || save.get(p) instanceof Value)) {
if (save.containsKey(p))
dataManager.setInternalProperty(entity, p, save.get(p));
}
else if (save != null && save.containsKey(p)) {
if (!ObjectUtil.objectEquals(dataManager, val, save.get(p)))
dataManager.setInternalProperty(entity, p, save.get(p));
}
else if (val instanceof Identifiable)
resetEntity(mergeContext, val, (Identifiable)val, cache);
else if (val != null && parent != null && !ObjectUtil.isSimple(val))
resetEntity(mergeContext, val, parent, cache);
}
// Must be here because entity reset may have triggered useless new saved properties
markNotDirty(entity, null);
}
/**
* @private
* Internal implementation of entity reset all
*/
public void resetAllEntities(MergeContext mergeContext, Set cache) {
boolean found = false;
do {
found = false;
for (Object entity : savedProperties.keySet()) {
if (entity instanceof Identifiable) {
found = true;
resetEntity(mergeContext, entity, (Identifiable)entity, cache);
break;
}
}
}
while (found);
if (dirtyCount > 0)
log.error("Incomplete reset of context, could be a bug");
}
/**
* @private
* Check if a value is empty
*
* @return value is empty
*/
public boolean isEmpty(Object val) {
if (val == null)
return true;
else if (val instanceof String)
return val.equals("");
else if (val.getClass().isArray())
return Array.getLength(val) == 0;
else if (val instanceof Date)
return ((Date)val).getTime() == 0L;
else if (val instanceof List>)
return ((List>)val).size() == 0;
else if (val instanceof Map, ?>)
return ((Map, ?>)val).size() == 0;
return false;
}
public static class Change {
private ChangeKind kind;
private int location;
private Object[] items;
public Change(ChangeKind kind, int location, Object[] items) {
this.kind = kind;
this.location = location;
this.items = items;
}
public ChangeKind getKind() {
return kind;
}
public int getLocation() {
return location;
}
public Object[] getItems() {
return items;
}
public void moveLocation(int offset) {
location += offset;
}
}
}