package er.extensions.eof;
import java.util.Enumeration;
import com.webobjects.eoaccess.EOAccessArrayFaultHandler;
import com.webobjects.eocontrol.EOEditingContext;
import com.webobjects.eocontrol.EOEnterpriseObject;
import com.webobjects.eocontrol.EOFaultHandler;
import com.webobjects.eocontrol.EOFaulting;
import com.webobjects.eocontrol.EOGlobalID;
import com.webobjects.eocontrol.EOKeyGlobalID;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSMutableArray;
import com.webobjects.foundation.NSMutableDictionary;
/**
* Provides a cache for to-many faults. Useful when you have a lot (say a
* few thousand) objects for which you need to access a to-many relationship
* that hasn't been fetched yet.
*
* The idea is that instead of batch-faulting you could fetch all the entries
* for a given to-many relationship in one fetch (as raw rows), group them in
* memory and register the faults here. Then you set up your EODatabaseContext
* delegate to use this object to try and clear the fault first.
*
* @author ak
*
*/
public class ERXArrayFaultCache {
private NSMutableDictionary cache = new NSMutableDictionary();
/**
* Register the to-many faults by entity name and relationship name. The entries
* are a dictionary where the key is the source EOGlobalID for the relationship
* and the values are arrays of global IDs for the destination.
* @param entityName
* @param relationshipName
* @param entries
*/
public void registerRelationshipCacheEntries(String entityName, String relationshipName, NSDictionary entries) {
synchronized (cache) {
if(entries == null) {
cache.removeObjectForKey(entityName + "\0" + relationshipName);
} else {
cache.setObjectForKey(entries.immutableClone(), entityName + "\0" + relationshipName);
}
}
}
private NSDictionary relationshipCacheEntriesForEntity(String entityName, String relationshipName) {
synchronized (cache) {
return (NSDictionary) cache.objectForKey(entityName + "\0" + relationshipName);
}
}
/**
* Attempts to clear a fault by looking up the source global id and faulting in the corresponding
* destination values.
* @param obj
* @return true if the fault could be cleared or it wasn't a fault in the first place, false if not
*/
public boolean clearFault(Object obj) {
if(!EOFaultHandler.isFault(obj)) {
return true;
}
EOFaulting fault = (EOFaulting)obj;
if (fault.faultHandler() instanceof EOAccessArrayFaultHandler) {
EOAccessArrayFaultHandler handler = (EOAccessArrayFaultHandler) fault.faultHandler();
EOKeyGlobalID sourceGid = handler.sourceGlobalID();
EOEditingContext ec = handler.editingContext();
synchronized (cache) {
NSDictionary entries = relationshipCacheEntriesForEntity(sourceGid.entityName(), handler.relationshipName());
if(entries != null) {
NSArray gids = (NSArray) entries.objectForKey(sourceGid);
if(gids != null) {
NSMutableArray eos = new NSMutableArray(gids.count());
for (Enumeration enumerator = gids.objectEnumerator(); enumerator.hasMoreElements();) {
EOGlobalID gid = (EOGlobalID) enumerator.nextElement();
EOEnterpriseObject eo = ec.faultForGlobalID(gid, ec);
eos.addObject(eo);
}
EOFaultHandler.clearFault(obj);
((NSMutableArray)fault).addObjectsFromArray(eos);
return true;
}
}
}
}
return false;
}
}