package er.extensions.eof;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import com.webobjects.eoaccess.EOAdaptor;
import com.webobjects.eoaccess.EODatabase;
import com.webobjects.eoaccess.EODatabaseContext;
import com.webobjects.eoaccess.EOEntity;
import com.webobjects.eoaccess.EOModel;
import com.webobjects.eocontrol.EOEnterpriseObject;
import com.webobjects.eocontrol.EOGlobalID;
import com.webobjects.eocontrol.EOTemporaryGlobalID;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSLog;
import com.webobjects.foundation.NSMutableDictionary;
import com.webobjects.foundation.NSNotification;
import com.webobjects.foundation.NSNotificationCenter;
import com.webobjects.foundation.NSSet;
import er.extensions.foundation.ERXArrayUtilities;
import er.extensions.foundation.ERXStringUtilities;
public class ERXDatabase extends EODatabase {
public static final String SnapshotCacheChanged = "SnapshotCacheChanged";
public static final String CacheChangeKey = "CacheChange";
protected static int SnapshotCacheMapInitialCapacity = 1048576;
protected static float SnapshotCacheMapInitialLoadFactor = 0.75f;
private boolean _globalIDChanged;
private boolean _decrementSnapshot;
public static void setSnapshotCacheMapInitialCapacity( int capacity ) {
NSLog.out.appendln( "Setting SnapshotCacheMapInitialCapacity = " + capacity );
SnapshotCacheMapInitialCapacity = capacity;
}
public static void setSnapshotCacheMapInitialLoadFactor( float loadFactor ) {
NSLog.out.appendln( "Setting SnapshotCacheMapInitialLoadFactor = " + loadFactor );
SnapshotCacheMapInitialLoadFactor = loadFactor;
}
public ERXDatabase(EOAdaptor adaptor) {
super(adaptor);
// AK: huge performance optimization when you use badly distributed LONG keys
NSLog.out.appendln( "Using SnapshotCacheMapInitialCapacity = " + SnapshotCacheMapInitialCapacity );
NSLog.out.appendln( "Using SnapshotCacheMapInitialLoadFactor = " + SnapshotCacheMapInitialLoadFactor );
_snapshots = new NSMutableDictionary() {
/**
* Do I need to update serialVersionUID?
* See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the
* <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
*/
private static final long serialVersionUID = 1L;
Map hashMap = new HashMap( SnapshotCacheMapInitialCapacity, SnapshotCacheMapInitialLoadFactor );
@Override
public Object objectForKey(Object key) {
return hashMap.get(key);
}
@Override
public void setObjectForKey(Object object, Object key) {
hashMap.put(key, object);
}
@Override
public Object removeObjectForKey(Object key) {
return hashMap.remove(key);
}
@Override
public NSDictionary immutableClone() {
return new NSDictionary(hashMap);
}
@Override
public NSArray allKeys() {
return new NSArray(hashMap.keySet());
}
@Override
public int size() {
return hashMap.size();
}
@Override
public int count() {
return hashMap.size();
}
};
}
public ERXDatabase(EOModel model) {
super(model);
}
public ERXDatabase(EODatabase _database) {
this(_database.adaptor());
Enumeration modelsEnum = _database.models().objectEnumerator();
while (modelsEnum.hasMoreElements()) {
EOModel model = (EOModel) modelsEnum.nextElement();
addModel(model);
}
_database.dispose();
}
public int snapshotCacheSize() {
return _snapshots.size();
}
public synchronized void _notifyCacheChange(CacheChange cacheChange) {
NSNotificationCenter.defaultCenter().postNotification(ERXDatabase.SnapshotCacheChanged, this, new NSDictionary(cacheChange, ERXDatabase.CacheChangeKey));
}
@Override
protected NSSet _cachedFetchAttributesForEntityNamed(String name) {
return super._cachedFetchAttributesForEntityNamed(name);
}
@Override
protected void _clearLastRecords() {
super._clearLastRecords();
}
@Override
protected _DatabaseRecord _fastHashGet(EOGlobalID gid) {
return super._fastHashGet(gid);
}
@Override
protected void _fastHashInsert(_DatabaseRecord rec, EOGlobalID gid) {
super._fastHashInsert(rec, gid);
if (_globalIDChanged) {
_notifyCacheChange(new SnapshotInserted(gid, snapshotForGlobalID(gid)));
}
}
@Override
protected void _fastHashRemove(EOGlobalID gid) {
// if (_decrementSnapshot) {
// System.out.println("ERXDatabase._fastHashRemove: remove " + gid);
// NSLog.out.appendln(new RuntimeException());
// }
super._fastHashRemove(gid);
}
@Override
public void _forgetSnapshotForGlobalID(EOGlobalID gid) {
super._forgetSnapshotForGlobalID(gid);
}
@Override
protected void _freeToManyMap(_DatabaseRecord rec) {
super._freeToManyMap(rec);
}
@Override
public void _globalIDChanged(NSNotification notification) {
boolean oldGlobalIDChanged = _globalIDChanged;
_globalIDChanged = true;
try {
super._globalIDChanged(notification);
}
finally {
_globalIDChanged = oldGlobalIDChanged;
}
}
@Override
public void recordSnapshotForGlobalID(NSDictionary snapshot, EOGlobalID gid) {
if (!ERXDatabaseContext.isFetching() && !(gid instanceof EOTemporaryGlobalID)) {
_notifyCacheChange(new SnapshotUpdated(gid, snapshot));
}
super.recordSnapshotForGlobalID(snapshot, gid);
}
@Override
public void recordSnapshotForSourceGlobalID(NSArray gids, EOGlobalID gid, String name) {
if (!ERXDatabaseContext.isFetching()) {
NSArray originalToManyGIDs = snapshotForSourceGlobalID(gid, name);
_notifyCacheChange(new ToManySnapshotUpdated(gid, name, originalToManyGIDs, gids));
}
super.recordSnapshotForSourceGlobalID(gids, gid, name);
}
@Override
public int _indexOfRegisteredContext(EODatabaseContext context) {
return super._indexOfRegisteredContext(context);
}
@Override
protected EOGlobalID _recordedGIDForSnapshotWithGid(EOGlobalID gid) {
return super._recordedGIDForSnapshotWithGid(gid);
}
@Override
protected void _setTimestampForCachedGlobalID(EOGlobalID gid) {
super._setTimestampForCachedGlobalID(gid);
}
@Override
public int _snapshotCountForGlobalID(EOGlobalID gid) {
return super._snapshotCountForGlobalID(gid);
}
@Override
public EOAdaptor adaptor() {
return super.adaptor();
}
@Override
public void addModel(EOModel model) {
super.addModel(model);
}
@Override
public boolean addModelIfCompatible(EOModel model) {
return super.addModelIfCompatible(model);
}
@Override
public void decrementSnapshotCountForGlobalID(EOGlobalID gid) {
boolean oldDecrementSnapshot = _decrementSnapshot;
_decrementSnapshot = true;
try {
super.decrementSnapshotCountForGlobalID(gid);
}
finally {
_decrementSnapshot = oldDecrementSnapshot;
}
}
@Override
public void dispose() {
super.dispose();
}
@Override
public EOEntity entityForObject(EOEnterpriseObject object) {
return super.entityForObject(object);
}
@Override
public EOEntity entityNamed(String entityName) {
return super.entityNamed(entityName);
}
@Override
public void forgetAllSnapshots() {
super.forgetAllSnapshots();
}
@Override
public void forgetSnapshotForGlobalID(EOGlobalID gid) {
super.forgetSnapshotForGlobalID(gid);
}
@Override
public void forgetSnapshotsForGlobalIDs(NSArray array) {
super.forgetSnapshotsForGlobalIDs(array);
}
@Override
public void handleDroppedConnection() {
super.handleDroppedConnection();
}
@Override
public void incrementSnapshotCountForGlobalID(EOGlobalID gid) {
super.incrementSnapshotCountForGlobalID(gid);
}
@Override
public void invalidateResultCache() {
super.invalidateResultCache();
}
@Override
public void invalidateResultCacheForEntityNamed(String name) {
super.invalidateResultCacheForEntityNamed(name);
}
@Override
public NSArray models() {
return super.models();
}
@Override
public void recordSnapshots(NSDictionary snapshots) {
super.recordSnapshots(snapshots);
}
@Override
public void recordToManySnapshots(NSDictionary snapshots) {
super.recordToManySnapshots(snapshots);
}
@Override
public void registerContext(EODatabaseContext context) {
super.registerContext(context);
}
@Override
public NSArray registeredContexts() {
return super.registeredContexts();
}
@Override
public void removeModel(EOModel model) {
super.removeModel(model);
}
@Override
public NSArray resultCacheForEntityNamed(String name) {
return super.resultCacheForEntityNamed(name);
}
@Override
public void setResultCache(NSArray cache, String name) {
super.setResultCache(cache, name);
}
@Override
public void setTimestampToNow() {
super.setTimestampToNow();
}
@Override
public NSDictionary snapshotForGlobalID(EOGlobalID gid, long timestamp) {
return super.snapshotForGlobalID(gid, timestamp);
}
@Override
public NSDictionary snapshotForGlobalID(EOGlobalID gid) {
return super.snapshotForGlobalID(gid);
}
@Override
public NSArray snapshotForSourceGlobalID(EOGlobalID gid, String name, long timestamp) {
return super.snapshotForSourceGlobalID(gid, name, timestamp);
}
@Override
public NSArray snapshotForSourceGlobalID(EOGlobalID gid, String name) {
return super.snapshotForSourceGlobalID(gid, name);
}
@Override
public NSDictionary snapshots() {
return super.snapshots();
}
@Override
public long timestampForGlobalID(EOGlobalID gid) {
return super.timestampForGlobalID(gid);
}
@Override
public long timestampForSourceGlobalID(EOGlobalID gid, String name) {
return super.timestampForSourceGlobalID(gid, name);
}
@Override
public void unregisterContext(EODatabaseContext context) {
super.unregisterContext(context);
}
public static abstract class CacheChange {
private EOGlobalID _gid;
public CacheChange(EOGlobalID gid) {
_gid = gid;
}
public EOGlobalID gid() {
return _gid;
}
@Override
public String toString() {
return "[" + ERXStringUtilities.getSimpleClassName(getClass()) + ": " + _gid + "]";
}
}
public static abstract class SnapshotCacheChange extends CacheChange {
private NSDictionary _snapshot;
public SnapshotCacheChange(EOGlobalID gid, NSDictionary snapshot) {
super(gid);
_snapshot = snapshot;
}
public NSDictionary snapshot() {
return _snapshot;
}
}
public static class SnapshotInserted extends SnapshotCacheChange {
public SnapshotInserted(EOGlobalID gid, NSDictionary snapshot) {
super(gid, snapshot);
}
}
public static class SnapshotUpdated extends SnapshotCacheChange {
public SnapshotUpdated(EOGlobalID gid, NSDictionary snapshot) {
super(gid, snapshot);
}
}
public static class SnapshotDeleted extends SnapshotCacheChange {
public SnapshotDeleted(EOGlobalID gid, NSDictionary snapshot) {
super(gid, snapshot);
}
}
public static class ToManySnapshotUpdated extends CacheChange {
private String _name;
private NSArray _addedGIDs;
private NSArray _removedGIDs;
private boolean _removeAll;
public ToManySnapshotUpdated(EOGlobalID sourceGID, String name, NSArray addedGIDs, NSArray removedGIDs, boolean removeAll) {
super(sourceGID);
_name = name;
_addedGIDs = addedGIDs;
_removedGIDs = removedGIDs;
_removeAll = removeAll;
}
public ToManySnapshotUpdated(EOGlobalID sourceGID, String name, NSArray originalToManyGIDs, NSArray newToManyGIDs) {
super(sourceGID);
_name = name;
if (originalToManyGIDs == null || originalToManyGIDs.count() == 0) {
_addedGIDs = newToManyGIDs;
}
else if (newToManyGIDs == null || newToManyGIDs.count() == 0) {
_removeAll = true;
_removedGIDs = originalToManyGIDs;
}
else {
_addedGIDs = ERXArrayUtilities.arrayMinusArray(newToManyGIDs, originalToManyGIDs);
_removedGIDs = ERXArrayUtilities.arrayMinusArray(originalToManyGIDs, newToManyGIDs);
}
}
public String name() {
return _name;
}
public NSArray removedGIDs() {
return _removedGIDs;
}
public boolean removeAll() {
return _removeAll;
}
public NSArray addedGIDs() {
return _addedGIDs;
}
@Override
public String toString() {
return "[ToManySnapshotChanged: sourceGID = " + gid() + "; name = " + _name + "; added = " + ((_addedGIDs == null) ? 0 : _addedGIDs.count()) + "; removed = " + ((_removedGIDs == null) ? 0 : _removedGIDs.count()) + "; removeAll = " + _removeAll + "]";
}
}
}