//// BXSetRelationProxy.m// BaseTen//// Copyright (C) 2006-2008 Marko Karppinen & Co. LLC.//// Before using this software, please review the available licensing options// by visiting http://basetenframework.org/licensing/ or by contacting// us at sales@karppinen.fi. Without an additional license, this software// may be distributed only in compliance with the GNU General Public License.////// This program is free software; you can redistribute it and/or modify// it under the terms of the GNU General Public License, version 2.0,// as published by the Free Software Foundation.//// This program 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 General Public License for more details.//// You should have received a copy of the GNU General Public License// along with this program; if not, write to the Free Software// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA//// $Id$//#import "BXDatabaseContext.h"#import "BXSetRelationProxy.h"#import "BXDatabaseObjectID.h"#import "BXDatabaseObject.h"#import "BXRelationshipDescription.h"#import "BXRelationshipDescriptionPrivate.h"#import "BXDatabaseContextPrivate.h"//Sadly, this is needed to receive the set proxy and to get a method signature.@interfaceBXSetRelationProxyHelper : NSObject{NSMutableSet*set;idobserver;}-(id)initWithProxy:(id)aProxycontainer:(NSMutableSet*)aContainer;@end@implementationBXSetRelationProxyHelper-(id)initWithProxy:(id)aProxycontainer:(NSMutableSet*)aContainer{if((self=[superinit])){set=aContainer;observer=aProxy;[selfaddObserver:selfforKeyPath:@"set"options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOldcontext:NULL];}returnself;}-(void)dealloc{[selfremoveObserver:selfforKeyPath:@"set"];[superdealloc];}-(void)observeValueForKeyPath:(NSString*)keyPathofObject:(id)objectchange:(NSDictionary*)changecontext:(void*)context{[observerobserveValueForKeyPath:keyPathofObject:objectchange:changecontext:context];}@end/** * \internal * \brief An NSMutableSet-style self-updating container proxy for relationships. * \ingroup auto_containers */@implementationBXSetRelationProxy-(id)BXInitWithArray:(NSMutableArray*)anArray{if((self=[superBXInitWithArray:anArray])){//From now on, receive notificationsmForwardToHelper=YES;mHelper=[[BXSetRelationProxyHelperalloc]initWithProxy:selfcontainer:mContainer];}returnself;}-(void)dealloc{[mHelperrelease];[mRelationshiprelease];[superdealloc];}//FIXME: need we this?#if 0- (id) mutableCopyWithZone: (NSZone *) zone{ BXSetRelationProxy* retval = [super mutableCopyWithZone: zone]; retval->mHelper = [[BXSetRelationProxyHelper alloc] initWithProxy: retval container: retval->mContainer]; retval->mRelationship = [mRelationship copyWithZone: zone]; retval->mForwardToHelper = mForwardToHelper; return retval;}#endif-(void)observeValueForKeyPath:(NSString*)keyPathofObject:(id)objectchange:(NSDictionary*)changecontext:(void*)context{NSMutableSet*oldValue=[NSMutableSetsetWithSet:mContainer];NSSet*changed=nil;NSKeyValueSetMutationKindmutationKind=0;SELundoSelector=NULL;switch([[changeobjectForKey:NSKeyValueChangeKindKey]intValue]){caseNSKeyValueChangeReplacement:{changed=[changeobjectForKey:NSKeyValueChangeNewKey];[oldValueunionSet:[changeobjectForKey:NSKeyValueChangeOldKey]];mutationKind=NSKeyValueSetSetMutation;undoSelector=@selector(setSet:);break;}caseNSKeyValueChangeInsertion:{changed=[changeobjectForKey:NSKeyValueChangeNewKey];[oldValueminusSet:changed];mutationKind=NSKeyValueUnionSetMutation;undoSelector=@selector(minusSet:);break;}caseNSKeyValueChangeRemoval:{changed=[changeobjectForKey:NSKeyValueChangeOldKey];[oldValueunionSet:changed];mutationKind=NSKeyValueMinusSetMutation;undoSelector=@selector(unionSet:);break;}default:break;}if(0!=mutationKind){mChanging=YES;//If context isn't autocommitting, undo and redo happen differently.if([mContextautocommits]&&NULL!=undoSelector)[[mContextundoManager]registerUndoWithTarget:selfselector:undoSelectorobject:changed];//Set mContainer temporarily to old since someone might be KVC-observing.//We also send the KVC posting since we have to replace the container again//before didChange gets sent.idrealContainer=mContainer;mContainer=oldValue;mForwardToHelper=NO;NSString*key=[selfkey];[mOwnerwillChangeValueForKey:keywithSetMutation:mutationKindusingObjects:changed];//Make the change.NSError*localError=nil;[mRelationshipsetTarget:realContainerforObject:mOwnererror:&localError];if(nil!=localError)[[mContextinternalDelegate]databaseContext:mContexthadError:localErrorwillBePassedOn:NO];//Switch back.mContainer=realContainer;mForwardToHelper=YES;[mOwnerdidChangeValueForKey:keywithSetMutation:mutationKindusingObjects:changed];mChanging=NO;}}-(void)fetchedForEntity:(BXEntityDescription*)entitypredicate:(NSPredicate*)predicate{[selfsetFilterPredicate:predicate];}-(void)fetchedForRelationship:(BXRelationshipDescription*)relationshipowner:(BXDatabaseObject*)databaseObjectkey:(NSString*)key{[selfsetEntity:[relationshipdestinationEntity]];[selfsetRelationship:relationship];[selfsetOwner:databaseObject];[selfsetKey:key];}-(void)setRelationship:(BXRelationshipDescription*)relationship{if(mRelationship!=relationship){[mRelationshiprelease];mRelationship=[relationshipretain];}}-(BXRelationshipDescription*)relationship{returnmRelationship;}-(void)forwardInvocation:(NSInvocation*)anInvocation{//Unless we modify the helper's proxy object, changes won't be notified.//Do otherwise only under special circumstances.if(mForwardToHelper)[anInvocationinvokeWithTarget:[mHelpermutableSetValueForKey:@"set"]];else[anInvocationinvokeWithTarget:mContainer];}-(NSString*)key{return[[[mRelationshipname]copy]autorelease];}@end