/*
  Copyright (C) 2000-2004 SKYRIX Software AG

  This file is part of OpenGroupware.org.

  OGo 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, or (at your option) any
  later version.

  OGo 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 OGo; see the file COPYING.  If not, write to the
  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
  02111-1307, USA.
*/
// $Id$

#include "EOKeyValueCoding.h"
#include "EONull.h"
#include "common.h"

#if GNU_RUNTIME
#  include <objc/objc-api.h>
#  include <objc/encoding.h>
#endif

static EONull *null = nil;

#if !NeXT_Foundation_LIBRARY

static id idMethodGetFunc(void* info1, void* info2, id self);
static id idIvarGetFunc(void* info1, void* info2, id self);
static void idMethodSetFunc(void* info1, void* info2, id self, id val);
static void idIvarSetFunc(void* info1, void* info2, id self, id val);
static id charMethodGetFunc(void* info1, void* info2, id self);
static id charIvarGetFunc(void* info1, void* info2, id self);
static void charMethodSetFunc(void* info1, void* info2, id self, id val);
static void charIvarSetFunc(void* info1, void* info2, id self, id val);
static id unsignedCharMethodGetFunc(void* info1, void* info2, id self);
static id unsignedCharIvarGetFunc(void* info1, void* info2, id self);
static void unsignedCharMethodSetFunc(void* info1, void* info2, id self, id val);
static void unsignedCharIvarSetFunc(void* info1, void* info2, id self, id val);
static id shortMethodGetFunc(void* info1, void* info2, id self);
static id shortIvarGetFunc(void* info1, void* info2, id self);
static void shortMethodSetFunc(void* info1, void* info2, id self, id val);
static void shortIvarSetFunc(void* info1, void* info2, id self, id val);
static id unsignedShortMethodGetFunc(void* info1, void* info2, id self);
static id unsignedShortIvarGetFunc(void* info1, void* info2, id self);
static void unsignedShortMethodSetFunc(void* info1, void* info2, id self, id val);
static void unsignedShortIvarSetFunc(void* info1, void* info2, id self, id val);
static id intMethodGetFunc(void* info1, void* info2, id self);
static id intIvarGetFunc(void* info1, void* info2, id self);
static void intMethodSetFunc(void* info1, void* info2, id self, id val);
static void intIvarSetFunc(void* info1, void* info2, id self, id val);
static id unsignedIntMethodGetFunc(void* info1, void* info2, id self);
static id unsignedIntIvarGetFunc(void* info1, void* info2, id self);
static void unsignedIntMethodSetFunc(void* info1, void* info2, id self, id val);
static void unsignedIntIvarSetFunc(void* info1, void* info2, id self, id val);
static id longMethodGetFunc(void* info1, void* info2, id self);
static id longIvarGetFunc(void* info1, void* info2, id self);
static void longMethodSetFunc(void* info1, void* info2, id self, id val);
static void longIvarSetFunc(void* info1, void* info2, id self, id val);
static id unsignedLongMethodGetFunc(void* info1, void* info2, id self);
static id unsignedLongIvarGetFunc(void* info1, void* info2, id self);
static void unsignedLongMethodSetFunc(void* info1, void* info2, id self, id val);
static void unsignedLongIvarSetFunc(void* info1, void* info2, id self, id val);
static id longLongMethodGetFunc(void* info1, void* info2, id self);
static id longLongIvarGetFunc(void* info1, void* info2, id self);
static void longLongMethodSetFunc(void* info1, void* info2, id self, id val);
static void longLongIvarSetFunc(void* info1, void* info2, id self, id val);
static id unsignedLongLongMethodGetFunc(void* info1, void* info2, id self);
static id unsignedLongLongIvarGetFunc(void* info1, void* info2, id self);
static void unsignedLongLongMethodSetFunc(void* info1, void* info2, id self, id val);
static void unsignedLongLongIvarSetFunc(void* info1, void* info2, id self, id val);
static id floatMethodGetFunc(void* info1, void* info2, id self);
static id floatIvarGetFunc(void* info1, void* info2, id self);
static void floatMethodSetFunc(void* info1, void* info2, id self, id val);
static void floatIvarSetFunc(void* info1, void* info2, id self, id val);
static id doubleMethodGetFunc(void* info1, void* info2, id self);
static id doubleIvarGetFunc(void* info1, void* info2, id self);
static void doubleMethodSetFunc(void* info1, void* info2, id self, id val);
static void doubleIvarSetFunc(void* info1, void* info2, id self, id val);

static Class NumberClass = Nil;
static Class StringClass = Nil;

@implementation NSObject(EOKeyValueCoding)

/*
 *  Types
 */

typedef struct _KeyValueMethod {
  NSString*   key;
  Class       class;
} KeyValueMethod;

typedef struct _GetKeyValueBinding {
  /* info1, info2, self */
  id (*access)(void *, void *, id);
  void *info1;
  void *info2;
} GetKeyValueBinding;

typedef struct _SetKeyValueBinding {
  /* info1, info2, self, val */
  void (*access)(void *, void *, id, id);
  void *info1;
  void *info2;
} SetKeyValueBinding;

/*
 * Globals
 */

static NSMapTable* getValueBindings = NULL;
static NSMapTable* setValueBindings = NULL;
static BOOL keyValueDebug = NO;
static BOOL keyValueInit  = NO;

/*
 *  KeyValueMapping
 */

