// OSPArrayFaultSuppressor.h
// Created by Patrick Middleton on 06-Nov-2006.
// Copyright (c) 2006-7 OneStep Solutions Plc. All rights reserved.

// [1] purpose of OSPArrayFaultSuppressor and OSPArrayFaultSuppressorRecord [2] limitations [3] usage

// [1] purpose
// When inserting or deleting detail objects from to-many relationship, EOF will fire the corresponding array fault in the master,
// if it hasn't already been fired.  This can lead to the fetch of thousands of objects which are not needed.  It would be nice
// to selectively defeat such fetches.

// [2] limitations
// This pair of classes doesn't work with database inheritance
// This pair of classes isn't fully automatic
// This pair of classes is liable to cause some (non-array) faults to fire
// This pair of classes won't work properly if there is no inverse to-one relationship matching the to-many relationship
// This pair of classes won't support multiple concurrent EOF stacks with models having common entity names in different schemas
// If the firing of non-array faults is a problem, you're doing something else wrong in the first place

// [3] usage
// EOEditingContext has a delegate method -editingContext:shouldFetchObjectsDescribedByFetchSpecification:
// The doco says "Called by objectsWithFetchSpecification:editingContext:.  If delegate already has appropriate results cached
// it can return them and the fetch will be bypassed.  Returning nil causes the fetch to be propagated to the parentObjectStore."
// That's what we use.  If the editingContext's delegate is not one of our objects but something else like an OSPDGController,
// there should be a hook in that object to pass on the message.
// We create an OSPArrayFaultSuppressor; we can have one shared one per application, or one per BasicController, or have lots
// of transient instances.
// We have our detail object and our master object.
//  If these are faults, they'll be fired.
// We need the master's relationship name for the to-many and the detail's relationship name for the to-one.
//  If we don't supply them, these classes will use EOF metadata to try to find them.
// The master and detail need to be in the same EOEditingContext
//  If they're not, EOF will complain loudly anyway

// We call -[OSPArrayFaultSuppressor suppressMaster:detail:masterDetailName:detailToMasterName:]
//  and we hold on to the OSPArrayFaultSuppressorRecord instance returned
// We call [master addObject:detail toBothSidesOfRelationshipWithKey:masterDetailName] or [master removedObject:detail fromBothSidesOfRelationshipWithKey:masterDetailName]
//  This may trigger a fetch; we ensure that the -editingContext:shouldFetchObjectsDescribedByFetchSpecification: message is passed to the OSPArrayFaultSuppressor.
//  This reviews its OSPArrayFaultSuppressorRecords; if one matches, it will return an NSArray
//   It will examine every object known to the editing context, and if it's of the same class as the detail, it will be added to an array,
//   and that array will be filtered using the fetchspecification's qualifier in-memory.  So, any objects known to the editing context that would be
//   in the to-many relationship cleared fault, if we did no suppression, will be in the returned array.  This is necessary when deleting!
//   This may cause to-one faults to fire.
// We save changes.
// We can examine the OSPArrayFaultSuppressorRecord to see if fault suppression occurred for the master using -[OSPArrayFaultSuppressorRecord tripped]
// There is convenience API to turn all masters for which fault suppression has occurred back into faults -- this will reset -[OSPArrayFaultSuppressorRecord tripped]
// We can remove the OSPArrayFaultSuppressorRecord from the OSPArrayFaultSuppressor, or leave it where it is.

// suppressor = [[OSPArrayFaultSuppressor alloc] init]
//
// contact1 = [[[Contact alloc] init] autorelease];
// [ec insertObject:contact1];
// r1 = [suppressor suppressMaster:company detail:contact1 masterDetailName:@"contacts" detailToMasterName:@"company"];
// [company addObject:contact1 toBothSidesOfRelationshipWithKey:@"contacts"];
// [ec saveChanges];
//
// contact2 = [[[Contact alloc] init] autorelease];
// [ec insertObject:contact2];
// [company addObject:contact2 toBothSidesOfRelationshipWithKey:@"contacts"];
// [ec saveChanges];
//
// [suppressor refaultObjectsInEditingContext:ec];
// [company removeObject:contact2 fromBothSidesOfRelationshipWithKey:@"contacts"];
// [ec saveChanges];
//
// [suppressor removeArrayFaultSuppressorRecord:r1];

#import <EOControl/EOControl.h>
#import "OSPArrayFaultSuppressorRecord.h"

@interface OSPArrayFaultSuppressor : NSObject
{
    NSMutableArray *arrayFaultSuppressorRecordArray;
}


+ (BOOL)debugEnabled;
+ (void)setDebugEnabled:(BOOL)flag;

- (OSPArrayFaultSuppressorRecord *)suppressMaster:(id)masterObject detail:(id)detailObject masterDetailName:(NSString *)masterDetailName detailToMasterName:(NSString *)detailToMasterName;
- (NSArray *)objectsToRefaultInEditingContext:(EOEditingContext *)anEc;
- (void)refaultObjectsInEditingContext:(EOEditingContext *)anEc;

- (NSArray *)editingContext:(EOEditingContext *)editingContext shouldFetchObjectsDescribedByFetchSpecification:(EOFetchSpecification *)fetchSpecification;
- (NSArray *)arrayFaultSuppressorRecordArray;
- (void)addArrayFaultSuppressorRecord:(OSPArrayFaultSuppressorRecord *)aRecord;
- (void)removeArrayFaultSuppressorRecord:(OSPArrayFaultSuppressorRecord *)aRecord;

@end
