mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-25 17:51:01 +00:00
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@29753 72102866-910b-0410-8b05-ffd578937521
525 lines
14 KiB
Objective-C
525 lines
14 KiB
Objective-C
/* Mutable array proxies for GNUstep's KeyValueCoding
|
|
Copyright (C) 2007 Free Software Foundation, Inc.
|
|
|
|
Written by: Chris Farber <chris@chrisfarber.net>
|
|
|
|
This file is part of the GNUstep Base Library.
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library 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 Lesser General Public
|
|
License along with this library; if not, write to the Free
|
|
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02111 USA.
|
|
|
|
$Date: 2007-06-08 04: 04: 14 -0400 (Fri, 08 Jun 2007) $ $Revision: 25230 $
|
|
*/
|
|
|
|
#import "common.h"
|
|
#import "Foundation/NSInvocation.h"
|
|
#import "Foundation/NSIndexSet.h"
|
|
#import "Foundation/NSKeyValueObserving.h"
|
|
|
|
@interface NSKeyValueMutableArray : NSMutableArray
|
|
{
|
|
@protected
|
|
id object;
|
|
NSString *key;
|
|
NSMutableArray *array;
|
|
BOOL otherChangeInProgress;
|
|
}
|
|
|
|
+ (NSKeyValueMutableArray *) arrayForKey: (NSString *)aKey ofObject: (id)anObject;
|
|
- (id) initWithKey: (NSString *)aKey ofObject: (id)anObject;
|
|
|
|
@end
|
|
|
|
@interface NSKeyValueFastMutableArray : NSKeyValueMutableArray
|
|
{
|
|
@private
|
|
NSInvocation *insertObjectInvocation;
|
|
NSInvocation *removeObjectInvocation;
|
|
NSInvocation *replaceObjectInvocation;
|
|
}
|
|
|
|
+ (id) arrayForKey: (NSString *)aKey ofObject: (id)anObject
|
|
withCapitalizedKey: (const char *)capitalized;
|
|
|
|
- (id) initWithKey: (NSString *)aKey ofObject: (id)anObject
|
|
withCapitalizedKey: (const char *)capitalized;
|
|
|
|
@end
|
|
|
|
@interface NSKeyValueSlowMutableArray : NSKeyValueMutableArray
|
|
{
|
|
@private
|
|
NSInvocation *setArrayInvocation;
|
|
}
|
|
|
|
+ (id) arrayForKey: (NSString *)aKey ofObject: (id)anObject
|
|
withCapitalizedKey: (const char *)capitalized;
|
|
|
|
- (id) initWithKey: (NSString *)aKey ofObject: (id)anObject
|
|
withCapitalizedKey: (const char *)capitalized;
|
|
|
|
@end
|
|
|
|
@interface NSKeyValueIvarMutableArray : NSKeyValueMutableArray
|
|
{
|
|
@private
|
|
}
|
|
|
|
+ (id) arrayForKey: (NSString *)aKey ofObject: (id)anObject;
|
|
|
|
- (id) initWithKey: (NSString *)aKey ofObject: (id)anObject;
|
|
|
|
@end
|
|
|
|
|
|
@implementation NSKeyValueMutableArray
|
|
|
|
+ (NSKeyValueMutableArray *) arrayForKey: (NSString *)aKey
|
|
ofObject: (id)anObject
|
|
{
|
|
NSKeyValueMutableArray *proxy;
|
|
unsigned size = [aKey maximumLengthOfBytesUsingEncoding:
|
|
NSUTF8StringEncoding];
|
|
char keybuf[size + 1];
|
|
[aKey getCString: keybuf
|
|
maxLength: size + 1
|
|
encoding: NSUTF8StringEncoding];
|
|
if (islower(*keybuf))
|
|
{
|
|
*keybuf = toupper(*keybuf);
|
|
}
|
|
|
|
proxy = [NSKeyValueFastMutableArray arrayForKey: aKey
|
|
ofObject: anObject
|
|
withCapitalizedKey: keybuf];
|
|
if (proxy == nil)
|
|
{
|
|
proxy = [NSKeyValueSlowMutableArray arrayForKey: aKey
|
|
ofObject: anObject
|
|
withCapitalizedKey: keybuf];
|
|
|
|
if (proxy == nil)
|
|
{
|
|
proxy = [NSKeyValueIvarMutableArray arrayForKey: aKey
|
|
ofObject: anObject];
|
|
}
|
|
}
|
|
return proxy;
|
|
}
|
|
|
|
- (id) initWithKey: (NSString *)aKey ofObject: (id)anObject
|
|
{
|
|
if ((self = [super init]) != nil)
|
|
{
|
|
object = anObject;
|
|
key = [aKey copy];
|
|
otherChangeInProgress = NO;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (unsigned) count
|
|
{
|
|
if (array == nil)
|
|
{
|
|
array = [object valueForKey: key];
|
|
}
|
|
return [array count];
|
|
}
|
|
|
|
- (id) objectAtIndex: (unsigned)index
|
|
{
|
|
if (array == nil)
|
|
{
|
|
array = [object valueForKey: key];
|
|
}
|
|
return [array objectAtIndex: index];
|
|
}
|
|
|
|
- (void) addObject: (id)anObject
|
|
{
|
|
[self insertObject: anObject atIndex: [self count]];
|
|
}
|
|
|
|
- (void) removeLastObject
|
|
{
|
|
[self removeObjectAtIndex: ([self count] - 1)];
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation NSKeyValueFastMutableArray
|
|
|
|
+ (id) arrayForKey: (NSString *)aKey ofObject: (id)anObject
|
|
withCapitalizedKey: (const char *)capitalized
|
|
{
|
|
return [[[self alloc] initWithKey: aKey ofObject: anObject
|
|
withCapitalizedKey: capitalized] autorelease];
|
|
}
|
|
|
|
- (id) initWithKey: (NSString *)aKey ofObject: (id)anObject
|
|
withCapitalizedKey: (const char *)capitalized
|
|
{
|
|
SEL insert;
|
|
SEL remove;
|
|
SEL replace;
|
|
|
|
insert = NSSelectorFromString
|
|
([NSString stringWithFormat: @"insertObject:in%sAtIndex:", capitalized]);
|
|
remove = NSSelectorFromString
|
|
([NSString stringWithFormat: @"removeObjectFrom%sAtIndex:", capitalized]);
|
|
if (!([anObject respondsToSelector: insert]
|
|
&& [anObject respondsToSelector: remove]))
|
|
{
|
|
DESTROY(self);
|
|
return nil;
|
|
}
|
|
replace = NSSelectorFromString
|
|
([NSString stringWithFormat: @"replaceObjectIn%sAtIndex:withObject:",
|
|
capitalized]);
|
|
|
|
if ((self = [super initWithKey: aKey ofObject: anObject]) != nil)
|
|
{
|
|
insertObjectInvocation = [[NSInvocation invocationWithMethodSignature:
|
|
[anObject methodSignatureForSelector: insert]] retain];
|
|
[insertObjectInvocation setTarget: anObject];
|
|
[insertObjectInvocation setSelector: insert];
|
|
removeObjectInvocation = [[NSInvocation invocationWithMethodSignature:
|
|
[anObject methodSignatureForSelector: remove]] retain];
|
|
[removeObjectInvocation setTarget: anObject];
|
|
[removeObjectInvocation setSelector: remove];
|
|
if ([anObject respondsToSelector: replace])
|
|
{
|
|
replaceObjectInvocation
|
|
= [[NSInvocation invocationWithMethodSignature:
|
|
[anObject methodSignatureForSelector: replace]] retain];
|
|
[replaceObjectInvocation setTarget: anObject];
|
|
[replaceObjectInvocation setSelector: replace];
|
|
}
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void) dealloc
|
|
{
|
|
[insertObjectInvocation release];
|
|
[removeObjectInvocation release];
|
|
[replaceObjectInvocation release];
|
|
[super dealloc];
|
|
}
|
|
|
|
- (void) removeObjectAtIndex: (unsigned)index
|
|
{
|
|
NSIndexSet *indexes = nil;
|
|
|
|
if (!otherChangeInProgress)
|
|
{
|
|
indexes = [NSIndexSet indexSetWithIndex: index];
|
|
[object willChange: NSKeyValueChangeRemoval
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
}
|
|
[removeObjectInvocation setArgument: &index atIndex: 2];
|
|
[removeObjectInvocation invoke];
|
|
if (!otherChangeInProgress)
|
|
{
|
|
[object didChange: NSKeyValueChangeRemoval
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
}
|
|
}
|
|
|
|
- (void) insertObject: (id)anObject atIndex: (unsigned)index
|
|
{
|
|
NSIndexSet *indexes = nil;
|
|
|
|
if (!otherChangeInProgress)
|
|
{
|
|
indexes = [NSIndexSet indexSetWithIndex: index];
|
|
[object willChange: NSKeyValueChangeInsertion
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
}
|
|
[insertObjectInvocation setArgument: &anObject atIndex: 2];
|
|
[insertObjectInvocation setArgument: &index atIndex: 3];
|
|
[insertObjectInvocation invoke];
|
|
if (!otherChangeInProgress)
|
|
{
|
|
[object didChange: NSKeyValueChangeInsertion
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
}
|
|
}
|
|
|
|
- (void) replaceObjectAtIndex: (unsigned)index withObject: (id)anObject
|
|
{
|
|
NSIndexSet *indexes = nil;
|
|
BOOL triggerNotifications = !otherChangeInProgress;
|
|
|
|
if (triggerNotifications)
|
|
{
|
|
otherChangeInProgress = YES;
|
|
indexes = [NSIndexSet indexSetWithIndex: index];
|
|
[object willChange: NSKeyValueChangeReplacement
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
}
|
|
if (replaceObjectInvocation)
|
|
{
|
|
[replaceObjectInvocation setArgument: &index atIndex: 2];
|
|
[replaceObjectInvocation setArgument: &anObject atIndex: 3];
|
|
[replaceObjectInvocation invoke];
|
|
}
|
|
else
|
|
{
|
|
[self removeObjectAtIndex: index];
|
|
[self insertObject: anObject atIndex: index];
|
|
}
|
|
if (triggerNotifications)
|
|
{
|
|
[object didChange: NSKeyValueChangeReplacement
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
otherChangeInProgress = NO;
|
|
}
|
|
}
|
|
|
|
|
|
@end
|
|
|
|
@implementation NSKeyValueSlowMutableArray
|
|
|
|
+ (id) arrayForKey: (NSString *)aKey ofObject: (id)anObject
|
|
withCapitalizedKey: (const char *)capitalized
|
|
{
|
|
return [[[self alloc] initWithKey: aKey ofObject: anObject
|
|
withCapitalizedKey: capitalized] autorelease];
|
|
}
|
|
|
|
- (id) initWithKey: (NSString *)aKey ofObject: (id)anObject
|
|
withCapitalizedKey: (const char *)capitalized;
|
|
|
|
{
|
|
SEL set = NSSelectorFromString([NSString stringWithFormat:
|
|
@"set%s:", capitalized]);
|
|
|
|
if (![anObject respondsToSelector: set])
|
|
{
|
|
DESTROY(self);
|
|
return nil;
|
|
}
|
|
|
|
if ((self = [super initWithKey: aKey ofObject: anObject]) != nil)
|
|
{
|
|
setArrayInvocation = [[NSInvocation invocationWithMethodSignature:
|
|
[anObject methodSignatureForSelector: set]] retain];
|
|
[setArrayInvocation setSelector: set];
|
|
[setArrayInvocation setTarget: anObject];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void) removeObjectAtIndex: (unsigned)index
|
|
{
|
|
NSIndexSet *indexes = nil;
|
|
NSMutableArray *temp;
|
|
|
|
if (!otherChangeInProgress)
|
|
{
|
|
indexes = [NSIndexSet indexSetWithIndex: index];
|
|
[object willChange: NSKeyValueChangeRemoval
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
}
|
|
|
|
temp = [NSMutableArray arrayWithArray: [object valueForKey: key]];
|
|
[temp removeObjectAtIndex: index];
|
|
|
|
[setArrayInvocation setArgument: &temp atIndex: 2];
|
|
[setArrayInvocation invoke];
|
|
|
|
if (!otherChangeInProgress)
|
|
{
|
|
[object didChange: NSKeyValueChangeRemoval
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
}
|
|
}
|
|
|
|
- (void) insertObject: (id)anObject atIndex: (unsigned)index
|
|
{
|
|
NSIndexSet *indexes = nil;
|
|
NSMutableArray *temp;
|
|
|
|
if (!otherChangeInProgress)
|
|
{
|
|
indexes = [NSIndexSet indexSetWithIndex: index];
|
|
[object willChange: NSKeyValueChangeInsertion
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
}
|
|
|
|
temp = [NSMutableArray arrayWithArray: [object valueForKey: key]];
|
|
[temp insertObject: anObject atIndex: index];
|
|
|
|
[setArrayInvocation setArgument: &temp atIndex: 2];
|
|
[setArrayInvocation invoke];
|
|
|
|
if (!otherChangeInProgress)
|
|
{
|
|
[object didChange: NSKeyValueChangeInsertion
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
}
|
|
}
|
|
|
|
- (void) replaceObjectAtIndex: (unsigned)index withObject: (id)anObject
|
|
{
|
|
NSIndexSet *indexes = nil;
|
|
NSMutableArray *temp;
|
|
|
|
if (!otherChangeInProgress)
|
|
{
|
|
indexes = [NSIndexSet indexSetWithIndex: index];
|
|
[object willChange: NSKeyValueChangeReplacement
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
}
|
|
|
|
temp = [NSMutableArray arrayWithArray: [object valueForKey: key]];
|
|
[temp removeObjectAtIndex: index];
|
|
[temp insertObject: anObject atIndex: index];
|
|
|
|
[setArrayInvocation setArgument: &temp atIndex: 2];
|
|
[setArrayInvocation invoke];
|
|
|
|
if (!otherChangeInProgress)
|
|
{
|
|
[object didChange: NSKeyValueChangeReplacement
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
}
|
|
}
|
|
|
|
|
|
@end
|
|
|
|
|
|
@implementation NSKeyValueIvarMutableArray
|
|
|
|
+ (id) arrayForKey: (NSString *)aKey ofObject: (id)anObject
|
|
{
|
|
return [[[self alloc] initWithKey: aKey ofObject: anObject] autorelease];
|
|
}
|
|
|
|
- (id) initWithKey: (NSString *)aKey ofObject: (id)anObject
|
|
{
|
|
if ((self = [super initWithKey: aKey ofObject: anObject]) != nil)
|
|
{
|
|
unsigned size = [aKey maximumLengthOfBytesUsingEncoding:
|
|
NSUTF8StringEncoding];
|
|
char cKey[size + 2];
|
|
char *cKeyPtr = &cKey[0];
|
|
const char *type = 0;
|
|
BOOL found = NO;
|
|
int offset;
|
|
|
|
cKey[0] = '_';
|
|
[aKey getCString: cKeyPtr + 1
|
|
maxLength: size + 1
|
|
encoding: NSUTF8StringEncoding];
|
|
|
|
if (!GSObjCFindVariable (anObject, cKeyPtr, &type, &size, &offset))
|
|
found = GSObjCFindVariable (anObject, ++cKeyPtr, &type, &size, &offset);
|
|
if (found)
|
|
{
|
|
array = GSObjCGetVal (anObject, cKeyPtr, NULL, type, size, offset);
|
|
}
|
|
else
|
|
{
|
|
array = [object valueForKey: key];
|
|
}
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void) addObject: (id)anObject
|
|
{
|
|
NSIndexSet *indexes = [NSIndexSet indexSetWithIndex: [array count]];
|
|
|
|
[object willChange: NSKeyValueChangeInsertion
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
[array addObject: anObject];
|
|
[object didChange: NSKeyValueChangeInsertion
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
}
|
|
|
|
- (void) removeObjectAtIndex: (unsigned)index
|
|
{
|
|
NSIndexSet *indexes = [NSIndexSet indexSetWithIndex: index];
|
|
|
|
[object willChange: NSKeyValueChangeRemoval
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
[array removeObjectAtIndex: index];
|
|
[object didChange: NSKeyValueChangeRemoval
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
}
|
|
|
|
- (void) insertObject: (id)anObject atIndex: (unsigned)index
|
|
{
|
|
NSIndexSet *indexes = [NSIndexSet indexSetWithIndex: index];
|
|
|
|
[object willChange: NSKeyValueChangeInsertion
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
[array insertObject: anObject atIndex: index];
|
|
[object didChange: NSKeyValueChangeInsertion
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
}
|
|
|
|
- (void) removeLastObject
|
|
{
|
|
NSIndexSet *indexes = [NSIndexSet indexSetWithIndex: [array count] - 1];
|
|
|
|
[object willChange: NSKeyValueChangeRemoval
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
[array removeObjectAtIndex: [indexes firstIndex]];
|
|
[object didChange: NSKeyValueChangeRemoval
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
}
|
|
|
|
- (void) replaceObjectAtIndex: (unsigned)index withObject: (id)anObject
|
|
{
|
|
NSIndexSet *indexes = [NSIndexSet indexSetWithIndex: index];
|
|
|
|
[object willChange: NSKeyValueChangeReplacement
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
[array replaceObjectAtIndex: index withObject: anObject];
|
|
[object didChange: NSKeyValueChangeReplacement
|
|
valuesAtIndexes: indexes
|
|
forKey: key];
|
|
}
|
|
|
|
|
|
@end
|