static GetKeyValueBinding* newGetBinding(NSString* key, id instance)
{
  GetKeyValueBinding *ret = NULL;
  void *info1 = NULL;
  void *info2 = NULL;
  id (*fptr)(void*, void*, id) = NULL;

  // Lookup method name [-(type)key]
  {
    Class      class = [instance class];
    unsigned   clen  = [key cStringLength];
    char       *cbuf;
    const char *ckey;
    SEL        sel;
    struct objc_method* mth;
    
    cbuf = malloc(clen + 1);
    [key getCString:cbuf]; cbuf[clen] = '\0';
    ckey = cbuf;
    sel = sel_get_any_uid(ckey);
    
    if (sel && (mth = class_get_instance_method(class, sel)) &&
        method_get_number_of_arguments(mth) == 2) {
      switch(*objc_skip_type_qualifiers(mth->method_types)) {
        case _C_ID:
          fptr = (id (*)(void*, void*, id))idMethodGetFunc;
          break;
        case _C_CHR:
          fptr = (id (*)(void*, void*, id))charMethodGetFunc;
          break;
        case _C_UCHR:
          fptr = (id (*)(void*, void*, id))unsignedCharMethodGetFunc;
          break;
        case _C_SHT:
          fptr = (id (*)(void*, void*, id))shortMethodGetFunc;
          break;
        case _C_USHT:
          fptr = (id (*)(void*, void*, id))unsignedShortMethodGetFunc;
          break;
        case _C_INT:
          fptr = (id (*)(void*, void*, id))intMethodGetFunc;
          break;
        case _C_UINT:
          fptr = (id (*)(void*, void*, id))unsignedIntMethodGetFunc;
          break;
        case _C_LNG:
          fptr = (id (*)(void*, void*, id))longMethodGetFunc;
          break;
        case _C_ULNG:
          fptr = (id (*)(void*, void*, id))unsignedLongMethodGetFunc;
          break;
        case 'q':
          fptr = (id (*)(void*, void*, id))longLongMethodGetFunc;
          break;
        case 'Q':
          fptr = (id (*)(void*, void*, id))unsignedLongLongMethodGetFunc;
          break;
        case _C_FLT:
          fptr = (id (*)(void*, void*, id))floatMethodGetFunc;
          break;
        case _C_DBL:
          fptr = (id (*)(void*, void*, id))doubleMethodGetFunc;
          break;
      }
      if (fptr) {
        info1 = (void*)(mth->method_imp);
        info2 = (void*)(mth->method_name);
      }
    }
    if (cbuf) free(cbuf);
  }
        
  // Lookup ivar name
  if (!fptr) {
    Class class = [instance class];
    unsigned   clen;
    char       *cbuf;
    const char *ckey;
    int i;
    
    clen = [key cStringLength];
    cbuf = malloc(clen + 1);
    [key getCString:cbuf]; cbuf[clen] = '\0';
    ckey = cbuf;
    
    while (class) {
      for (i = 0; class->ivars && i < class->ivars->ivar_count; i++) {
        if (!Strcmp(ckey, class->ivars->ivar_list[i].ivar_name)) {
          switch(*objc_skip_type_qualifiers(class->ivars->ivar_list[i].ivar_type)) {
            case _C_ID:
              fptr = (id (*)(void*, void*, id))idIvarGetFunc;
              break;
            case _C_CHR:
              fptr = (id (*)(void*, void*, id))charIvarGetFunc;
              break;
            case _C_UCHR:
              fptr = (id (*)(void*, void*, id))unsignedCharIvarGetFunc;
              break;
            case _C_SHT:
              fptr = (id (*)(void*, void*, id))shortIvarGetFunc;
              break;
            case _C_USHT:
              fptr = (id (*)(void*, void*, id))unsignedShortIvarGetFunc;
              break;
            case _C_INT:
              fptr = (id (*)(void*, void*, id))intIvarGetFunc;
              break;
            case _C_UINT:
              fptr = (id (*)(void*, void*, id))unsignedIntIvarGetFunc;
              break;
            case _C_LNG:
              fptr = (id (*)(void*, void*, id))longIvarGetFunc;
              break;
            case _C_ULNG:
              fptr = (id (*)(void*, void*, id))unsignedLongIvarGetFunc;
              break;
            case 'q':
              fptr = (id (*)(void*, void*, id))longLongIvarGetFunc;
              break;
            case 'Q':
              fptr = (id (*)(void*, void*, id))unsignedLongLongIvarGetFunc;
              break;
            case _C_FLT:
              fptr = (id (*)(void*, void*, id))floatIvarGetFunc;
              break;
            case _C_DBL:
              fptr = (id (*)(void*, void*, id))doubleIvarGetFunc;
              break;
          }
          if (fptr) {
            info2 = (void*)(class->ivars->ivar_list[i].ivar_offset);
            break;
          }
        }
      }
      class = class->super_class;
    }
    if (cbuf) free(cbuf);
  }
    
  // Make binding and insert into map
  if (fptr) {
    KeyValueMethod     *mkey;
    GetKeyValueBinding *bin;
        
    mkey = Malloc(sizeof(KeyValueMethod));
    bin  = Malloc(sizeof(GetKeyValueBinding));
    mkey->key   = [key copy];
    mkey->class = [instance class];
    
    bin->access = fptr;
    bin->info1 = info1;
    bin->info2 = info2;
        
    NSMapInsert(getValueBindings, mkey, bin);
    ret = bin;
  }
    
  // If no way to access value warn
  if (!ret && keyValueDebug)
    NSLog(@"cannnot get key `%@' for instance of class `%@'",
          key, NSStringFromClass([instance class]));
    
  return ret;
}

