2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23 #include "WOKeyPathAssociation.h"
24 #include <NGObjWeb/WOComponent.h>
25 #include "NSObject+WO.h"
28 #if LIB_FOUNDATION_BOEHM_GC
29 # if LIB_FOUNDATION_LIBRARY
30 # include <extensions/GarbageCollector.h>
32 # error no BoehmGC support on this Foundation!
36 #if NeXT_RUNTIME || APPLE_RUNTIME
37 # include <objc/objc.h>
38 # include <objc/objc-api.h>
39 # include <objc/objc-class.h>
41 # define METHOD_NULL NULL
42 # define object_is_instance(XXX) \
43 ((XXX != nil) && CLS_ISCLASS(*((Class *)XXX)))
44 # define sel_get_uid sel_getUid
45 # define class_get_class_method class_getClassMethod
46 # define class_get_instance_method class_getInstanceMethod
48 # define __CLS_INFO(cls) ((cls)->info)
50 # define __CLS_ISINFO(cls, mask) ((__CLS_INFO(cls) & mask) == mask)
53 # define CLS_ISCLASS(cls) ((cls) && __CLS_ISINFO(cls, CLS_CLASS))
57 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY || \
58 APPLE_FOUNDATION_LIBRARY
59 bool _CFDictionaryIsMutable(CFDictionaryRef dict);
66 This is an association class for so called keypaths. It uses extensive
67 caching to reuse calculated method pointers.
69 Note the -kvcIsPreferredInKeyPath method. It is used whether -valueForKey:
70 and -takeValue:forKey: has preference or the objects methods. In usual cases
71 it can be assumed that -valueForKey has the preference, but not in the case
74 WOSession, WOComponent, WOApplication, WOContext
76 which use -valueForKey: as a fallback. Since -valueForKey: is defined as a
77 category on NSObject it eliminates the caching scheme used.
81 // #define HEAVY_DEBUG 1
82 // #define USE_EXCEPTION_HANDLERS 1
85 #if USE_EXCEPTION_HANDLERS
86 # warning using local exception handlers in associations, slows down templates
90 WOKeyType_unknown = 0,
99 IMP method; // real method or takeValue:ForKey:
100 char (*cmethod) (id, SEL);
101 unsigned char (*ucmethod)(id, SEL);
102 int (*imethod) (id, SEL);
103 unsigned int (*uimethod)(id, SEL);
104 short (*smethod) (id, SEL);
105 unsigned short (*usmethod)(id, SEL);
106 const char * (*strmethod)(id, SEL);
107 float (*fmethod)(id, SEL);
108 double (*dmethod)(id, SEL);
112 IMP method; // real method or takeValue:ForKey:
113 void (*omethod) (id, SEL, id);
114 void (*cmethod) (id, SEL, char);
115 void (*ucmethod) (id, SEL, unsigned char);
116 void (*imethod) (id, SEL, int);
117 void (*uimethod) (id, SEL, unsigned int);
118 void (*smethod) (id, SEL, short);
119 void (*usmethod) (id, SEL, unsigned short);
120 void (*strmethod)(id, SEL, const char *);
121 void (*fmethod) (id, SEL, float);
122 void (*dmethod) (id, SEL, double);
132 WOGetMethodType access;
134 NSString *key; // for valueForKey:
136 SEL get; // get method selector
139 unsigned char retType;
140 } WOKeyPathComponent;
152 } WOReturnValueHolder;
154 #define intNumObj(__VAL__) \
155 (__VAL__==0?inum0:(__VAL__==1?inum1:[NumberClass numberWithInt:__VAL__]))
157 #define uintNumObj(__VAL__) \
158 (__VAL__ ==0 ? uinum0 : \
159 (__VAL__==1?uinum1:[NumberClass numberWithUnsignedInt:__VAL__]))
161 @implementation WOKeyPathAssociation
167 static Class NumberClass = Nil;
168 static Class StringClass = Nil;
169 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY || \
170 APPLE_FOUNDATION_LIBRARY
171 static Class NSCFDictionaryClass = Nil;
173 static int debugOn = -1;
175 #define IS_NUMSTR(__VAL__) (__VAL__>=0 && __VAL__<50)
176 #define IS_UNUMSTR(__VAL__) (__VAL__<50)
177 static NSString *numStrings[] = {
178 @"0", @"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9",
179 @"10", @"11", @"12", @"13", @"14", @"15", @"16", @"17", @"18", @"19",
180 @"20", @"21", @"22", @"23", @"24", @"25", @"26", @"27", @"28", @"29",
181 @"30", @"31", @"32", @"33", @"34", @"35", @"36", @"37", @"38", @"39",
182 @"40", @"41", @"42", @"43", @"44", @"45", @"46", @"47", @"48", @"49"
184 static NSNumber *inum0 = nil, *inum1 = nil;
185 static NSNumber *uinum0 = nil, *uinum1 = nil;
188 static BOOL isInitialized = NO;
190 if (isInitialized) return;
192 NSAssert2([super version] == 2,
193 @"invalid superclass (%@) version %i !",
194 NSStringFromClass([self superclass]), [super version]);
197 debugOn = [[[NSUserDefaults standardUserDefaults]
198 objectForKey:@"WODebugKeyPathAssociation"]
201 NumberClass = [NSNumber class];
202 StringClass = [NSString class];
203 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY || \
204 APPLE_FOUNDATION_LIBRARY
205 NSCFDictionaryClass = NSClassFromString(@"NSCFDictionary");
208 inum0 = [[NumberClass numberWithInt:0] retain];
209 inum1 = [[NumberClass numberWithInt:1] retain];
210 uinum0 = [[NumberClass numberWithUnsignedInt:0] retain];
211 uinum1 = [[NumberClass numberWithUnsignedInt:1] retain];
214 static inline WOKeyPathComponent *
215 _getComponent(register WOKeyPathAssociation *self, register unsigned _idx)
217 return (WOKeyPathComponent *)
218 (self->keyPath + (_idx * sizeof(WOKeyPathComponent)));
221 static inline void _freeKeyPathComponent(WOKeyPathComponent *info) {
226 if ((info->type == WOKeyType_kvc) || (info->type == WOKeyType_binding)) {
227 [info->extra.key release];
228 info->extra.key = nil;
235 _parseKeyPath(WOKeyPathAssociation *self,NSString *_keyPath)
237 unsigned keyLen = [_keyPath cStringLength];
242 buf = malloc(keyLen + 1);
245 [_keyPath getCString:buf]; buf[keyLen] = '\0';
247 // get number of components
250 if (*tmp == '.') (self->size)++;
254 self->keyPath = calloc(self->size, sizeof(WOKeyPathComponent));
256 // transfer components
261 for (pos = 0; pos < self->size; pos++) {
262 WOKeyPathComponent *info = _getComponent(self, pos);
264 // goto end or next '.'
265 while ((*tmp != '\0') && (*tmp != '.'))
268 info->keyLen = (tmp - cstr);
269 info->ckey = malloc(info->keyLen + 4);
270 memcpy(info->ckey, cstr, info->keyLen);
271 info->ckey[info->keyLen] = '\0';
273 NSCAssert(strlen(info->ckey) > 0, @"invalid ckey ..");
274 NSCAssert((int)strlen(info->ckey) == (int)info->keyLen,
275 @"size and content differ");
279 info->type = WOKeyType_unknown;
288 - (id)initWithKeyPath:(NSString *)_keyPath {
289 if ([_keyPath length] < 1) {
290 self = [self autorelease];
292 NSLog(@"ERROR: passed invalid keypath (%@) to association !", _keyPath);
295 if ((self = [super init])) {
296 _parseKeyPath(self, _keyPath);
301 NSLog(@"keypaths can not be created using 'init' ..");
302 [NSException raise:@"InvalidUseOfMethodException"
303 format:@"keypaths can not be created using 'init'"];
311 for (cnt = 0; cnt < self->size; cnt++)
312 _freeKeyPathComponent(_getComponent(self, cnt));
321 - (NSString *)keyPath {
322 register unsigned char pos;
327 len = self->size; // size-1 '.' chars and the '\0' char
328 for (pos = 0; pos < self->size; pos++) {
329 WOKeyPathComponent *info = _getComponent(self, pos);
332 buffer = malloc(len + 4);
335 for (pos = 0, len = 0; pos < self->size; pos++) {
336 WOKeyPathComponent *info = _getComponent(self, pos);
342 memcpy(&(buffer[len]), info->ckey, info->keyLen);
347 return [[[StringClass alloc] initWithCStringNoCopy:buffer
352 - (id)initWithString:(NSString *)_s {
353 return [self initWithKeyPath:_s];
358 static inline void _fillInfo(WOKeyPathAssociation *self, id object,
359 WOKeyPathComponent *info)
361 Class clazz = [object class];
362 BOOL needRefill = NO;
366 else if (info->type == WOKeyType_unknown) // first invocation of 'value'
368 else if ((info->object == nil) || (object == nil))
370 else if (info->isa != clazz)
372 else if (info->object != object)
376 // object is the same, can use cached representation
381 struct objc_method *method = NULL;
383 Method_t method = METHOD_NULL;
386 if ((info->type == WOKeyType_kvc) || (info->type == WOKeyType_binding)) {
387 /* release old key */
388 [info->extra.key release];
389 info->extra.key = nil;
392 info->type = WOKeyType_unknown;
393 info->retType = _C_ID;
395 if (*(info->ckey) == '^') { /* a binding key */
396 method = class_get_instance_method(clazz, @selector(valueForBinding:));
397 info->type = WOKeyType_binding;
399 [[StringClass alloc] initWithCString:(info->ckey + 1)];
403 if (object_is_instance(object)) {
404 if ([object kvcIsPreferredInKeyPath]) {
405 method = class_get_instance_method(clazz, @selector(valueForKey:));
407 if (method != METHOD_NULL) {
408 info->type = WOKeyType_kvc;
410 [[StringClass alloc] initWithCString:info->ckey];
413 info->extra.sel.get = sel_get_uid(info->ckey);
414 method = class_get_instance_method(clazz, info->extra.sel.get);
415 if (method != METHOD_NULL)
416 info->type = WOKeyType_method;
420 info->extra.sel.get = sel_get_uid(info->ckey);
421 method = class_get_instance_method(clazz, info->extra.sel.get);
423 if (method != METHOD_NULL)
424 info->type = WOKeyType_method;
427 class_get_instance_method(clazz, @selector(valueForKey:));
428 if (method != METHOD_NULL) {
429 info->type = WOKeyType_kvc;
431 [[StringClass alloc] initWithCString:info->ckey];
436 else { /* object is a class */
437 method = class_get_class_method(object, @selector(valueForKey:));
438 if (method != METHOD_NULL) {
439 info->type = WOKeyType_kvc;
440 info->extra.key = [[StringClass alloc] initWithCString:info->ckey];
443 info->extra.sel.get = sel_get_uid(info->ckey);
445 class_get_class_method(*(Class *)object, info->extra.sel.get);
446 if (method != METHOD_NULL) {
447 info->type = WOKeyType_method;
453 info->object = object;
454 info->isa = [object class];
455 info->isFault = [object isFault];
457 if (method != METHOD_NULL) {
459 info->access.method = method->method_imp;
461 info->access.method = method_get_imp(method);
463 info->retType = *(method->method_types);
467 NSLog(@"type is %i, key is '%s'", info->type, info->ckey);
472 - (NSException *)handleGetException:(NSException *)_exception {
473 static NSString *excKey = @"exception";
474 static NSString *kpKey = @"keyPath";
475 static NSString *assocKey = @"association";
476 static NSString *excName = @"WOKeyPathException";
477 static NSString *excReason = @"couldn't get value for a keypath component";
481 ui = [NSDictionary dictionaryWithObjectsAndKeys:
483 [self keyPath], kpKey,
487 e = [[NSException alloc] initWithName:excName
493 static WOReturnValueHolder _getComponentValue(WOKeyPathAssociation *self,
495 WOKeyPathComponent *info)
497 WOReturnValueHolder retValue;
500 NSCAssert1(info, @"%s: missing info !", __PRETTY_FUNCTION__);
503 _fillInfo(self, object, info);
506 if (info->type == WOKeyType_method) {
508 NSLog(@"get key %s of keyPath %@\n"
509 @" from: 0x%08X[%@]\n"
510 @" via method (ret %c)",
511 info->ckey, [self keyPath], object,
512 NSStringFromClass([object class]), info->retType);
514 #if USE_EXCEPTION_HANDLERS
517 if ((info->retType == _C_ID) || (info->retType == _C_CLASS)) {
518 retValue.object = info->access.method(object, info->extra.sel.get);
520 NSLog(@" got result 0x%08X[%@]: %@", retValue.object,
521 NSStringFromClass([retValue.object class]),
526 switch (info->retType) {
529 retValue.object = info->access.method(object, info->extra.sel.get);
532 retValue.object = object;
537 retValue.c = info->access.ucmethod(object, info->extra.sel.get);
541 retValue.sint = info->access.imethod(object, info->extra.sel.get);
544 retValue.uint = info->access.uimethod(object, info->extra.sel.get);
548 retValue.ss = info->access.smethod(object, info->extra.sel.get);
551 retValue.us = info->access.usmethod(object, info->extra.sel.get);
555 retValue.flt = info->access.fmethod(object, info->extra.sel.get);
559 retValue.dbl = info->access.dmethod(object, info->extra.sel.get);
563 retValue.cstr = info->access.strmethod(object, info->extra.sel.get);
567 NSLog(@"%@: unsupported type '%c' !", self, info->retType);
568 [NSException raise:@"WORuntimeException"
570 @"in WOKeyPathAssociation %@: unsupported type '%c'",
571 self, info->retType];
575 #if USE_EXCEPTION_HANDLERS
578 [[self handleGetException:localException] raise];
582 else if (info->type == WOKeyType_kvc) {
584 NSLog(@"get keyPath %@ from %@ via KVC", [self keyPath], object);
587 NSLog(@"ckey: %s", info->ckey);
588 NSLog(@"key-class: %s", (*(Class *)info->extra.key)->name);
589 NSLog(@"key: %@", info->extra.key);
592 #if LIB_FOUNDATION_BOEHM_GC
593 [GarbageCollector collectGarbages];
597 info->access.method(object, @selector(valueForKey:), info->extra.key);
599 else if (info->type == WOKeyType_binding) {
601 NSLog(@"get keyPath %@ from %@ via binding", [self keyPath], object);
605 info->access.method(object, @selector(valueForBinding:), info->extra.key);
607 else { // unknown || ivar
609 NSLog(@"unknown info type for keyPath %@ from %@ !!",
610 [self keyPath], object);
612 retValue.object = nil;
618 static inline id _objectify(unsigned char _type, WOReturnValueHolder *_value) {
621 //NSLog(@"shall convert value of type '%c'", _type);
626 result = _value->object;
630 result = _value->object;
635 result = [NumberClass numberWithUnsignedChar:_value->c];
639 result = intNumObj(_value->sint);
642 result = uintNumObj(_value->uint);
645 result = [NumberClass numberWithShort:_value->ss];
648 result = [NumberClass numberWithUnsignedShort:_value->us];
651 result = [NumberClass numberWithFloat:_value->flt];
654 result = [NumberClass numberWithDouble:_value->dbl];
658 result = _value->cstr
659 ? [StringClass stringWithCString:_value->cstr]
664 NSLog(@"unsupported type '%c' !", _type);
665 [NSException raise:@"WORuntimeException"
666 format:@"in WOKeyPathAssociation: unsupported type '%c'",
671 //NSLog(@"made %@[0x%08X].", NSStringFromClass([result class]), result);
677 _getValueN(WOKeyPathAssociation *self, unsigned _count, id root)
679 register unsigned cnt;
682 for (cnt = 0; (cnt < _count) && (object != nil); cnt++) {
683 WOKeyPathComponent *info;
684 WOReturnValueHolder retValue;
686 info = _getComponent(self, cnt);
688 NSCAssert1(info, @"%s: missing info !", __PRETTY_FUNCTION__);
690 retValue = _getComponentValue(self, object, info);
692 object = (info->type == WOKeyType_method)
693 ? _objectify(info->retType, &retValue)
697 //NSLog(@"object %@ for keyPath %@", object, [self keyPath]);
702 static inline id _getValue(WOKeyPathAssociation *self, id root) {
703 return _getValueN(self, self->size, root);
706 static id _getOneValue(WOKeyPathAssociation *self, id root) {
707 WOKeyPathComponent *info;
708 WOReturnValueHolder retValue;
710 info = (WOKeyPathComponent *)self->keyPath;
711 retValue = _getComponentValue(self, root, info);
713 return (info->type == WOKeyType_method)
714 ? _objectify(info->retType, &retValue)
718 static inline void _getSetSelName(register unsigned char *buf,
719 register const unsigned char *_key,
720 register unsigned _len) {
732 buf[3] = _key[0]; buf[4] = _key[1];
735 buf[3] = _key[0]; buf[4] = _key[1]; buf[5] = _key[2];
738 buf[3] = _key[0]; buf[4] = _key[1]; buf[5] = _key[2];
739 buf[6] = _key[3]; break;
741 buf[3] = _key[0]; buf[4] = _key[1]; buf[5] = _key[2];
742 buf[6] = _key[3]; buf[7] = _key[4];
745 buf[3] = _key[0]; buf[4] = _key[1]; buf[5] = _key[2];
746 buf[6] = _key[3]; buf[7] = _key[4]; buf[8] = _key[5];
750 memcpy(&(buf[3]), _key, _len);
753 buf[3] = toupper(buf[3]);
755 buf[_len + 4] = '\0';
757 static inline SEL _getSetSel(register const unsigned char *_key,
758 register unsigned _len) {
760 _getSetSelName(buf, _key, _len);
761 return sel_get_uid(buf);
764 static BOOL _setValue(WOKeyPathAssociation *self, id _value, id root) {
765 WOKeyPathComponent *info;
769 object = _getValueN(self, self->size - 1, root);
771 if (object == nil) // nothing to set ..
772 return YES; // receiver == nil isn't an error condition
774 info = _getComponent(self, self->size - 1);
775 NSCAssert(info->keyLen < 255, @"keysize to big ..");
777 _fillInfo(self, object, info);
779 if (info->type == WOKeyType_method) { // determine set-selector
780 SEL setSel = _getSetSel(info->ckey, info->keyLen);
782 if (![object respondsToSelector:setSel]) {
784 NSLog(@"%@: Could not set value for key '%s', "
785 @"object %@ doesn't respond to %@.",
786 self, info->ckey, object,
787 setSel ? NSStringFromSelector(setSel) : @"<NULL>");
794 if ((sm.method = [object methodForSelector:setSel])) {
795 switch (info->retType) {
798 sm.omethod(object, setSel, _value);
802 sm.cmethod(object, setSel, [_value charValue]);
805 sm.ucmethod(object, setSel, [_value unsignedCharValue]);
809 sm.smethod(object, setSel, [_value shortValue]);
812 sm.usmethod(object, setSel, [_value unsignedShortValue]);
816 sm.imethod(object, setSel, [_value intValue]);
819 sm.uimethod(object, setSel, [_value unsignedIntValue]);
823 sm.fmethod(object, setSel, [_value floatValue]);
827 sm.dmethod(object, setSel, [_value doubleValue]);
832 sm.strmethod(object, setSel, NULL);
835 if ((clen = [_value cStringLength]) == 0)
836 sm.strmethod(object, setSel, "");
839 buf = malloc(clen + 4);
840 [_value getCString:buf]; buf[clen] = '\0';
841 sm.strmethod(object, setSel, buf);
848 NSLog(@"%@: cannot set type '%c' yet (key=%s, method=%@) ..",
849 self, info->retType, info->ckey,
850 NSStringFromSelector(setSel));
851 [NSException raise:@"WORuntimeException"
853 @"in WOKeyPathAssociation %@: "
854 @"cannot set type '%c' yet (key=%s, method=%@)",
855 self, info->retType, info->ckey,
856 NSStringFromSelector(setSel)];
862 NSLog(@"%@: did not find method %@ in object %@",
863 self, NSStringFromSelector(setSel), object);
868 else if (info->type == WOKeyType_kvc) { // takeValue:forKey:..
869 NSCAssert(info->extra.key, @"no key object set ..");
870 #if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY || \
871 APPLE_FOUNDATION_LIBRARY
872 if([object isKindOfClass:NSCFDictionaryClass] &&
873 !_CFDictionaryIsMutable((CFDictionaryRef)object))
876 [object takeValue:_value forKey:info->extra.key];
879 else if (info->type == WOKeyType_binding) { // setValue:forBinding:
880 NSCAssert(info->extra.key, @"no key object set ..");
881 [object setValue:_value forBinding:info->extra.key];
885 NSLog(@"%@: Could not set value for key '%s'.", self, info->ckey);
890 - (void)setValue:(id)_value inComponent:(WOComponent *)_component {
892 NSLog(@"%@: set value %@ component %@", self, _value, _component);
894 _setValue(self, _value, _component);
896 - (id)valueInComponent:(WOComponent *)_component {
900 NSLog(@"%@: get value in component %@", self, _component);
902 #if USE_EXCEPTION_HANDLERS
905 result = (self->size > 1)
906 ? _getValue(self, _component)
907 : _getOneValue(self, _component);
908 #if USE_EXCEPTION_HANDLERS
911 fprintf(stderr, "during evaluation of keypath %s:\n %s\n",
912 [[self description] cString],
913 [[localException description] cString]);
915 [localException raise];
922 NSLog(@"%@: get value in component %@", self, _component);
924 return (self->size > 1)
925 ? _getValue(self, _component)
926 : _getOneValue(self, _component);
930 - (BOOL)isValueConstant {
933 - (BOOL)isValueSettable {
939 - (void)setUnsignedIntValue:(unsigned int)_value
940 inComponent:(WOComponent *)_wo
942 WOKeyPathComponent *info;
945 NSLog(@"%@: set uint value %i in component %@", self, _value, _wo);
947 if (self->size > 1) {
948 _setValue(self, uintNumObj(_value), _wo);
952 info = (WOKeyPathComponent *)self->keyPath;
953 NSCAssert(info->keyLen < 255, @"keysize to big ..");
955 _fillInfo(self, _wo, info);
957 if (info->type == WOKeyType_method) { /* determine set-selector */
958 if (info->retType == _C_CHR || info->retType == _C_UCHR ||
959 info->retType == _C_INT || info->retType == _C_UINT) {
963 setSel = _getSetSel(info->ckey, info->keyLen);
964 sm.method = [_wo methodForSelector:setSel];
965 NSAssert1(sm.method, @"didn't find method for key %s", info->ckey);
967 switch (info->retType) {
969 if (((int)_value < -126) || ((int)_value > 127))
970 NSLog(@"%@: value (%i) out of range for char !", self, _value);
971 sm.cmethod(_wo, setSel, (char)_value);
975 if ((_value < 0) || (_value > 255))
976 NSLog(@"%@: value (%i) out of range for uchar !", self, _value);
977 sm.ucmethod(_wo, setSel, (unsigned char)_value);
981 sm.imethod(_wo, setSel, (int)_value);
985 sm.uimethod(_wo, setSel, (unsigned int)_value);
990 [NSException raise:@"WORuntimeException"
992 @"in WOKeyPathAssociation %@: "
993 @"does not handle type %c",
994 self, info->retType];
1000 _setValue(self, uintNumObj(_value), _wo);
1005 if (info->type == WOKeyType_kvc) { // takeValue:forKey:..
1006 NSCAssert(info->extra.key, @"no key object set ..");
1007 [_wo takeValue:uintNumObj(_value) forKey:info->extra.key];
1010 if (info->type == WOKeyType_binding) { // setValue:forBinding:
1011 NSCAssert(info->extra.key, @"no key object set ..");
1012 [_wo setValue:uintNumObj(_value) forBinding:info->extra.key];
1016 NSLog(@"%@: Could not set value for key '%s'.", self, info->ckey);
1018 - (unsigned int)unsignedIntValueInComponent:(WOComponent *)_component {
1019 WOKeyPathComponent *info;
1020 WOReturnValueHolder retValue;
1023 NSLog(@"%@: get uint value in component %@", self, _component);
1026 return [_getValue(self, _component) unsignedIntValue];
1028 info = (WOKeyPathComponent *)self->keyPath;
1029 retValue = _getComponentValue(self, _component, info);
1031 if (info->type == WOKeyType_method) {
1032 switch (info->retType) {
1033 case _C_UINT: return retValue.uint;
1034 case _C_INT: return retValue.sint;
1035 case _C_UCHR: return retValue.c;
1036 case _C_CHR: return retValue.c;
1037 case _C_SHT: return retValue.ss;
1038 case _C_USHT: return retValue.us;
1040 return [_objectify(info->retType, &retValue) unsignedIntValue];
1044 NSLog(@"ret value object for key '%s' is 0x%08X",
1045 info->ckey, retValue.object);
1046 NSLog(@"ret value object class is %@", [retValue.object class]);
1048 return [retValue.object unsignedIntValue];
1051 - (void)setIntValue:(int)_value inComponent:(WOComponent *)_wo {
1052 WOKeyPathComponent *info;
1055 NSLog(@"%@: set int value %i in component %@", self, _value, _wo);
1057 if (self->size > 1) {
1058 _setValue(self, intNumObj(_value), _wo);
1062 info = (WOKeyPathComponent *)self->keyPath;
1063 NSCAssert(info->keyLen < 255, @"keysize to big ..");
1065 _fillInfo(self, _wo, info);
1067 if (info->type == WOKeyType_method) { // determine set-selector
1068 if (info->retType == _C_CHR || info->retType == _C_UCHR ||
1069 info->retType == _C_INT || info->retType == _C_UINT) {
1073 setSel = _getSetSel(info->ckey, info->keyLen);
1074 sm.method = [_wo methodForSelector:setSel];
1075 NSAssert1(sm.method, @"didn't find method for key %s", info->ckey);
1077 switch (info->retType) {
1079 if (((int)_value < -126) || ((int)_value > 127))
1080 NSLog(@"%@: value (%i) out of range for char !", self, _value);
1081 sm.cmethod(_wo, setSel, (char)_value);
1085 if ((_value < 0) || (_value > 255))
1086 NSLog(@"%@: value (%i) out of range for uchar !", self, _value);
1087 sm.ucmethod(_wo, setSel, (unsigned char)_value);
1091 sm.imethod(_wo, setSel, (int)_value);
1095 sm.uimethod(_wo, setSel, (unsigned int)_value);
1100 [NSException raise:@"WORuntimeException"
1102 @"in WOKeyPathAssociation %@: "
1103 @"does not handle type %c",
1104 self, info->retType];
1110 _setValue(self, intNumObj(_value), _wo);
1115 if (info->type == WOKeyType_kvc) { // takeValue:forKey:
1116 NSCAssert(info->extra.key, @"no key object set ..");
1117 [_wo takeValue:intNumObj(_value) forKey:info->extra.key];
1121 if (info->type == WOKeyType_binding) { // setValue:forBinding:
1122 NSCAssert(info->extra.key, @"no key object set ..");
1123 [_wo setValue:intNumObj(_value) forBinding:info->extra.key];
1127 NSLog(@"%@: Could not set value for key '%s'.", self, info->ckey);
1129 - (int)intValueInComponent:(WOComponent *)_component {
1130 WOKeyPathComponent *info;
1131 WOReturnValueHolder retValue;
1134 NSLog(@"%@: get int value in component %@", self, _component);
1137 return [_getValue(self, _component) intValue];
1139 info = (WOKeyPathComponent *)self->keyPath;
1140 retValue = _getComponentValue(self, _component, info);
1142 if (info->type != WOKeyType_method)
1143 return [retValue.object intValue];
1145 switch (info->retType) {
1146 case _C_UINT: return retValue.uint;
1147 case _C_INT: return retValue.sint;
1148 case _C_UCHR: return retValue.c;
1149 case _C_CHR: return retValue.c;
1150 case _C_SHT: return retValue.ss;
1151 case _C_USHT: return retValue.us;
1152 default: return [_objectify(info->retType, &retValue) intValue];
1156 - (void)setBoolValue:(BOOL)_value inComponent:(WOComponent *)_wo {
1157 WOKeyPathComponent *info;
1160 NSLog(@"%@: set bool value %i in component %@", self, _value, _wo);
1162 if (self->size > 1) {
1163 _setValue(self, [NumberClass numberWithBool:_value], _wo);
1167 info = (WOKeyPathComponent *)self->keyPath;
1168 NSCAssert(info->keyLen < 255, @"keysize to big ..");
1170 _fillInfo(self, _wo, info);
1172 if (info->type == WOKeyType_method) { // determine set-selector
1173 if (info->retType == _C_CHR || info->retType == _C_UCHR ||
1174 info->retType == _C_INT || info->retType == _C_UINT) {
1178 setSel = _getSetSel(info->ckey, info->keyLen);
1179 sm.method = [_wo methodForSelector:setSel];
1180 NSAssert1(sm.method, @"didn't find method for key %s", info->ckey);
1182 switch (info->retType) {
1184 sm.cmethod(_wo, setSel, (char)_value);
1188 sm.ucmethod(_wo, setSel, (unsigned char)_value);
1192 sm.imethod(_wo, setSel, (int)_value);
1196 sm.uimethod(_wo, setSel, (unsigned int)_value);
1201 [NSException raise:@"WORuntimeException"
1203 @"in WOKeyPathAssociation %@: "
1204 @"does not handle type %c",
1205 self, info->retType];
1211 _setValue(self, [NumberClass numberWithBool:_value], _wo);
1214 else if (info->type == WOKeyType_kvc) { // takeValue:forKey:
1215 NSCAssert(info->extra.key, @"no key object set ..");
1216 [_wo takeValue:[NumberClass numberWithBool:_value]
1217 forKey:info->extra.key];
1219 else if (info->type == WOKeyType_binding) { // setValue:forBinding:
1220 NSCAssert(info->extra.key, @"no key object set ..");
1221 [_wo setValue:[NumberClass numberWithBool:_value]
1222 forBinding:info->extra.key];
1225 NSLog(@"%@: Could not set value for key '%s'.", self, info->ckey);
1228 - (BOOL)boolValueInComponent:(WOComponent *)_component {
1230 NSLog(@"%@: get bool value in component %@", self, _component);
1233 return [_getValue(self, _component) boolValue];
1235 WOKeyPathComponent *info;
1236 WOReturnValueHolder retValue;
1238 info = (WOKeyPathComponent *)self->keyPath;
1239 retValue = _getComponentValue(self, _component, info);
1241 if (info->type == WOKeyType_method) {
1242 switch (info->retType) {
1243 case _C_UINT: return retValue.uint;
1244 case _C_INT: return retValue.sint;
1245 case _C_UCHR: return retValue.c;
1246 case _C_CHR: return retValue.c;
1247 case _C_SHT: return retValue.ss;
1248 case _C_USHT: return retValue.us;
1251 return [_objectify(info->retType, &retValue) boolValue];
1255 return [retValue.object boolValue];
1259 - (void)setStringValue:(NSString *)_value inComponent:(WOComponent *)_wo {
1261 NSLog(@"%@: set string value '%@' in component %@", self, _value, _wo);
1263 _setValue(self, _value, _wo);
1265 - (NSString *)stringValueInComponent:(WOComponent *)_component {
1266 WOKeyPathComponent *info;
1267 WOReturnValueHolder retValue;
1270 NSLog(@"%@: get string value in component %@", self, _component);
1273 return [_getValue(self, _component) stringValue];
1275 info = (WOKeyPathComponent *)self->keyPath;
1276 retValue = _getComponentValue(self, _component, info);
1278 if (info->type != WOKeyType_method)
1279 return [retValue.object stringValue];
1281 switch (info->retType) {
1283 if (IS_UNUMSTR(retValue.uint)) return numStrings[retValue.uint];
1284 return [StringClass stringWithFormat:@"%u", retValue.uint];
1286 if (IS_NUMSTR(retValue.sint)) return numStrings[retValue.sint];
1287 return [StringClass stringWithFormat:@"%d", retValue.sint];
1289 if (IS_UNUMSTR(retValue.c)) return numStrings[retValue.c];
1290 return [StringClass stringWithFormat:@"%d", (int)retValue.c];
1292 if (IS_NUMSTR((char)retValue.c)) return numStrings[retValue.c];
1293 return [StringClass stringWithFormat:@"%d", (int)retValue.c];
1295 if (IS_NUMSTR(retValue.ss)) return numStrings[retValue.ss];
1296 return [StringClass stringWithFormat:@"%d", (int)retValue.ss];
1298 if (IS_UNUMSTR(retValue.us)) return numStrings[retValue.us];
1299 return [StringClass stringWithFormat:@"%d", (int)retValue.us];
1303 return [StringClass stringWithFormat:@"%0.7g", retValue.flt];
1306 return [_objectify(info->retType, &retValue) stringValue];
1312 - (void)encodeWithCoder:(NSCoder *)_coder {
1313 [_coder encodeObject:[self keyPath]];
1315 - (id)initWithCoder:(NSCoder *)_coder {
1316 return [self initWithKeyPath:[_coder decodeObject]];
1321 - (id)copyWithZone:(NSZone *)_zone {
1322 /* keypath associations are immutable */
1323 return [self retain];
1328 - (NSString *)description {
1329 return [StringClass stringWithFormat:@"<%@[0x%08X]: keyPath=%@>",
1330 NSStringFromClass([self class]), self,
1334 @end /* WOKeyPathAssociation */