libs-base/Source/NSKeyValueCoding.m

951 lines
23 KiB
Mathematica
Raw Permalink Normal View History

/** Implementation of KeyValueCoding for GNUStep
Copyright (C) 2000,2002 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
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
Lesser 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., 31 Milk Street #960789 Boston, MA 02196 USA.
<title>NSKeyValueCoding informal protocol reference</title>
$Date$ $Revision$
*/
#import "common.h"
#import "Foundation/NSArray.h"
#import "Foundation/NSAutoreleasePool.h"
#import "Foundation/NSDictionary.h"
#import "Foundation/NSEnumerator.h"
#import "Foundation/NSException.h"
#import "Foundation/NSKeyValueCoding.h"
#import "Foundation/NSMethodSignature.h"
#import "Foundation/NSNull.h"
#import "Foundation/NSSet.h"
#import "Foundation/NSValue.h"
/* For the NSKeyValueMutableArray and NSKeyValueMutableSet classes
*/
#include "NSKeyValueMutableArray.m"
#include "NSKeyValueMutableSet.m"
#if defined(__OBJC2__)
#import "NSKeyValueCoding+Caching.h"
#endif
/* this should move into autoconf once it's accepted */
#define WANT_DEPRECATED_KVC_COMPAT 1
#ifdef WANT_DEPRECATED_KVC_COMPAT
static IMP takePath = 0;
static IMP takeValue = 0;
static IMP takePathKVO = 0;
static IMP takeValueKVO = 0;
static inline void setupCompat()
{
if (takePath == 0)
{
Class c = NSClassFromString(@"GSKVOBase");
takePathKVO = [c instanceMethodForSelector:
@selector(takeValue:forKeyPath:)];
takePath = [NSObject instanceMethodForSelector:
@selector(takeValue:forKeyPath:)];
takeValueKVO = [c instanceMethodForSelector:
@selector(takeValue:forKey:)];
takeValue = [NSObject instanceMethodForSelector:
@selector(takeValue:forKey:)];
}
}
#endif
static void
SetValueForKey(NSObject *self, id anObject, const char *key, unsigned size)
{
SEL sel = 0;
const char *type = 0;
int off = 0;
if (size > 0)
{
const char *name;
char buf[size + 6];
char lo;
char hi;
memcpy(buf, "_set", 4);
memcpy(&buf[4], key, size);
lo = buf[4];
hi = islower(lo) ? toupper(lo) : lo;
buf[4] = hi;
buf[size + 4] = ':';
buf[size + 5] = '\0';
name = &buf[1]; // setKey:
type = NULL;
sel = sel_getUid(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
name = buf; // _setKey:
sel = sel_getUid(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
sel = 0;
if ([[self class] accessInstanceVariablesDirectly] == YES)
{
buf[size + 4] = '\0';
buf[3] = '_';
buf[4] = lo;
name = &buf[3]; // _key
if (GSObjCFindVariable(self, name, &type, &size, &off) == NO)
{
buf[4] = hi;
buf[3] = 's';
buf[2] = 'i';
buf[1] = '_';
name = &buf[1]; // _isKey
if (GSObjCFindVariable(self,
name, &type, &size, &off) == NO)
{
buf[4] = lo;
name = &buf[4]; // key
if (GSObjCFindVariable(self,
name, &type, &size, &off) == NO)
{
buf[4] = hi;
buf[3] = 's';
buf[2] = 'i';
name = &buf[2]; // isKey
GSObjCFindVariable(self,
name, &type, &size, &off);
}
}
}
}
}
else
{
GSOnceFLog(@"Key-value access using _setKey: is deprecated:");
}
}
}
Initial port of WinObjC's KVO implementation to GNUstep (#420) * GSAtomic: Add prefix to macro definitions * NSKVOSupport: Import * NSKVOSupport: Add test cases * NSKVOSwizzling: Ugly C Rewrite * NSKeyValueObserving: Use old implementation as fallback * NSKeyValueObserving: Rename TypeEncodingCases header * NSKVOSupport: Fix new objects not being added to NSKeyValueChangeNew set on set mutation * NSKeyValueMutableSet: Fix will and didChange notifications for set operations * NSKeyValueMutableSet: Document Accessor Search Patterns * NSKVOSupport: Add toMany test * NSKeyValueCoding: Change notifications when changing value via setValue:forKey: * NSKVOSupport: Add more tests * NSKVOSupport: Do not wrap block in try/finally to avoid crash in windows * NSKVOSwizzling: use _alloca on Windows * NSKVOSupport: Do not autorelease newWithObservee: * NSKVOSupport: Do not leak Observee and TestFacade objects * Improve runtime detection in makefile * Add file extension of source file in GNUMakefile * NSKVOSupport: Remove @status comments * NSKVOSupport: Implement private notify method * NSUserDefaults: KVO Support and fix macOS incompatibilities * NSKeyValueObserving: Set old to null if nil * NSKeyValueObserving: Remove cached new value * NSMethodSignature: Add signature cache * NSKVOSupport: Remove ObjC2 features and mark tests failing on GCC as hopeful * Call class method instead of private _keyPathsForValuesAffectingValueForKey * Move _keyPathsForValuesAffectingValueForKey body into class method and statically construct empty NSSet * NSUserDefaults: Change notification should contain old value from other domains aswell * NSUserDefaults: Fetch new value from all domains * NSKVOInternal: Fixup filename in header * NSUserDefaults: Go through search list instead of only one domain in KVO change * Making indentation a bit less worse * Add NSUserDefaults KVO tests * NSKVOSupport: NSUserDefaults test small fixes * Add autoreleasepool * NSUserDefaults: Only emit change notifications if value changed * Avoid compiler warnings and tidy some of the whitespace/formatting --------- Co-authored-by: Frederik Seiffert <frederik@algoriddim.com> Co-authored-by: rfm <richardfrithmacdonald@gmail.com> Co-authored-by: rfm <rfm@gnu.org>
2024-11-10 16:05:23 +00:00
GSObjCSetVal(self, key, anObject, sel, type, size, off);
}
#if !defined(__OBJC2__)
static id ValueForKey(NSObject *self, const char *key, unsigned size)
{
SEL sel = 0;
int off = 0;
const char *type = NULL;
if (size > 0)
{
const char *name;
char buf[size + 5];
char lo;
char hi;
memcpy(buf, "_get", 4);
memcpy(&buf[4], key, size);
buf[size + 4] = '\0';
lo = buf[4];
hi = islower(lo) ? toupper(lo) : lo;
buf[4] = hi;
name = &buf[1]; // getKey
sel = sel_getUid(name);
if ([self respondsToSelector: sel] == NO)
{
buf[4] = lo;
name = &buf[4]; // key
sel = sel_getUid(name);
if ([self respondsToSelector: sel] == NO)
{
buf[4] = hi;
buf[3] = 's';
buf[2] = 'i';
name = &buf[2]; // isKey
sel = sel_getUid(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
sel = 0;
}
}
}
if (sel == 0 && [[self class] accessInstanceVariablesDirectly] == YES)
{
buf[4] = hi;
name = buf; // _getKey
sel = sel_getUid(name);
if ([self respondsToSelector: sel] == NO)
{
buf[4] = lo;
buf[3] = '_';
name = &buf[3]; // _key
sel = sel_getUid(name);
if ([self respondsToSelector: sel] == NO)
{
sel = 0;
}
}
if (sel == 0)
{
if (GSObjCFindVariable(self, name, &type, &size, &off) == NO)
{
buf[4] = hi;
buf[3] = 's';
buf[2] = 'i';
buf[1] = '_';
name = &buf[1]; // _isKey
if (!GSObjCFindVariable(self, name, &type, &size, &off))
{
buf[4] = lo;
name = &buf[4]; // key
if (!GSObjCFindVariable(self, name, &type, &size, &off))
{
buf[4] = hi;
buf[3] = 's';
buf[2] = 'i';
name = &buf[2]; // isKey
GSObjCFindVariable(self, name, &type, &size, &off);
}
}
}
}
}
}
return GSObjCGetVal(self, key, sel, type, size, off);
}
#endif
@implementation NSObject(KeyValueCoding)
+ (BOOL) accessInstanceVariablesDirectly
{
return YES;
}
- (NSDictionary*) dictionaryWithValuesForKeys: (NSArray*)keys
{
NSMutableDictionary *dictionary;
NSEnumerator *enumerator;
id key;
#ifdef WANT_DEPRECATED_KVC_COMPAT
static IMP o = 0;
/* Backward compatibility hack */
if (o == 0)
{
o = [NSObject instanceMethodForSelector:
@selector(valuesForKeys:)];
}
if ([self methodForSelector: @selector(valuesForKeys:)] != o)
{
return [self valuesForKeys: keys];
}
#endif
dictionary = [NSMutableDictionary dictionaryWithCapacity: [keys count]];
enumerator = [keys objectEnumerator];
while ((key = [enumerator nextObject]) != nil)
{
id value = [self valueForKey: key];
if (value == nil)
{
value = [NSNull null];
}
[dictionary setObject: value forKey: key];
}
return dictionary;
}
- (NSMutableSet*) mutableSetValueForKey: (NSString*)aKey
{
return [NSKeyValueMutableSet setForKey: aKey ofObject: self];
}
- (NSMutableSet*) mutableSetValueForKeyPath: (NSString*)aKey
{
NSRange r = [aKey rangeOfString: @"." options: NSLiteralSearch];
if (r.length == 0)
{
return [self mutableSetValueForKey: aKey];
}
else
{
NSString *key = [aKey substringToIndex: r.location];
NSString *path = [aKey substringFromIndex: NSMaxRange(r)];
return [[self valueForKey: key] mutableSetValueForKeyPath: path];
}
}
- (NSMutableArray*) mutableArrayValueForKey: (NSString*)aKey
{
return [NSKeyValueMutableArray arrayForKey: aKey ofObject: self];
}
- (NSMutableArray*) mutableArrayValueForKeyPath: (NSString*)aKey
{
NSRange r = [aKey rangeOfString: @"." options: NSLiteralSearch];
if (r.length == 0)
{
return [self mutableArrayValueForKey: aKey];
}
else
{
NSString *key = [aKey substringToIndex: r.location];
NSString *path = [aKey substringFromIndex: NSMaxRange(r)];
return [[self valueForKey: key] mutableArrayValueForKeyPath: path];
}
}
- (void) setNilValueForKey: (NSString*)aKey
{
#ifdef WANT_DEPRECATED_KVC_COMPAT
static IMP o = 0;
/* Backward compatibility hack */
if (o == 0)
{
o = [NSObject instanceMethodForSelector:
@selector(unableToSetNilForKey:)];
}
if ([self methodForSelector: @selector(unableToSetNilForKey:)] != o)
{
[self unableToSetNilForKey: aKey];
return;
}
#endif
[NSException raise: NSInvalidArgumentException
format: @"%@ -- %@ 0x%"PRIxPTR": Given nil value to set for key \"%@\"",
NSStringFromSelector(_cmd), NSStringFromClass([self class]),
(NSUInteger)self, aKey];
}
- (void) setValue: (id)anObject forKey: (NSString*)aKey
{
unsigned size = [aKey length] * 8;
char key[size + 1];
Initial port of WinObjC's KVO implementation to GNUstep (#420) * GSAtomic: Add prefix to macro definitions * NSKVOSupport: Import * NSKVOSupport: Add test cases * NSKVOSwizzling: Ugly C Rewrite * NSKeyValueObserving: Use old implementation as fallback * NSKeyValueObserving: Rename TypeEncodingCases header * NSKVOSupport: Fix new objects not being added to NSKeyValueChangeNew set on set mutation * NSKeyValueMutableSet: Fix will and didChange notifications for set operations * NSKeyValueMutableSet: Document Accessor Search Patterns * NSKVOSupport: Add toMany test * NSKeyValueCoding: Change notifications when changing value via setValue:forKey: * NSKVOSupport: Add more tests * NSKVOSupport: Do not wrap block in try/finally to avoid crash in windows * NSKVOSwizzling: use _alloca on Windows * NSKVOSupport: Do not autorelease newWithObservee: * NSKVOSupport: Do not leak Observee and TestFacade objects * Improve runtime detection in makefile * Add file extension of source file in GNUMakefile * NSKVOSupport: Remove @status comments * NSKVOSupport: Implement private notify method * NSUserDefaults: KVO Support and fix macOS incompatibilities * NSKeyValueObserving: Set old to null if nil * NSKeyValueObserving: Remove cached new value * NSMethodSignature: Add signature cache * NSKVOSupport: Remove ObjC2 features and mark tests failing on GCC as hopeful * Call class method instead of private _keyPathsForValuesAffectingValueForKey * Move _keyPathsForValuesAffectingValueForKey body into class method and statically construct empty NSSet * NSUserDefaults: Change notification should contain old value from other domains aswell * NSUserDefaults: Fetch new value from all domains * NSKVOInternal: Fixup filename in header * NSUserDefaults: Go through search list instead of only one domain in KVO change * Making indentation a bit less worse * Add NSUserDefaults KVO tests * NSKVOSupport: NSUserDefaults test small fixes * Add autoreleasepool * NSUserDefaults: Only emit change notifications if value changed * Avoid compiler warnings and tidy some of the whitespace/formatting --------- Co-authored-by: Frederik Seiffert <frederik@algoriddim.com> Co-authored-by: rfm <richardfrithmacdonald@gmail.com> Co-authored-by: rfm <rfm@gnu.org>
2024-11-10 16:05:23 +00:00
BOOL shouldNotify = [[self class] automaticallyNotifiesObserversForKey:aKey];
#ifdef WANT_DEPRECATED_KVC_COMPAT
IMP o = [self methodForSelector: @selector(takeValue:forKey:)];
setupCompat();
if (o != takeValue && o != takeValueKVO)
{
(*o)(self, @selector(takeValue:forKey:), anObject, aKey);
return;
}
#endif
[aKey getCString: key
maxLength: size + 1
encoding: NSUTF8StringEncoding];
size = strlen(key);
Initial port of WinObjC's KVO implementation to GNUstep (#420) * GSAtomic: Add prefix to macro definitions * NSKVOSupport: Import * NSKVOSupport: Add test cases * NSKVOSwizzling: Ugly C Rewrite * NSKeyValueObserving: Use old implementation as fallback * NSKeyValueObserving: Rename TypeEncodingCases header * NSKVOSupport: Fix new objects not being added to NSKeyValueChangeNew set on set mutation * NSKeyValueMutableSet: Fix will and didChange notifications for set operations * NSKeyValueMutableSet: Document Accessor Search Patterns * NSKVOSupport: Add toMany test * NSKeyValueCoding: Change notifications when changing value via setValue:forKey: * NSKVOSupport: Add more tests * NSKVOSupport: Do not wrap block in try/finally to avoid crash in windows * NSKVOSwizzling: use _alloca on Windows * NSKVOSupport: Do not autorelease newWithObservee: * NSKVOSupport: Do not leak Observee and TestFacade objects * Improve runtime detection in makefile * Add file extension of source file in GNUMakefile * NSKVOSupport: Remove @status comments * NSKVOSupport: Implement private notify method * NSUserDefaults: KVO Support and fix macOS incompatibilities * NSKeyValueObserving: Set old to null if nil * NSKeyValueObserving: Remove cached new value * NSMethodSignature: Add signature cache * NSKVOSupport: Remove ObjC2 features and mark tests failing on GCC as hopeful * Call class method instead of private _keyPathsForValuesAffectingValueForKey * Move _keyPathsForValuesAffectingValueForKey body into class method and statically construct empty NSSet * NSUserDefaults: Change notification should contain old value from other domains aswell * NSUserDefaults: Fetch new value from all domains * NSKVOInternal: Fixup filename in header * NSUserDefaults: Go through search list instead of only one domain in KVO change * Making indentation a bit less worse * Add NSUserDefaults KVO tests * NSKVOSupport: NSUserDefaults test small fixes * Add autoreleasepool * NSUserDefaults: Only emit change notifications if value changed * Avoid compiler warnings and tidy some of the whitespace/formatting --------- Co-authored-by: Frederik Seiffert <frederik@algoriddim.com> Co-authored-by: rfm <richardfrithmacdonald@gmail.com> Co-authored-by: rfm <rfm@gnu.org>
2024-11-10 16:05:23 +00:00
if (shouldNotify)
{
[self willChangeValueForKey: aKey];
}
SetValueForKey(self, anObject, key, size);
Initial port of WinObjC's KVO implementation to GNUstep (#420) * GSAtomic: Add prefix to macro definitions * NSKVOSupport: Import * NSKVOSupport: Add test cases * NSKVOSwizzling: Ugly C Rewrite * NSKeyValueObserving: Use old implementation as fallback * NSKeyValueObserving: Rename TypeEncodingCases header * NSKVOSupport: Fix new objects not being added to NSKeyValueChangeNew set on set mutation * NSKeyValueMutableSet: Fix will and didChange notifications for set operations * NSKeyValueMutableSet: Document Accessor Search Patterns * NSKVOSupport: Add toMany test * NSKeyValueCoding: Change notifications when changing value via setValue:forKey: * NSKVOSupport: Add more tests * NSKVOSupport: Do not wrap block in try/finally to avoid crash in windows * NSKVOSwizzling: use _alloca on Windows * NSKVOSupport: Do not autorelease newWithObservee: * NSKVOSupport: Do not leak Observee and TestFacade objects * Improve runtime detection in makefile * Add file extension of source file in GNUMakefile * NSKVOSupport: Remove @status comments * NSKVOSupport: Implement private notify method * NSUserDefaults: KVO Support and fix macOS incompatibilities * NSKeyValueObserving: Set old to null if nil * NSKeyValueObserving: Remove cached new value * NSMethodSignature: Add signature cache * NSKVOSupport: Remove ObjC2 features and mark tests failing on GCC as hopeful * Call class method instead of private _keyPathsForValuesAffectingValueForKey * Move _keyPathsForValuesAffectingValueForKey body into class method and statically construct empty NSSet * NSUserDefaults: Change notification should contain old value from other domains aswell * NSUserDefaults: Fetch new value from all domains * NSKVOInternal: Fixup filename in header * NSUserDefaults: Go through search list instead of only one domain in KVO change * Making indentation a bit less worse * Add NSUserDefaults KVO tests * NSKVOSupport: NSUserDefaults test small fixes * Add autoreleasepool * NSUserDefaults: Only emit change notifications if value changed * Avoid compiler warnings and tidy some of the whitespace/formatting --------- Co-authored-by: Frederik Seiffert <frederik@algoriddim.com> Co-authored-by: rfm <richardfrithmacdonald@gmail.com> Co-authored-by: rfm <rfm@gnu.org>
2024-11-10 16:05:23 +00:00
if (shouldNotify)
{
[self didChangeValueForKey: aKey];
}
}
- (void) setValue: (id)anObject forKeyPath: (NSString*)aKey
{
NSRange r = [aKey rangeOfString: @"." options: NSLiteralSearch];
#ifdef WANT_DEPRECATED_KVC_COMPAT
IMP o = [self methodForSelector: @selector(takeValue:forKeyPath:)];
setupCompat();
if (o != takePath && o != takePathKVO)
{
(*o)(self, @selector(takeValue:forKeyPath:), anObject, aKey);
return;
}
#endif
if (r.length == 0)
{
[self setValue: anObject forKey: aKey];
}
else
{
NSString *key = [aKey substringToIndex: r.location];
NSString *path = [aKey substringFromIndex: NSMaxRange(r)];
[[self valueForKey: key] setValue: anObject forKeyPath: path];
}
}
- (void) setValue: (id)anObject forUndefinedKey: (NSString*)aKey
{
NSDictionary *dict;
NSException *exp;
#ifdef WANT_DEPRECATED_KVC_COMPAT
static IMP o = 0;
/* Backward compatibility hack */
if (o == 0)
{
o = [NSObject instanceMethodForSelector:
@selector(handleTakeValue:forUnboundKey:)];
}
if ([self methodForSelector: @selector(handleTakeValue:forUnboundKey:)] != o)
{
[self handleTakeValue: anObject forUnboundKey: aKey];
return;
}
#endif
dict = [NSDictionary dictionaryWithObjectsAndKeys:
(anObject ? (id)anObject : (id)@"(nil)"), @"NSTargetObjectUserInfoKey",
(aKey ? (id)aKey : (id)@"(nil)"), @"NSUnknownUserInfoKey",
nil];
exp = [NSException exceptionWithName: NSUndefinedKeyException
reason: @"Unable to set value for undefined key"
userInfo: dict];
[exp raise];
}
- (void) setValuesForKeysWithDictionary: (NSDictionary*)aDictionary
{
NSEnumerator *enumerator;
NSString *key;
#ifdef WANT_DEPRECATED_KVC_COMPAT
static IMP o = 0;
/* Backward compatibility hack */
if (o == 0)
{
o = [NSObject instanceMethodForSelector:
@selector(takeValuesFromDictionary:)];
}
if ([self methodForSelector: @selector(takeValuesFromDictionary:)] != o)
{
[self takeValuesFromDictionary: aDictionary];
return;
}
#endif
enumerator = [aDictionary keyEnumerator];
while ((key = [enumerator nextObject]) != nil)
{
[self setValue: [aDictionary objectForKey: key] forKey: key];
}
}
- (BOOL) validateValue: (id*)aValue
forKey: (NSString*)aKey
error: (NSError**)anError
{
unsigned size;
if (aValue == 0 || (size = [aKey length] * 8) == 0)
{
[NSException raise: NSInvalidArgumentException format: @"nil argument"];
}
else
{
char name[size + 16];
SEL sel;
BOOL (*imp)(id,SEL,id*,id*);
memcpy(name, "validate", 8);
[aKey getCString: &name[8]
maxLength: size + 1
encoding: NSUTF8StringEncoding];
size = strlen(&name[8]);
memcpy(&name[size + 8], ":error:", 7);
name[size + 15] = '\0';
if (islower(name[8]))
{
name[8] = toupper(name[8]);
}
sel = sel_getUid(name);
if (sel != 0 && [self respondsToSelector: sel] == YES)
{
imp = (BOOL (*)(id,SEL,id*,id*))[self methodForSelector: sel];
return (*imp)(self, sel, aValue, anError);
}
}
return YES;
}
- (BOOL) validateValue: (id*)aValue
forKeyPath: (NSString*)aKey
error: (NSError**)anError
{
NSRange r = [aKey rangeOfString: @"." options: NSLiteralSearch];
if (r.length == 0)
{
return [self validateValue: aValue forKey: aKey error: anError];
}
else
{
NSString *key = [aKey substringToIndex: r.location];
NSString *path = [aKey substringFromIndex: NSMaxRange(r)];
return [[self valueForKey: key] validateValue: aValue
forKeyPath: path
error: anError];
}
}
- (id) valueForKey: (NSString*)aKey
{
#if defined(__OBJC2__)
return valueForKeyWithCaching(self, aKey);
#else
unsigned size = [aKey length] * 8;
char key[size + 1];
[aKey getCString: key
maxLength: size + 1
encoding: NSUTF8StringEncoding];
size = strlen(key);
return ValueForKey(self, key, size);
#endif
}
- (id) valueForKeyPath: (NSString*)aKey
{
NSRange r = [aKey rangeOfString: @"." options: NSLiteralSearch];
if (r.length == 0)
{
return [self valueForKey: aKey];
}
else
{
NSString *key = [aKey substringToIndex: r.location];
NSString *path = [aKey substringFromIndex: NSMaxRange(r)];
return [[self valueForKey: key] valueForKeyPath: path];
}
}
- (id) valueForUndefinedKey: (NSString*)aKey
{
NSDictionary *dict;
NSException *exp;
NSString *reason;
#ifdef WANT_DEPRECATED_KVC_COMPAT
static IMP o = 0;
/* Backward compatibility hack */
if (o == 0)
{
o = [NSObject instanceMethodForSelector:
@selector(handleQueryWithUnboundKey:)];
}
if ([self methodForSelector: @selector(handleQueryWithUnboundKey:)] != o)
{
return [self handleQueryWithUnboundKey: aKey];
}
#endif
dict = [NSDictionary dictionaryWithObjectsAndKeys:
self, @"NSTargetObjectUserInfoKey",
(aKey ? (id)aKey : (id)@"(nil)"), @"NSUnknownUserInfoKey",
nil];
reason = [NSString stringWithFormat:
@"Unable to find value for key \"%@\" of object %@ (%@)",
aKey, self, [self class]];
exp = [NSException exceptionWithName: NSUndefinedKeyException
reason: reason
userInfo: dict];
[exp raise];
return nil;
}
#ifdef WANT_DEPRECATED_KVC_COMPAT
+ (BOOL) useStoredAccessor
{
return YES;
}
- (id) storedValueForKey: (NSString*)aKey
{
unsigned size;
if ([[self class] useStoredAccessor] == NO)
{
return [self valueForKey: aKey];
}
size = [aKey length] * 8;
if (size > 0)
{
SEL sel = 0;
const char *type = NULL;
int off = 0;
const char *name;
char key[size + 1];
char buf[size + 5];
char lo;
char hi;
memcpy(buf, "_get", 4);
[aKey getCString: key
maxLength: size + 1
encoding: NSUTF8StringEncoding];
size = strlen(key);
memcpy(&buf[4], key, size);
buf[size + 4] = '\0';
lo = buf[4];
hi = islower(lo) ? toupper(lo) : lo;
buf[4] = hi;
name = buf; // _getKey
sel = sel_getUid(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
buf[3] = '_';
buf[4] = lo;
name = &buf[3]; // _key
sel = sel_getUid(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
sel = 0;
}
}
if (sel == 0)
{
if ([[self class] accessInstanceVariablesDirectly] == YES)
{
// _key
if (GSObjCFindVariable(self, name, &type, &size, &off) == NO)
{
name = &buf[4]; // key
GSObjCFindVariable(self, name, &type, &size, &off);
}
}
if (type == NULL)
{
buf[3] = 't';
buf[4] = hi;
name = &buf[1]; // getKey
sel = sel_getUid(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
buf[4] = lo;
name = &buf[4]; // key
sel = sel_getUid(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
sel = 0;
}
}
}
}
if (sel != 0 || type != NULL)
{
return GSObjCGetVal(self, key, sel, type, size, off);
}
}
[self handleTakeValue: nil forUnboundKey: aKey];
return nil;
}
- (void) takeStoredValue: (id)anObject forKey: (NSString*)aKey
{
unsigned size;
if ([[self class] useStoredAccessor] == NO)
{
[self takeValue: anObject forKey: aKey];
return;
}
size = [aKey length] * 8;
if (size > 0)
{
SEL sel;
const char *type;
int off;
const char *name;
char key[size + 1];
char buf[size + 6];
char lo;
char hi;
memcpy(buf, "_set", 4);
[aKey getCString: key
maxLength: size + 1
encoding: NSUTF8StringEncoding];
size = strlen(key);
memcpy(&buf[4], key, size);
buf[size + 4] = '\0';
lo = buf[4];
hi = islower(lo) ? toupper(lo) : lo;
buf[4] = hi;
buf[size + 4] = ':';
buf[size + 5] = '\0';
name = buf; // _setKey:
type = NULL;
off = 0;
sel = sel_getUid(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
sel = 0;
if ([[self class] accessInstanceVariablesDirectly] == YES)
{
buf[size + 4] = '\0';
buf[4] = lo;
buf[3] = '_';
name = &buf[3]; // _key
if (GSObjCFindVariable(self, name, &type, &size, &off) == NO)
{
name = &buf[4]; // key
GSObjCFindVariable(self, name, &type, &size, &off);
}
}
if (type == NULL)
{
buf[size + 4] = ':';
buf[4] = hi;
buf[3] = 't';
name = &buf[1]; // setKey:
sel = sel_getUid(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
sel = 0;
}
}
}
if (sel != 0 || type != NULL)
{
GSObjCSetVal(self, key, anObject, sel, type, size, off);
return;
}
}
[self handleTakeValue: anObject forUnboundKey: aKey];
}
- (void) takeStoredValuesFromDictionary: (NSDictionary*)aDictionary
{
NSEnumerator *enumerator = [aDictionary keyEnumerator];
NSNull *null = [NSNull null];
NSString *key;
while ((key = [enumerator nextObject]) != nil)
{
id obj = [aDictionary objectForKey: key];
if (obj == null)
{
obj = nil;
}
[self takeStoredValue: obj forKey: key];
}
}
- (id) handleQueryWithUnboundKey: (NSString*)aKey
{
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
self, @"NSTargetObjectUserInfoKey",
(aKey ? (id)aKey : (id)@"(nil)"), @"NSUnknownUserInfoKey",
nil];
NSException *exp = [NSException exceptionWithName: NSUndefinedKeyException
reason: @"Unable to find value for key"
userInfo: dict];
GSOnceMLog(@"This method is deprecated, use -valueForUndefinedKey:");
[exp raise];
return nil;
}
- (void) handleTakeValue: (id)anObject forUnboundKey: (NSString*)aKey
{
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
(anObject ? (id)anObject : (id)@"(nil)"), @"NSTargetObjectUserInfoKey",
(aKey ? (id)aKey : (id)@"(nil)"), @"NSUnknownUserInfoKey",
nil];
NSException *exp = [NSException exceptionWithName: NSUndefinedKeyException
reason: @"Unable to set value for key"
userInfo: dict];
GSOnceMLog(@"This method is deprecated, use -setValue:forUndefinedKey:");
[exp raise];
}
- (void) takeValue: (id)anObject forKey: (NSString*)aKey
{
SEL sel = 0;
const char *type = 0;
int off = 0;
unsigned size = [aKey length] * 8;
char key[size + 1];
GSOnceMLog(@"This method is deprecated, use -setValue:forKey:");
[aKey getCString: key
maxLength: size + 1
encoding: NSUTF8StringEncoding];
size = strlen(key);
if (size > 0)
{
const char *name;
char buf[size + 6];
char lo;
char hi;
memcpy(buf, "_set", 4);
memcpy(&buf[4], key, size);
lo = buf[4];
hi = islower(lo) ? toupper(lo) : lo;
buf[4] = hi;
buf[size + 4] = ':';
buf[size + 5] = '\0';
name = &buf[1]; // setKey:
type = NULL;
sel = sel_getUid(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
name = buf; // _setKey:
sel = sel_getUid(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
sel = 0;
if ([[self class] accessInstanceVariablesDirectly] == YES)
{
buf[size + 4] = '\0';
buf[3] = '_';
buf[4] = lo;
name = &buf[4]; // key
if (GSObjCFindVariable(self, name, &type, &size, &off) == NO)
{
name = &buf[3]; // _key
GSObjCFindVariable(self, name, &type, &size, &off);
}
}
}
}
}
GSObjCSetVal(self, key, anObject, sel, type, size, off);
}
- (void) takeValue: (id)anObject forKeyPath: (NSString*)aKey
{
NSRange r = [aKey rangeOfString: @"." options: NSLiteralSearch];
GSOnceMLog(@"This method is deprecated, use -setValue:forKeyPath:");
if (r.length == 0)
{
[self takeValue: anObject forKey: aKey];
}
else
{
NSString *key = [aKey substringToIndex: r.location];
NSString *path = [aKey substringFromIndex: NSMaxRange(r)];
[[self valueForKey: key] takeValue: anObject forKeyPath: path];
}
}
- (void) takeValuesFromDictionary: (NSDictionary*)aDictionary
{
NSEnumerator *enumerator = [aDictionary keyEnumerator];
NSNull *null = [NSNull null];
NSString *key;
GSOnceMLog(@"This method is deprecated, use -setValuesForKeysWithDictionary:");
while ((key = [enumerator nextObject]) != nil)
{
id obj = [aDictionary objectForKey: key];
if (obj == null)
{
obj = nil;
}
[self takeValue: obj forKey: key];
}
}
- (void) unableToSetNilForKey: (NSString*)aKey
{
GSOnceMLog(@"This method is deprecated, use -setNilValueForKey:");
[NSException raise: NSInvalidArgumentException format:
@"%@ -- %@ 0x%"PRIxPTR": Given nil value to set for key \"%@\"",
NSStringFromSelector(_cmd), NSStringFromClass([self class]),
(NSUInteger)self, aKey];
}
- (NSDictionary*) valuesForKeys: (NSArray*)keys
{
NSMutableDictionary *dict;
NSNull *null = [NSNull null];
unsigned count = [keys count];
unsigned pos;
GSOnceMLog(@"This method is deprecated, use -dictionaryWithValuesForKeys:");
dict = [NSMutableDictionary dictionaryWithCapacity: count];
for (pos = 0; pos < count; pos++)
{
NSString *key = [keys objectAtIndex: pos];
id val = [self valueForKey: key];
if (val == nil)
{
val = null;
}
[dict setObject: val forKey: key];
}
return AUTORELEASE([dict copy]);
}
#endif
@end