static SetKeyValueBinding* newSetBinding(NSString* key, id instance)
{
  SetKeyValueBinding *ret = NULL;
  void *info1 = NULL;
  void *info2 = NULL;
  void (*fptr)(void*, void*, id, id) = NULL;
    
  // Lookup method name [-(void)setKey:(type)arg]
  {
    Class      class = [instance class];
    unsigned   clen  = [key cStringLength];
    char       *cbuf;
    const char *ckey;
    SEL        sel;
    struct objc_method* mth;
    char  sname[clen + 7];

    cbuf = malloc(clen + 1);
    [key getCString:cbuf]; cbuf[clen] = '\0';
    ckey = cbuf;
    
    // Make sel from name
    Strcpy(sname, "set");
    Strcat(sname, ckey);
    Strcat(sname, ":");
    sname[3] = islower((int)sname[3]) ? toupper((int)sname[3]) : sname[3];
    sel = sel_get_any_uid(sname);
        
    if (sel && (mth = class_get_instance_method(class, sel)) &&
        method_get_number_of_arguments(mth) == 3 &&
        *objc_skip_type_qualifiers(mth->method_types) == _C_VOID) {
      char* argType = (char*)(mth->method_types);
                
      argType = (char*)objc_skip_argspec(argType);    // skip return
      argType = (char*)objc_skip_argspec(argType);    // skip self
      argType = (char*)objc_skip_argspec(argType);    // skip SEL
                
      switch(*objc_skip_type_qualifiers(argType)) {
        case _C_ID:
          fptr = (void (*)(void*, void*, id, id))idMethodSetFunc;
          break;
        case _C_CHR:
          fptr = (void (*)(void*, void*, id, id))charMethodSetFunc;
          break;
        case _C_UCHR:
          fptr = (void (*)(void*, void*, id, id))unsignedCharMethodSetFunc;
          break;
        case _C_SHT:
          fptr = (void (*)(void*, void*, id, id))shortMethodSetFunc;
          break;
        case _C_USHT:
          fptr = (void (*)(void*, void*, id, id))unsignedShortMethodSetFunc;
          break;
        case _C_INT:
          fptr = (void (*)(void*, void*, id, id))intMethodSetFunc;
          break;
        case _C_UINT:
          fptr = (void (*)(void*, void*, id, id))unsignedIntMethodSetFunc;
          break;
        case _C_LNG:
          fptr = (void (*)(void*, void*, id, id))longMethodSetFunc;
          break;
        case _C_ULNG:
          fptr = (void (*)(void*, void*, id, id))unsignedLongMethodSetFunc;
          break;
        case 'q':
          fptr = (void (*)(void*, void*, id, id))longLongMethodSetFunc;
          break;
        case 'Q':
          fptr = (void (*)(void*, void*, id, id))unsignedLongLongMethodSetFunc;
          break;
        case _C_FLT:
          fptr = (void (*)(void*, void*, id, id))floatMethodSetFunc;
          break;
        case _C_DBL:
          fptr = (void (*)(void*, void*, id, id))doubleMethodSetFunc;
          break;
      }
      if (fptr) {
        info1 = (void*)(mth->method_imp);
        info2 = (void*)(mth->method_name);
      }
    }
    if (cbuf) free(cbuf);
  }    
  // Lookup ivar name
  if (!fptr) {
    Class class = [instance class];
    unsigned   clen  = [key cStringLength];
    char       *cbuf;
    const char *ckey;
    int i;

    cbuf = malloc(clen + 1);
    [key getCString:cbuf]; cbuf[clen] = '\0';
    ckey = cbuf;
        
    while (class) {
      for (i = 0; class->ivars && i < class->ivars->ivar_count; i++) {
        if (!Strcmp(ckey, class->ivars->ivar_list[i].ivar_name)) {
          switch(*objc_skip_type_qualifiers(class->ivars->ivar_list[i].ivar_type)) {
            case _C_ID:
              fptr = (void (*)(void*, void*, id, id))idIvarSetFunc;
              break;
            case _C_CHR:
              fptr = (void (*)(void*, void*, id, id))charIvarSetFunc;
              break;
            case _C_UCHR:
              fptr = (void (*)(void*, void*, id, id))unsignedCharIvarSetFunc;
              break;
            case _C_SHT:
              fptr = (void (*)(void*, void*, id, id))shortIvarSetFunc;
              break;
            case _C_USHT:
              fptr = (void (*)(void*, void*, id, id))unsignedShortIvarSetFunc;
              break;
            case _C_INT:
              fptr = (void (*)(void*, void*, id, id))intIvarSetFunc;
              break;
            case _C_UINT:
              fptr = (void (*)(void*, void*, id, id))unsignedIntIvarSetFunc;
              break;
            case _C_LNG:
              fptr = (void (*)(void*, void*, id, id))longIvarSetFunc;
              break;
            case _C_ULNG:
              fptr = (void (*)(void*, void*, id, id))unsignedLongIvarSetFunc;
              break;
            case 'q':
              fptr = (void (*)(void*, void*, id, id))longLongIvarSetFunc;
              break;
            case 'Q':
              fptr = (void (*)(void*, void*, id, id))unsignedLongLongIvarSetFunc;
              break;
            case _C_FLT:
              fptr = (void (*)(void*, void*, id, id))floatIvarSetFunc;
              break;
            case _C_DBL:
              fptr = (void (*)(void*, void*, id, id))doubleIvarSetFunc;
              break;
          }
          if (fptr) {
            info2 = (void*)(class->ivars->ivar_list[i].ivar_offset);
            break;
          }
        }
      }
      class = class->super_class;
    }
    if (cbuf) free(cbuf);
  }
    
  // Make binding and insert into map
  if (fptr) {
    KeyValueMethod     *mkey;
    SetKeyValueBinding *bin;
        
    mkey = Malloc(sizeof(KeyValueMethod));
    bin  = Malloc(sizeof(SetKeyValueBinding));
    mkey->key = [key copy];
    mkey->class = [instance class];
    
    bin->access = fptr;
    bin->info1 = info1;
    bin->info2 = info2;
        
    NSMapInsert(setValueBindings, mkey, bin);
    ret = bin;
  }
  // If no way to access value warn
  if (!ret && keyValueDebug)
    NSLog(@"cannnot set key `%@' for instance of class `%@'",
          key, NSStringFromClass([instance class]));
  
  return ret;
}

/*
 * MapTable initialization
 */

static unsigned keyValueMapHash(NSMapTable* table, KeyValueMethod* map) {
  return [map->key hash] + (((int)(map->class)) >> 4);
}

static BOOL keyValueMapCompare(NSMapTable* table,
                               KeyValueMethod* map1, KeyValueMethod* map2)
{
  return (map1->class == map2->class) && [map1->key isEqual:map2->key];
}

static void mapRetainNothing(NSMapTable* table, KeyValueMethod* map) {
}

static void keyValueMapKeyRelease(NSMapTable* table, KeyValueMethod* map) {
  [map->key release];
  Free(map);
}

static void keyValueMapValRelease(NSMapTable* table, void* map) {
  Free(map);
}

static NSString* keyValueMapDescribe(NSMapTable* table, KeyValueMethod* map) {
  if (StringClass == Nil) StringClass = [NSString class];
  return [StringClass stringWithFormat:@"%@:%@",
                   NSStringFromClass(map->class), map->key];
}

static NSString* describeBinding(NSMapTable* table, GetKeyValueBinding* bin) {
  if (StringClass == Nil) StringClass = [NSString class];
  return [StringClass stringWithFormat:@"%08x:%08x", bin->info1, bin->info2];
}

static NSMapTableKeyCallBacks keyValueKeyCallbacks = {
  (unsigned(*)(NSMapTable *, const void *))keyValueMapHash,
  (BOOL(*)(NSMapTable *, const void *, const void *))keyValueMapCompare,
  (void (*)(NSMapTable *, const void *anObject))mapRetainNothing,
  (void (*)(NSMapTable *, void *anObject))keyValueMapKeyRelease,
  (NSString *(*)(NSMapTable *, const void *))keyValueMapDescribe,
  (const void *)NULL
}; 

const NSMapTableValueCallBacks keyValueValueCallbacks = {
  (void (*)(NSMapTable *, const void *))mapRetainNothing,
  (void (*)(NSMapTable *, void *))keyValueMapValRelease,
  (NSString *(*)(NSMapTable *, const void *))describeBinding
}; 

static void initKeyValueBindings(void)
{
  getValueBindings = NSCreateMapTable(keyValueKeyCallbacks, 
                                      keyValueValueCallbacks, 31);
  setValueBindings = NSCreateMapTable(keyValueKeyCallbacks, 
                                      keyValueValueCallbacks, 31);
  keyValueInit = YES;
}

