1/*
2  Copyright (C) 2000-2005 SKYRIX Software AG
3
4  This file is part of SOPE.
5
6  SOPE 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
9  later version.
10
11  SOPE 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.
15
16  You should have received a copy of the GNU Lesser General Public
17  License along with SOPE; see the file COPYING.  If not, write to the
18  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19  02111-1307, USA.
20*/
21
22#include "NSObject+WO.h"
23#include "common.h"
24#include <string.h>
25
26#if APPLE_RUNTIME || NeXT_RUNTIME
27#  include <objc/objc-class.h>
28#endif
29
30#if NeXT_Foundation_LIBRARY || APPLE_FOUNDATION_LIBRARY || \
31    COCOA_Foundation_LIBRARY
32
33#ifndef __APPLE__
34@implementation NSObject(FoundationCompability)
35
36- (id)copyWithZone:(NSZone *)_z {
37  return [self retain];
38}
39
40@end /* NSObject(FoundationCompability) */
41#endif
42
43#endif /* NeXT_Foundation_LIBRARY */
44
45#if GNUSTEP_BASE_LIBRARY
46extern BOOL __objc_responds_to(id, SEL);
47#endif
48
49@implementation NSObject(NGObjWebKVC)
50
51- (BOOL)kvcIsPreferredInKeyPath {
52  return NO;
53}
54
55@end /* NSObject(NGObjWebKVC) */
56
57@implementation NSDictionary(NGObjWebKVC)
58
59- (BOOL)kvcIsPreferredInKeyPath {
60  return YES;
61}
62
63@end /* NSDictionary(NGObjWebKVC) */
64
65@implementation NSObject(Faults)
66#ifndef __APPLE__
67+ (BOOL)isFault {
68  return NO;
69}
70- (BOOL)isFault {
71  return NO;
72}
73#endif
74@end /* NSObject(Faults) */
75
76// ******************** KVC methods ********************
77
78static inline void _getSetSelName(register unsigned char *buf,
79                                  register const unsigned char *_key,
80                                  register unsigned _len) {
81  buf[0] = 's';
82  buf[1] = 'e';
83  buf[2] = 't';
84
85  switch (_len) {
86    case 0: break;
87
88    case 1:
89      buf[3] = _key[0];
90      break;
91    case 2:
92      buf[3] = _key[0];
93      buf[4] = _key[1];
94      break;
95    case 3:
96      buf[3] = _key[0];
97      buf[4] = _key[1];
98      buf[5] = _key[2];
99      break;
100    case 4:
101      buf[3] = _key[0];
102      buf[4] = _key[1];
103      buf[5] = _key[2];
104      buf[6] = _key[3];
105      break;
106    case 5:
107      buf[3] = _key[0];
108      buf[4] = _key[1];
109      buf[5] = _key[2];
110      buf[6] = _key[3];
111      buf[7] = _key[4];
112      break;
113    case 6:
114      buf[3] = _key[0];
115      buf[4] = _key[1];
116      buf[5] = _key[2];
117      buf[6] = _key[3];
118      buf[7] = _key[4];
119      buf[8] = _key[5];
120      break;
121
122    default:
123      memcpy(&(buf[3]), _key, _len);
124      break;
125  }
126  buf[3] = toupper(buf[3]);
127  buf[_len + 3] = ':';
128  buf[_len + 4] = '\0';
129}
130static inline SEL _getSetSel(register const unsigned char *_key,
131                             register unsigned _len) {
132  char buf[259];
133  _getSetSelName((unsigned char *)buf, _key, _len);
134  return sel_registerName(buf);
135}
136
137typedef union {
138  IMP            method; // real method or takeValue:ForKey:
139  char           (*cmethod) (id, SEL);
140  unsigned char  (*ucmethod)(id, SEL);
141  int            (*imethod) (id, SEL);
142  unsigned int   (*uimethod)(id, SEL);
143  short          (*smethod) (id, SEL);
144  unsigned short (*usmethod)(id, SEL);
145  const char *   (*strmethod)(id, SEL);
146  float          (*fmethod)(id, SEL);
147  double         (*dmethod)(id, SEL);
148} WOGetMethodType;
149
150typedef union {
151  IMP  method; // real method or takeValue:ForKey:
152  void (*omethod)  (id, SEL, id);
153  void (*cmethod)  (id, SEL, char);
154  void (*ucmethod) (id, SEL, unsigned char);
155  void (*imethod)  (id, SEL, int);
156  void (*uimethod) (id, SEL, unsigned int);
157  void (*smethod)  (id, SEL, short);
158  void (*usmethod) (id, SEL, unsigned short);
159  void (*strmethod)(id, SEL, const char *);
160  void (*fmethod)  (id, SEL, float);
161  void (*dmethod)  (id, SEL, double);
162} WOSetMethodType;
163
164BOOL WOSetKVCValueUsingMethod(id object, NSString *_key, id _value) {
165  NSMethodSignature *sig = nil;
166  WOSetMethodType   sm;
167  const char        *argType;
168  SEL               setSel;
169  unsigned          keyLen;
170  char              *buf;
171
172  if (object == nil) return NO;
173  if (_key   == nil) return NO;
174
175  keyLen = [_key cStringLength];
176
177  buf = malloc(keyLen + 2);
178  [_key getCString:buf];
179  setSel = _getSetSel((unsigned char *)buf, keyLen);
180  free(buf); buf = NULL;
181
182  if (setSel == NULL) // no such selector
183    return NO;
184
185  sig = [object methodSignatureForSelector:setSel];
186  if (sig == nil) // no signature
187    return NO;
188
189  sm.method = [object methodForSelector:setSel];
190  if (sm.method) {
191    argType = [sig getArgumentTypeAtIndex:2];
192
193    switch (*argType) {
194      case _C_CLASS:
195      case _C_ID:
196        sm.omethod(object, setSel, _value);
197        break;
198
199      case _C_CHR:
200        sm.cmethod(object, setSel, [(NSValue *)_value charValue]);
201        break;
202      case _C_UCHR:
203        sm.ucmethod(object, setSel, [_value unsignedCharValue]);
204        break;
205
206      case _C_SHT:
207        sm.smethod(object, setSel, [_value shortValue]);
208        break;
209      case _C_USHT:
210        sm.usmethod(object, setSel, [_value unsignedShortValue]);
211        break;
212
213      case _C_INT:
214        sm.imethod(object, setSel, [_value intValue]);
215        break;
216      case _C_UINT:
217        sm.uimethod(object, setSel, [_value unsignedIntValue]);
218        break;
219
220      case _C_FLT:
221        sm.fmethod(object, setSel, [_value floatValue]);
222        break;
223
224      case _C_DBL:
225        sm.dmethod(object, setSel, [_value doubleValue]);
226        break;
227
228      case _C_CHARPTR: {
229        char *s;
230        s = NGMallocAtomic([_value cStringLength] + 1);
231        [_value getCString:s];
232        sm.strmethod(object, setSel, s);
233        NGFree(s); s = NULL;
234        break;
235      }
236
237      default:
238        NSLog(@"%s: cannot set type '%c' yet (key=%@, method=%@) ..",
239              __PRETTY_FUNCTION__,
240              *argType, _key, NSStringFromSelector(setSel));
241        [NSException raise:@"WORuntimeException"
242                     format:@"cannot set type '%c' yet (key=%@, method=%@)",
243                       *argType, _key, NSStringFromSelector(setSel)];
244        return NO;
245    }
246    return YES;
247  }
248  else // did not find method
249    return NO;
250}
251
252IMP WOGetKVCGetMethod(id object, NSString *_key) {
253  register SEL getSel;
254
255  if (object == nil) return NULL;
256  if (_key   == nil) return NULL;
257
258#if (defined(__GNU_LIBOBJC__) && (__GNU_LIBOBJC__ < 20100911))
259  {
260    unsigned keyLen;
261    char     *buf;
262
263    keyLen = [_key cStringLength];
264    buf = malloc(keyLen + 1);
265    [_key getCString:buf]; buf[keyLen] = '\0';
266    getSel = sel_registerName(buf);
267    free(buf);
268
269    if (getSel == NULL) // no such selector
270      return NULL;
271#if GNUSTEP_BASE_LIBRARY
272    if (!__objc_responds_to(object, getSel))
273      return NULL;
274#endif
275
276    return [object methodForSelector:getSel];
277  }
278#else
279  if ((getSel = NSSelectorFromString(_key)) == NULL) // no such selector
280    return NULL;
281
282  if ([object respondsToSelector:getSel])
283    return [object methodForSelector:getSel];
284
285  return NULL;
286#endif
287}
288
289id WOGetKVCValueUsingMethod(id object, NSString *_key) {
290  NSMethodSignature *sig = nil;
291  WOGetMethodType   gm;
292  const char        *retType;
293  SEL               getSel;
294  unsigned          keyLen;
295
296  if (object == nil) return nil;
297  if (_key   == nil) return nil;
298
299  // TODO: this su***
300  // TODO: add support for ivars
301  keyLen = [_key cStringLength];
302
303  {
304    char *buf;
305    buf = malloc(keyLen + 1);
306    [_key getCString:buf];
307    getSel = sel_registerName(buf);
308    if (getSel == NULL) // no such selector
309      return nil;
310    free(buf); buf = NULL;
311  }
312#if GNUSTEP_BASE_LIBRARY && !defined(__GNUSTEP_RUNTIME__)
313  if (!__objc_responds_to(object, getSel))
314    return nil;
315#endif
316
317  gm.method = [object methodForSelector:getSel];
318  if (gm.method == NULL) // no such method
319    return nil;
320
321  sig = [object methodSignatureForSelector:getSel];
322  if (sig == nil) // no signature
323    return nil;
324
325  {
326    static Class NSNumberClass = Nil;
327    id value = nil;
328
329    if (NSNumberClass == Nil)
330      NSNumberClass = [NSNumber class];
331
332    retType = [sig methodReturnType];
333
334    switch (*retType) {
335      case _C_CLASS:
336      case _C_ID:
337        value = gm.method(object, getSel);
338        value = AUTORELEASE(RETAIN(value));
339        break;
340
341      case _C_CHR:
342        value = [NSNumberClass numberWithChar:gm.cmethod(object, getSel)];
343        break;
344      case _C_UCHR:
345        value = [NSNumberClass numberWithUnsignedChar:
346                                 gm.ucmethod(object, getSel)];
347        break;
348
349      case _C_SHT:
350        value = [NSNumberClass numberWithShort:gm.smethod(object, getSel)];
351        break;
352      case _C_USHT:
353        value = [NSNumberClass numberWithUnsignedShort:
354                                 gm.usmethod(object, getSel)];
355        break;
356
357      case _C_INT:
358        value = [NSNumberClass numberWithInt:gm.imethod(object, getSel)];
359        break;
360      case _C_UINT:
361        value = [NSNumberClass numberWithUnsignedInt:
362                                 gm.uimethod(object, getSel)];
363        break;
364
365      case _C_FLT:
366        value = [NSNumberClass numberWithFloat:gm.fmethod(object, getSel)];
367        break;
368      case _C_DBL:
369        value = [NSNumberClass numberWithDouble:gm.dmethod(object, getSel)];
370        break;
371
372      case _C_CHARPTR: {
373        const char *cstr = gm.strmethod(object, getSel);
374        value = cstr ? [NSString stringWithCString:cstr] : nil;
375        break;
376      }
377
378      default:
379        NSLog(@"%s: cannot get type '%c' yet (key=%@, method=%@) ..",
380              __PRETTY_FUNCTION__,
381              *retType, _key, NSStringFromSelector(getSel));
382        [NSException raise:@"WORuntimeException"
383                     format:@"cannot get type '%c' yet (key=%@, method=%@)",
384                       *retType, _key, NSStringFromSelector(getSel)];
385        return nil;
386    }
387    return value;
388  }
389}
390