/* 
 * Access Methods 
 */

static inline void removeAllBindings(void) {
  NSResetMapTable(getValueBindings);
  NSResetMapTable(setValueBindings);
}

static inline id getValue(NSString* key, id instance) {
  KeyValueMethod     mkey = { key, [instance class] };
  GetKeyValueBinding *bin;
  id value = nil;
  
  if (NumberClass == Nil)
    NumberClass = [NSNumber class];
  
  // Check Init
  if (!keyValueInit)
    initKeyValueBindings();
    
  // Get existing binding
  bin = (GetKeyValueBinding *)NSMapGet(getValueBindings, &mkey);
    
  // Create new binding
  if (bin == NULL)
    bin = newGetBinding(key, instance);
    
  // Get value if binding is ok
  if (bin)
    value = bin->access(bin->info1, bin->info2, instance);
    
  return value;
}

static inline BOOL setValue(NSString* key, id instance, id value)
{
  KeyValueMethod mkey = {key, [instance class]};
  SetKeyValueBinding* bin;

  if (NumberClass == Nil)
    NumberClass = [NSNumber class];
    
  // Check Init
  if (!keyValueInit)
    initKeyValueBindings();
    
  // Get existing binding
  bin = (SetKeyValueBinding *)NSMapGet(setValueBindings, &mkey);
    
  // Create new binding
  if (bin == NULL)
    bin = newSetBinding(key, instance);
    
  // Get value if binding is ok
  if (bin)
    bin->access(bin->info1, bin->info2, instance, value);
    
  return (bin != NULL);
}

+ (BOOL)accessInstanceVariablesDirectly {
  return NO;
}

- (void)handleTakeValue:(id)_value forUnboundKey:(NSString *)_key {
  NSDictionary *ui;

  ui = [NSDictionary dictionaryWithObjectsAndKeys:
		       self, @"EOTargetObjectUserInfoKey",
		       [self class],
		       @"EOTargetObjectClassUserInfoKey",
		       _key, @"EOUnknownUserInfoKey",
		     nil];
  [[NSException exceptionWithName:@"EOUnknownKeyException"
		reason:@"called -takeValue:forKey: with unknown key"
		userInfo:ui] raise];
}

- (id)handleQueryWithUnboundKey:(NSString *)_key {
  NSDictionary *ui;

  ui = [NSDictionary dictionaryWithObjectsAndKeys:
		       self, @"EOTargetObjectUserInfoKey",
		       _key, @"EOUnknownUserInfoKey",
		     nil];
  [[NSException exceptionWithName:@"EOUnknownKeyException"
		reason:@"called -valueForKey: with unknown key"
		userInfo:ui] raise];
  return nil;
}

- (void)unableToSetNullForKey:(NSString *)_key {
  [NSException raise:@"NSInvalidArgumentException" 
	       format:
		  @"EOKeyValueCoding cannot set EONull value for key %@,"
		  @"in instance of %@ class.",
		  _key, NSStringFromClass([self class])];
}

+ (void)flushAllKeyBindings {
  removeAllBindings();
}
- (void)flushKeyBindings {
  // EOF 1.1 method
  removeAllBindings();
}

- (void)setKeyValueCodingWarnings:(BOOL)aFlag {
  keyValueDebug = aFlag;
}

- (void)takeValuesFromDictionary:(NSDictionary *)dictionary {
  NSEnumerator *keyEnum;
  id key;

  if (null == nil) null = [EONull null];
  keyEnum = [dictionary keyEnumerator];
    
  while ((key = [keyEnum nextObject])) {
    id value;
    
    value = [dictionary objectForKey:key];

    /* automagically convert EONull to nil */
    if (value == null) value = nil;
    
    [self takeValue:value forKey:key];
    
#if 0 // this doesn't support overridden methods ...
    if (!setValue(key, self, value)) {
      [self handleTakeValue:value forUnboundKey:key];
    }
#endif
  }
}

- (NSDictionary *)valuesForKeys:(NSArray *)keys {
  static Class  NSDictionaryClass = Nil;
  int n = [keys count];

  if (NSDictionaryClass == Nil)
    NSDictionaryClass = [NSDictionary class];
  if (null == nil)
    null = [EONull null];
  
  if (n == 0)
    return [NSDictionaryClass dictionary];
  else if (n == 1) {
    NSString *key;
    id value;
    
    key   = [keys objectAtIndex:0];
    //value = getValue(key, self);
    value = [self valueForKey:key];

    /* automagically convert 'nil' to EONull */
    if (value == nil) value = null;
    
    return [NSDictionaryClass dictionaryWithObject:value forKey:key];
  }
  else {
    id newKeys[n];
    id newVals[n];
    int i;
        
    for (i = 0; i < n; i++) {
      id key;
      id val;
      
      key = [keys objectAtIndex:i];
      //val = getValue(key, self);
      val = [self valueForKey:key];

      /* automagically convert 'nil' to EONull */
      if (val == nil) val = null;
      
      newKeys[i] = key;
      newVals[i] = val;
    }
    
    return [NSDictionaryClass dictionaryWithObjects:newVals
                              forKeys:newKeys
                              count:n];
  }
}

- (void)takeValue:(id)_value forKey:(NSString *)_key {
  if (!setValue(_key, self, _value)) {
    //NSLog(@"ERROR(%s): couldn't take value for key %@", key);
    [self handleTakeValue:_value forUnboundKey:_key];
  }
}

- (id)valueForKey:(NSString *)key {
  id val;
  
  if ((val = getValue(key, self)))
    return val;
  
  return nil;
}

/* stored values */

+ (BOOL)useStoredAccessor {
  return YES;
}

- (void)takeStoredValue:(id)_value forKey:(NSString *)_key {
  if ([[self class] useStoredAccessor]) {
    BOOL ok = YES;

    /* this should be different */
    
    ok = setValue(_key, self, _value);
    if (!ok) [self handleTakeValue:_value forUnboundKey:_key];
  }
  else
    [self takeValue:_value forKey:_key];
}

- (id)storedValueForKey:(NSString *)_key {
  if ([[self class] useStoredAccessor]) {
    id val;

    /* this should be different */
    
    if ((val = getValue(_key, self)))
      return val;

    /* val = [self handleQueryWithUnboundKey:_key] */
    
    return nil;
  }
  else
    return [self valueForKey:_key];
}

@end /* NSObject(EOKeyValueCoding) */

@implementation NSObject(EOKeyPathValueCoding)

- (void)takeValue:(id)_value forKeyPath:(NSString *)_keyPath {
  NSArray *keyPath;
  unsigned i, count;
  id target;

  keyPath = [_keyPath componentsSeparatedByString:@"."];
  count = [keyPath count];

  if (count < 2)
    [self takeValue:_value forKey:_keyPath];
  else {

    target = self;
    for (i = 0; i < (count - 1) ; i++) {
      if ((target = [target valueForKey:[keyPath objectAtIndex:i]]) == nil)
        /* nil component */
        return;
    }
  
    [target takeValue:_value forKey:[keyPath lastObject]];
  }
}
- (id)valueForKeyPath:(NSString *)_keyPath {
#if 1
  const unsigned char *buf;
  unsigned int  i, start, len;
  id value;

  if ((len = [_keyPath cStringLength]) == 0)
    return [self valueForKey:_keyPath];
  
  if (StringClass == Nil) StringClass = [NSString class];
  value = self;
  
  buf = [_keyPath cString];
  if (index(buf, '.') == NULL)
    /* no point contained .. */
    return [self valueForKey:_keyPath];
  
  for (i = start = 0; i < len; i++) {
    if (buf[i] == '.') {
      /* found a pt */
      NSString *key;
      
      key = (start < i)
        ? [StringClass stringWithCString:&(buf[start]) length:(i - start)]
        : @"";
      
      if ((value = [value valueForKey:key]) == nil)
        return nil;
      
      start = (i + 1); /* next part is after the pt */
    }
  }
  /* check last part */
  {
    NSString *key;
    
    key = (start < i)
      ? [StringClass stringWithCString:&(buf[start]) length:(i - start)]
      : @"";
    return [value valueForKey:key];
  }
#else
  /* naive implementation */
  NSEnumerator *keyPath;
  NSString *key;
  id value;
  
  value   = self;
  keyPath = [[_keyPath componentsSeparatedByString:@"."] objectEnumerator];
  while ((key = [keyPath nextObject]) && (value != nil))
    value = [value valueForKey:key];
  return value;
#endif
}

@end /* NSObject(EOKeyPathValueCoding) */

@implementation NSArray(EOKeyValueCoding)

- (id)computeSumForKey:(NSString *)_key {
  unsigned i, cc = [self count];
  id (*objAtIdx)(id, SEL, unsigned int);
  double sum;

  if (cc == 0) return [NSNumber numberWithDouble:0.0];
  
  objAtIdx = (void*)[self methodForSelector:@selector(objectAtIndex:)];
    
  for (i = 0, sum = 0.0; i < cc; i++) {
    register id o;
      
    o = objAtIdx(self, @selector(objectAtIndex:), i);
    sum += [o doubleValue];
  }
  return [NSNumber numberWithDouble:sum];
}

- (id)computeAvgForKey:(NSString *)_key {
  unsigned i, cc = [self count];
  id (*objAtIdx)(id, SEL, unsigned int);
  double sum;

  if (cc == 0) return [NSNumber numberWithDouble:0.0];
  
  objAtIdx = (void *)[self methodForSelector:@selector(objectAtIndex:)];
    
  for (i = 0, sum = 0.0; i < cc; i++) {
    register id o;
      
    o = objAtIdx(self, @selector(objectAtIndex:), i);

    sum += [o doubleValue];
  }
  return [NSNumber numberWithDouble:(sum / (double)cc)];
}

- (id)computeCountForKey:(NSString *)_key {
  return [NSNumber numberWithUnsignedInt:[self count]];
}

- (id)computeMaxForKey:(NSString *)_key {
  unsigned i, cc = [self count];
  id (*objAtIdx)(id, SEL, unsigned int);
  double max;

  if (cc == 0) return nil;
    
  objAtIdx = (void *)[self methodForSelector:@selector(objectAtIndex:)];

  max = [objAtIdx(self, @selector(objectAtIndex:), 0) doubleValue];
  for (i = 1; i < cc; i++) {
    register double ov;
      
    ov = [objAtIdx(self, @selector(objectAtIndex:), i) doubleValue];
    if (ov > max) max = ov;
  }
  return [NSNumber numberWithDouble:max];
}

- (id)computeMinForKey:(NSString *)_key {
  unsigned i, cc = [self count];
  id (*objAtIdx)(id, SEL, unsigned int);
  double min;

  if (cc == 0) return nil;
  
  objAtIdx = (void *)[self methodForSelector:@selector(objectAtIndex:)];
    
  min = [objAtIdx(self, @selector(objectAtIndex:), 0) doubleValue];
  for (i = 1; i < cc; i++) {
    register double ov;
      
    ov = [objAtIdx(self, @selector(objectAtIndex:), i) doubleValue];
    if (ov < min) min = ov;
  }
  return [NSNumber numberWithDouble:min];
}

- (id)valueForKey:(NSString *)_key {
  if ([_key hasPrefix:@"@"]) {
    /* process a computed function */
    const char *keyStr;
    char       *bufPtr;
    unsigned   keyLen = [_key cStringLength];
    char       *kbuf, *buf;
    SEL        sel;

    kbuf = malloc(keyLen + 4);
    buf  = malloc(keyLen + 20);
    [_key getCString:kbuf];
    keyStr = kbuf;
    bufPtr = buf;
    strcpy(buf, "compute");       bufPtr += 7;
    *bufPtr = toupper(keyStr[1]); bufPtr++;
    strncpy(&(buf[8]), &(keyStr[2]), keyLen - 2); bufPtr += (keyLen - 2);
    strcpy(bufPtr, "ForKey:");
    if (kbuf) free(kbuf);
    
    sel = sel_get_any_uid(buf);
    if (buf) free(buf);
    
    return sel != NULL ? [self performSelector:sel withObject:_key] : nil;
  }
  else {
    /* process the _key in a map function */
    unsigned i, cc = [self count];
    id objects[cc];
    id (*objAtIdx)(id, SEL, unsigned int);

#if DEBUG
    if ([_key isEqualToString:@"count"]) {
      NSLog(@"WARNING(%s): USED -valueForKey(@\"count\") ON NSArray, YOU"
            @"PROBABLY WANT TO USE @count INSTEAD !",
            __PRETTY_FUNCTION__);
      return [self valueForKey:@"@count"];
    }
#endif
    
    objAtIdx = (void *)[self methodForSelector:@selector(objectAtIndex:)];
    
    for (i = 0; i < cc; i++) {
      register id o;
      
      o = [objAtIdx(self, @selector(objectAtIndex:), i) valueForKey:_key];
      
      if (o)
        objects[i] = o;
      else {
        if (null == nil) null = [EONull null];
        objects[i] = null;
      }
    }
    
    return [NSArray arrayWithObjects:objects count:cc];
  }
}

@end /* NSArray(EOKeyValueCoding) */

@implementation NSDictionary(EOKeyValueCoding)

- (NSDictionary *)valuesForKeys:(NSArray *)keys {
  int n = [keys count];

  if (n == 0)
    return [NSDictionary dictionary];
  else if (n == 1) {
    NSString *key = [keys objectAtIndex:0];

    return [NSDictionary dictionaryWithObject:[self objectForKey:key]
                         forKey:key];
  }
  else {
    NSMutableArray *newKeys, *newVals;
    int i;
        
    newKeys = [NSMutableArray arrayWithCapacity:n];
    newVals = [NSMutableArray arrayWithCapacity:n];
    
    for (i = 0; i < n; i++) {
      id key = [keys objectAtIndex:i];
      id val = [self objectForKey:key];
      
      if (val) {
        [newKeys addObject:key];
        [newVals addObject:val];
      }
    }
    
    return [NSDictionary dictionaryWithObjects:newVals forKeys:newKeys];
  }
}

- (void)takeValue:(id)_value forKey:(NSString *)_key {
  //#warning takeValue:forKey: is ignored in NSDictionaries !
  // ignore
  //[self handleTakeValue:_value forUnboundKey:_key];
}

- (id)valueForKey:(id)_key {
  id obj;
  
  if (_key == nil) // TODO: warn about nil key?
    return nil;
  if ((obj = [self objectForKey:_key]) == nil)
    return nil;
  
  if (null == nil) null = [[NSNull null] retain];
  if (obj == null)
    return nil;
    
  return obj;
}

@end /* NSDictionary(EOKeyValueCoding) */

@implementation NSMutableDictionary(EOKeyValueCoding)

- (void)takeValuesFromDictionary:(NSDictionary*)dictionary {
  [self addEntriesFromDictionary:dictionary];
}

- (void)takeValue:(id)_value forKey:(id)_key {
  if (_value == nil) _value = [NSNull null];
  [self setObject:_value forKey:_key];
}

@end /* NSMutableDictionary(EOKeyValueCoding) */

/*
 *  Accessor functions
 */

/* ACCESS to keys of id type. */

static id idMethodGetFunc(void* info1, void* info2, id self) {
  id (*fptr)(id, SEL) = (id(*)(id, SEL))info1;
  id val = fptr(self, (SEL)info2);
  return val;
}
static id idIvarGetFunc(void* info1, void* info2, id self) {
  id* ptr = (id*)((char*)self + (int)info2);
  return *ptr;
}

static void idMethodSetFunc(void* info1, void* info2, id self, id val) {
  void (*fptr)(id, SEL, id) = (void(*)(id, SEL, id))info1;
  fptr(self, (SEL)info2, val);
}

static void idIvarSetFunc(void* info1, void* info2, id self, id val)
{
  id* ptr = (id*)((char*)self + (int)info2);
  ASSIGN(*ptr, val);
}

/* ACCESS to keys of char type. */

static id charMethodGetFunc(void* info1, void* info2, id self)
{
  char (*fptr)(id, SEL) = (char(*)(id, SEL))info1;
  char val = fptr(self, (SEL)info2);
  return [NumberClass numberWithChar:val];
}

static id charIvarGetFunc(void* info1, void* info2, id self)
{
  char* ptr = (char*)((char*)self + (int)info2);
  return [NumberClass numberWithChar:*ptr];
}

static void charMethodSetFunc(void* info1, void* info2, id self, id val)
{
  void (*fptr)(id, SEL, char) = (void(*)(id, SEL, char))info1;
  fptr(self, (SEL)info2, [val charValue]);
}

static void charIvarSetFunc(void* info1, void* info2, id self, id val)
{
  char* ptr = (char*)((char*)self + (int)info2);
  *ptr = [val charValue];
}


/* ACCESS to keys of unsigned char type. */

static id unsignedCharMethodGetFunc(void* info1, void* info2, id self)
{
  unsigned char (*fptr)(id, SEL) = (unsigned char(*)(id, SEL))info1;
  unsigned char val = fptr(self, (SEL)info2);
  return [NumberClass numberWithUnsignedChar:val];
}

static id unsignedCharIvarGetFunc(void* info1, void* info2, id self)
{
  unsigned char* ptr = (unsigned char*)((char*)self + (int)info2);
  return [NumberClass numberWithUnsignedChar:*ptr];
}

static void unsignedCharMethodSetFunc(void* info1, void* info2, id self, id val)
{
  void (*fptr)(id, SEL, unsigned char) = (void(*)(id, SEL, unsigned char))info1;
  fptr(self, (SEL)info2, [val unsignedCharValue]);
}

static void unsignedCharIvarSetFunc(void* info1, void* info2, id self, id val)
{
  unsigned char* ptr = (unsigned char*)((char*)self + (int)info2);
  *ptr = [val unsignedCharValue];
}


/* ACCESS to keys of short type. */

static id shortMethodGetFunc(void* info1, void* info2, id self)
{
  short (*fptr)(id, SEL) = (short(*)(id, SEL))info1;
  short val = fptr(self, (SEL)info2);
  return [NumberClass numberWithShort:val];
}

static id shortIvarGetFunc(void* info1, void* info2, id self)
{
  short* ptr = (short*)((char*)self + (int)info2);
  return [NumberClass numberWithShort:*ptr];
}

static void shortMethodSetFunc(void* info1, void* info2, id self, id val)
{
  void (*fptr)(id, SEL, short) = (void(*)(id, SEL, short))info1;
  fptr(self, (SEL)info2, [val shortValue]);
}

static void shortIvarSetFunc(void* info1, void* info2, id self, id val)
{
  short* ptr = (short*)((char*)self + (int)info2);
  *ptr = [val shortValue];
}


/* ACCESS to keys of unsigned short type. */

static id unsignedShortMethodGetFunc(void* info1, void* info2, id self)
{
  unsigned short (*fptr)(id, SEL) = (unsigned short(*)(id, SEL))info1;
  unsigned short val = fptr(self, (SEL)info2);
  return [NumberClass numberWithUnsignedShort:val];
}

static id unsignedShortIvarGetFunc(void* info1, void* info2, id self)
{
  unsigned short* ptr = (unsigned short*)((char*)self + (int)info2);
  return [NumberClass numberWithUnsignedShort:*ptr];
}

static void unsignedShortMethodSetFunc(void* info1, void* info2, id self, id val)
{
  void (*fptr)(id, SEL, unsigned short) = (void(*)(id, SEL, unsigned short))info1;
  fptr(self, (SEL)info2, [val unsignedShortValue]);
}

static void unsignedShortIvarSetFunc(void* info1, void* info2, id self, id val)
{
  unsigned short* ptr = (unsigned short*)((char*)self + (int)info2);
  *ptr = [val unsignedShortValue];
}


/* ACCESS to keys of int type. */

static id intMethodGetFunc(void* info1, void* info2, id self)
{
  int (*fptr)(id, SEL) = (int(*)(id, SEL))info1;
  int val = fptr(self, (SEL)info2);
  return [NumberClass numberWithInt:val];
}

static id intIvarGetFunc(void* info1, void* info2, id self)
{
  int* ptr = (int*)((char*)self + (int)info2);
  return [NumberClass numberWithInt:*ptr];
}

static void intMethodSetFunc(void* info1, void* info2, id self, id val)
{
  void (*fptr)(id, SEL, int) = (void(*)(id, SEL, int))info1;
  fptr(self, (SEL)info2, [val intValue]);
}

static void intIvarSetFunc(void* info1, void* info2, id self, id val)
{
  int* ptr = (int*)((char*)self + (int)info2);
  *ptr = [val intValue];
}


/* ACCESS to keys of unsigned int type. */

static id unsignedIntMethodGetFunc(void* info1, void* info2, id self)
{
  unsigned int (*fptr)(id, SEL) = (unsigned int(*)(id, SEL))info1;
  unsigned int val = fptr(self, (SEL)info2);
  return [NumberClass numberWithUnsignedInt:val];
}

static id unsignedIntIvarGetFunc(void* info1, void* info2, id self)
{
  unsigned int* ptr = (unsigned int*)((char*)self + (int)info2);
  return [NumberClass numberWithUnsignedInt:*ptr];
}

static void unsignedIntMethodSetFunc(void* info1, void* info2, id self, id val)
{
  void (*fptr)(id, SEL, unsigned int) = (void(*)(id, SEL, unsigned int))info1;
  fptr(self, (SEL)info2, [val unsignedIntValue]);
}

static void unsignedIntIvarSetFunc(void* info1, void* info2, id self, id val)
{
  unsigned int* ptr = (unsigned int*)((char*)self + (int)info2);
  *ptr = [val unsignedIntValue];
}


/* ACCESS to keys of long type. */

static id longMethodGetFunc(void* info1, void* info2, id self)
{
  long (*fptr)(id, SEL) = (long(*)(id, SEL))info1;
  long val = fptr(self, (SEL)info2);
  return [NumberClass numberWithLong:val];
}

static id longIvarGetFunc(void* info1, void* info2, id self)
{
  long* ptr = (long*)((char*)self + (int)info2);
  return [NumberClass numberWithLong:*ptr];
}

static void longMethodSetFunc(void* info1, void* info2, id self, id val)
{
  void (*fptr)(id, SEL, long) = (void(*)(id, SEL, long))info1;
  fptr(self, (SEL)info2, [val longValue]);
}

static void longIvarSetFunc(void* info1, void* info2, id self, id val)
{
  long* ptr = (long*)((char*)self + (int)info2);
  *ptr = [val longValue];
}


/* unsigned long type */

static id unsignedLongMethodGetFunc(void* info1, void* info2, id self) {
  unsigned long (*fptr)(id, SEL) = (unsigned long(*)(id, SEL))info1;
  unsigned long val = fptr(self, (SEL)info2);
  return [NumberClass numberWithUnsignedLong:val];
}

static id unsignedLongIvarGetFunc(void* info1, void* info2, id self) {
  unsigned long* ptr = (unsigned long*)((char*)self + (int)info2);
  return [NumberClass numberWithUnsignedLong:*ptr];
}

static void unsignedLongMethodSetFunc(void* info1, void* info2, id self, id val) {
  void (*fptr)(id, SEL, unsigned long) = (void(*)(id, SEL, unsigned long))info1;
  fptr(self, (SEL)info2, [val unsignedLongValue]);
}

static void unsignedLongIvarSetFunc(void* info1, void* info2, id self, id val) {
  unsigned long* ptr = (unsigned long*)((char*)self + (int)info2);
  *ptr = [val unsignedLongValue];
}


/* long long type */

static id longLongMethodGetFunc(void* info1, void* info2, id self) {
  long long (*fptr)(id, SEL) = (long long(*)(id, SEL))info1;
  long long val = fptr(self, (SEL)info2);
  return [NumberClass numberWithLongLong:val];
}

static id longLongIvarGetFunc(void* info1, void* info2, id self) {
  long long* ptr = (long long*)((char*)self + (int)info2);
  return [NumberClass numberWithLongLong:*ptr];
}

static void longLongMethodSetFunc(void* info1, void* info2, id self, id val) {
  void (*fptr)(id, SEL, long long) = (void(*)(id, SEL, long long))info1;
  fptr(self, (SEL)info2, [val longLongValue]);
}

static void longLongIvarSetFunc(void* info1, void* info2, id self, id val) {
  long long* ptr = (long long*)((char*)self + (int)info2);
  *ptr = [val longLongValue];
}


/* unsigned long long type */

static id unsignedLongLongMethodGetFunc(void* info1, void* info2, id self) {
  unsigned long long (*fptr)(id, SEL) = (unsigned long long(*)(id, SEL))info1;
  unsigned long long val = fptr(self, (SEL)info2);
  return [NumberClass numberWithUnsignedLongLong:val];
}

static id unsignedLongLongIvarGetFunc(void* info1, void* info2, id self) {
  unsigned long long* ptr = (unsigned long long*)((char*)self + (int)info2);
  return [NumberClass numberWithUnsignedLongLong:*ptr];
}

static void unsignedLongLongMethodSetFunc(void* info1, void* info2, id self, id val) {
  void (*fptr)(id, SEL, unsigned long long) = (void(*)(id, SEL, unsigned long long))info1;
  fptr(self, (SEL)info2, [val unsignedLongLongValue]);
}

static void unsignedLongLongIvarSetFunc(void* info1, void* info2, id self, id val) {
  unsigned long long* ptr = (unsigned long long*)((char*)self + (int)info2);
  *ptr = [val unsignedLongLongValue];
}


/* float */

static id floatMethodGetFunc(void* info1, void* info2, id self) {
  float (*fptr)(id, SEL) = (float(*)(id, SEL))info1;
  float val = fptr(self, (SEL)info2);
  return [NumberClass numberWithFloat:val];
}

static id floatIvarGetFunc(void* info1, void* info2, id self) {
  float* ptr = (float*)((char*)self + (int)info2);
  return [NumberClass numberWithFloat:*ptr];
}

static void floatMethodSetFunc(void* info1, void* info2, id self, id val) {
  void (*fptr)(id, SEL, float) = (void(*)(id, SEL, float))info1;
  fptr(self, (SEL)info2, [val floatValue]);
}

static void floatIvarSetFunc(void* info1, void* info2, id self, id val) {
  float* ptr = (float*)((char*)self + (int)info2);
  *ptr = [val floatValue];
}


/* double */

static id doubleMethodGetFunc(void* info1, void* info2, id self) {
  double (*fptr)(id, SEL) = (double(*)(id, SEL))info1;
  double val = fptr(self, (SEL)info2);
  return [NumberClass numberWithDouble:val];
}

static id doubleIvarGetFunc(void* info1, void* info2, id self) {
  double* ptr = (double*)((char*)self + (int)info2);
  return [NumberClass numberWithDouble:*ptr];
}

static void doubleMethodSetFunc(void* info1, void* info2, id self, id val) {
  void (*fptr)(id, SEL, double) = (void(*)(id, SEL, double))info1;
  fptr(self, (SEL)info2, [val doubleValue]);
}

static void doubleIvarSetFunc(void* info1, void* info2, id self, id val) {
  double* ptr = (double*)((char*)self + (int)info2);
  *ptr = [val doubleValue];
}

#else /* NeXT_Foundation_LIBRARY */

@implementation NSArray(EOKeyValueCoding)

- (id)computeSumForKey:(NSString *)_key {
  unsigned i, cc = [self count];
  id (*objAtIdx)(id, SEL, unsigned int);
  double sum;

  if (cc == 0) return [NSNumber numberWithDouble:0.0];
  
  objAtIdx = (void*)[self methodForSelector:@selector(objectAtIndex:)];
    
  for (i = 0, sum = 0.0; i < cc; i++) {
    register id o;
      
    o = objAtIdx(self, @selector(objectAtIndex:), i);
    sum += [o doubleValue];
  }
  return [NSNumber numberWithDouble:sum];
}

- (id)computeAvgForKey:(NSString *)_key {
  unsigned i, cc = [self count];
  id (*objAtIdx)(id, SEL, unsigned int);
  double sum;

  if (cc == 0) return [NSNumber numberWithDouble:0.0];
  
  objAtIdx = (void *)[self methodForSelector:@selector(objectAtIndex:)];
    
  for (i = 0, sum = 0.0; i < cc; i++) {
    register id o;
      
    o = objAtIdx(self, @selector(objectAtIndex:), i);

    sum += [o doubleValue];
  }
  return [NSNumber numberWithDouble:(sum / (double)cc)];
}

- (id)computeCountForKey:(NSString *)_key {
  return [NSNumber numberWithUnsignedInt:[self count]];
}

- (id)computeMaxForKey:(NSString *)_key {
  unsigned i, cc = [self count];
  id (*objAtIdx)(id, SEL, unsigned int);
  double max;

  if (cc == 0) return nil;
    
  objAtIdx = (void *)[self methodForSelector:@selector(objectAtIndex:)];

  max = [objAtIdx(self, @selector(objectAtIndex:), 0) doubleValue];
  for (i = 1; i < cc; i++) {
    register double ov;
      
    ov = [objAtIdx(self, @selector(objectAtIndex:), i) doubleValue];
    if (ov > max) max = ov;
  }
  return [NSNumber numberWithDouble:max];
}

- (id)computeMinForKey:(NSString *)_key {
  unsigned i, cc = [self count];
  id (*objAtIdx)(id, SEL, unsigned int);
  double min;

  if (cc == 0) return nil;
  
  objAtIdx = (void *)[self methodForSelector:@selector(objectAtIndex:)];
    
  min = [objAtIdx(self, @selector(objectAtIndex:), 0) doubleValue];
  for (i = 1; i < cc; i++) {
    register double ov;
      
    ov = [objAtIdx(self, @selector(objectAtIndex:), i) doubleValue];
    if (ov < min) min = ov;
  }
  return [NSNumber numberWithDouble:min];
}

- (id)valueForKey:(NSString *)_key {
  if (null == nil) null = [[EONull null] retain];
  
  if ([_key hasPrefix:@"@"]) {
    /* process a computed function */
    const char *keyStr;
    char       *bufPtr;
    unsigned   keyLen = [_key cStringLength];
    char       *kbuf, *buf;
    SEL        sel;

    kbuf = malloc(keyLen + 1);
    buf  = malloc(keyLen + 16);
    [_key getCString:kbuf];
    keyStr = kbuf;
    bufPtr = buf;
    strcpy(buf, "compute");       bufPtr += 7;
    *bufPtr = toupper(keyStr[1]); bufPtr++;
    strncpy(&(buf[8]), &(keyStr[2]), keyLen - 2); bufPtr += (keyLen - 2);
    strcpy(bufPtr, "ForKey:");
    if (kbuf) free(kbuf);

#if NeXT_RUNTIME
    sel = sel_getUid(buf);
#else    
    sel = sel_get_any_uid(buf);
#endif
    if (buf) free(buf);
    
    return sel != NULL ? [self performSelector:sel withObject:_key] : nil;
  }
  else {
    /* process the _key in a map function */
    unsigned i, cc = [self count];
    NSArray  *result;
    id *objects;
    id (*objAtIdx)(id, SEL, unsigned int);

#if DEBUG
    if ([_key isEqualToString:@"count"]) {
      NSLog(@"WARNING(%s): USED -valueForKey(@\"count\") ON NSArray, YOU"
            @"PROBABLY WANT TO USE @count INSTEAD !",
            __PRETTY_FUNCTION__);
      return [self valueForKey:@"@count"];
    }
#endif
    
    if (cc == 0) return [NSArray array];
    
    objects = calloc(cc + 2, sizeof(id));
    objAtIdx = (void *)[self methodForSelector:@selector(objectAtIndex:)];
    
    for (i = 0; i < cc; i++) {
      register id o;
      
      o = [objAtIdx(self, @selector(objectAtIndex:), i) valueForKey:_key];
      objects[i] = o ? o : null;
    }
    
    result = [NSArray arrayWithObjects:objects count:cc];
    if (objects) free(objects);
    return result;
  }
}

@end /* NSArray(EOKeyValueCoding) */

#endif /* !NeXT_Foundation_LIBRARY */
