[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [RFA/gdl2] EOKeyValueCoding && EONull
From: |
David Ayers |
Subject: |
Re: [RFA/gdl2] EOKeyValueCoding && EONull |
Date: |
Mon, 25 Aug 2003 01:30:41 +0200 |
User-agent: |
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4) Gecko/20030624 |
David Ayers wrote:
Hello Manuel,
I'd like your approval on this one. This is my first cleanup of
EOKeyValueCoding. I've been using this implementation more or less
since February, but I haven't consciously used some of GDL2 extensions
(well only in simple tests). What this does:
- Relies on -base/Foundation's implementation for all equivalent
implementations.
- Replaces -base/Foundation's implementation of unableToSetNilForKey:
to call unableToSetNullForKey:
- Optimizes NSArray computational implementations to use NSDecmial
structs instead of temporary NSDecimalNumber objects
- Cleans up serveral implementations.
- Adds documentation
After private approval, I've commited an updated version. The diff very
lengthy and hardly readable as it is basically a rewrite, but I've
attached it anyway.
I've also slipped in a simplication of EONull, which is now a simple
define to NSNull. To keep scripting projects (and
NSClassFromString(@"EONull") ) happy, there is still an EONull class (a
subclass of NSNull) that should return the NSNull instance on all
relevant methods. There remains edge cases that may cause certain tests
to still fail, but the situation should be a lot better than before. If
we hit those edge cases, we may have to resort to poseAs: but I'd like
to avoid that for now, as it has other implications.
Cheers.
David
Committed:
* EOControl/EOKeyValueCoding.h/m: Rewritten and documented.
Declared and documented EOKeyValueCoding categories currently
implemented by gnustep-base/Foundation in NSKeyValueCoding.
Removed FOUNDATION_HAS_KVC conditionals as gnustep-base/Foundation
implement NSKeyValueCoding.
(-[NSObject takeStoredValuesFromDictionary:]): Added declaration
reportedly missing in Apple's headers to compelte EOKeyValueCoding
declarations.
Most implementations rewritten.
* EOControl/EONull.h/m: Define EONull to NSNull to allow pointer
equality checks. Implement EONull as NSNull subclass that returns
the NSNull instance. Remove old assertions.
? EOObserver.patch
? gdl2.patch
? gdl2.update
Index: ChangeLog
===================================================================
RCS file: /cvsroot/gnustep/gnustep/dev-libs/gdl2/ChangeLog,v
retrieving revision 1.66
diff -u -d -r1.66 ChangeLog
--- ChangeLog 11 Jul 2003 19:04:04 -0000 1.66
+++ ChangeLog 24 Aug 2003 23:24:13 -0000
@@ -1,3 +1,19 @@
+2003-08-23 David Ayers <d.ayers@inode.at>
+
+ * EOControl/EOKeyValueCoding.h/m: Rewritten and documented.
+ Declared and documented EOKeyValueCoding categories currently
+ implemented by gnustep-base/Foundation in NSKeyValueCoding.
+ Removed FOUNDATION_HAS_KVC conditionals as gnustep-base/Foundation
+ implement NSKeyValueCoding.
+ (-[NSObject takeStoredValuesFromDictionary:]): Added declaration
+ reportedly missing in Apple's headers to compelte EOKeyValueCoding
+ declarations.
+ Most implementations rewritten.
+
+ * EOControl/EONull.h/m: Define EONull to NSNull to allow pointer
+ equality checks. Implement EONull as NSNull subclass that returns
+ the NSNull instance. Remove old assertions.
+
2003-07-11 David Ayers <d.ayers@inode.at>
* *.m: Include GNUstep.h if GNUSTEP is not defined for OS X.
Index: EOControl/EOKeyValueCoding.h
===================================================================
RCS file: /cvsroot/gnustep/gnustep/dev-libs/gdl2/EOControl/EOKeyValueCoding.h,v
retrieving revision 1.7
diff -u -d -r1.7 EOKeyValueCoding.h
--- EOControl/EOKeyValueCoding.h 2 May 2003 10:06:06 -0000 1.7
+++ EOControl/EOKeyValueCoding.h 24 Aug 2003 23:24:13 -0000
@@ -6,6 +6,9 @@
Author: Mirko Viviani <mirko.viviani@rccr.cremona.it>
Date: February 2000
+ Modified: David Ayers <d.ayers@inode.at>
+ Date: February 2003
+
This file is part of the GNUstep Database Library.
This library is free software; you can redistribute it and/or
@@ -36,34 +39,109 @@
#include <Foundation/Foundation.h>
#endif
-#include <EOControl/EODefines.h>
+#include "EODefines.h"
+/**
+ * GDL2 aims to be compatible with EOF of WebObjects 4.5 and expects to be
+ * compiled with gnustep-base or the current version Foundation of Mac OS X
+ * together with gnustep-baseadd, the Additions subproject of gnustep-base.
+ * As many of the EOKeyValueCoding methods have moved to NSKeyValueCoding,
+ * GDL2 merely implements those methods which are not part of NSKeyValueCoding
+ * or reimplements those methods to insure WebObjects 4.5 compatibility or
+ * augment the behavior for GDL2 specific features.
+ */
+@interface NSObject (EOKeyValueCoding)
-@interface NSObject (EOKVCPAdditions2)
-- (void)smartTakeValue: (id)anObject
- forKey: (NSString *)aKey;
-- (void)smartTakeValue: (id)anObject
- forKeyPath: (NSString *)aKeyPath;
-- (void)takeStoredValue: value
- forKeyPath: (NSString *)key;
-- (id)storedValueForKeyPath: (NSString *)key;
-#if !FOUNDATION_HAS_KVC
-- (void)takeStoredValuesFromDictionary: (NSDictionary *)dictionary;
-#endif
-- (NSDictionary *)valuesForKeyPaths: (NSArray *)keyPaths;
-- (NSDictionary *)storedValuesForKeyPaths: (NSArray *)keyPaths;
+/**
+ * Unimplemented here. Relies on NSKeyValueCoding.
+ */
+- (id)valueForKey: (NSString *)key;
+
+/**
+ * Unimplemented here. Relies on NSKeyValueCoding.
+ */
+- (void)takeValue: (id)value forKey: (NSString *)key;
+
+/**
+ * Unimplemented here. Relies on NSKeyValueCoding.
+ */
+- (id)storedValueForKey: (NSString *)key;
+
+/**
+ * Unimplemented here. Relies on NSKeyValueCoding.
+ */
+- (void)takeStoredValue: (id)value forKey: (NSString *)key;
+
+
+/**
+ * Unimplemented here. Relies on NSKeyValueCoding.
+ */
++ (BOOL)accessInstanceVariablesDirectly;
+
+/**
+ * Unimplemented here. Relies on NSKeyValueCoding.
+ */
++ (BOOL)useStoredAccessor;
+
+
+/**
+ * Does nothing. Key bindings are currently not cached so there is no
+ * need to flush them. This method exists for API compatibility.
+ */
++ (void)flushAllKeyBindings;
+
+
+/**
+ * Unimplemented here. Relies on NSKeyValueCoding.
+ */
+- (id)handleQueryWithUnboundKey: (NSString *)key;
+
+/**
+ * Unimplemented here. Relies on NSKeyValueCoding.
+ */
+- (void)handleTakeValue: (id)value forUnboundKey: (NSString *)key;
+
+/**
+ * This method is invoked by the EOKeyValueCoding mechanism when an attempt
+ * is made to set an null value for a scalar attribute. This implementation
+ * raises an NSInvalidArgument exception. <br/>
+ * The NSKeyValueCoding -unableToSetNilForKey: is overriden to invoke this
+ * method instead. We manipulate the runtime to insure that our implementation
+ * of unableToSetNilForKey: is used in favor of the one in gnustep-base or
+ * Foundation.
+ */
- (void)unableToSetNullForKey: (NSString *)key;
+
@end
-#if NeXT_Foundation_LIBRARY
-@interface NSObject (MacOSXRevealed)
-- (void)takeStoredValuesFromDictionary: (NSDictionary *)dictionary;
+@interface NSObject (EOKeyValueCodingAdditions)
+
+/**
+ * Unimplemented here. Relies on NSKeyValueCoding.
+ */
+- (id)valueForKeyPath: (NSString *)keyPath;
+
+/**
+ * Unimplemented here. Relies on NSKeyValueCoding.
+ */
+- (void)takeValue: (id)value forKeyPath: (NSString *)keyPath;
+
+/**
+ * Unimplemented here. Relies on NSKeyValueCoding.
+ */
+- (NSDictionary *)valuesForKeys: (NSArray *)keys;
+
+/**
+ * Unimplemented here. Relies on NSKeyValueCoding.
+ */
+- (void)takeValuesFromDictionary: (NSDictionary *)dictionary;
+
@end
-#endif
@interface NSArray (EOKeyValueCoding)
- (id)valueForKey: (NSString *)key;
- (id)valueForKeyPath: (NSString *)keyPath;
+
- (id)computeSumForKey: (NSString *)key;
- (id)computeAvgForKey: (NSString *)key;
- (id)computeCountForKey: (NSString *)key;
@@ -72,22 +150,56 @@
@end
-
-
-#if !FOUNDATION_HAS_KVC
@interface NSDictionary (EOKeyValueCoding)
+/*
+ * Overrides gnustep-base and Foundations implementation.
+ * See documentation or source file for details on how it differs.
+ */
- (id)valueForKey: (NSString *)key;
+- (id)storedValueForKey: (NSString *)key;
+- (id)valueForKeyPath: (NSString *)keyPath;
+- (id)storedValueForKeyPath: (NSString*)keyPath;
@end
@interface NSMutableDictionary (EOKeyValueCoding)
+/*
+ * Overrides gnustep-base and Foundations implementation.
+ * See documentation or source file for details on how it differs.
+ */
- (void)takeValue: (id)value
forKey: (NSString*)key;
@end
-#endif
+
+
+@interface NSObject (EOKVCGDL2Additions)
+/* These are hooks for EOGenericRecord KVC implementaion. */
+- (void)smartTakeValue: (id)anObject
+ forKey: (NSString *)aKey;
+- (void)smartTakeValue: (id)anObject
+ forKeyPath: (NSString *)aKeyPath;
+
+- (void)takeStoredValue: value
+ forKeyPath: (NSString *)key;
+- (id)storedValueForKeyPath: (NSString *)key;
+
+- (NSDictionary *)valuesForKeyPaths: (NSArray *)keyPaths;
+- (NSDictionary *)storedValuesForKeyPaths: (NSArray *)keyPaths;
+@end
+
#define EOUnknownKeyException NSUnknownKeyException;
GDL2CONTROL_EXPORT NSString *EOTargetObjectUserInfoKey;
GDL2CONTROL_EXPORT NSString *EOUnknownUserInfoKey;
+
+/*
+ * The following declaration is reportedly missing in Apple's headers,
+ * yet are implemented.
+ */
+#if NeXT_Foundation_LIBRARY
+@interface NSObject (MacOSX)
+- (void)takeStoredValuesFromDictionary: (NSDictionary *)dictionary;
+@end
+#endif
#endif /* __EOKeyValueCoding_h__ */
Index: EOControl/EOKeyValueCoding.m
===================================================================
RCS file: /cvsroot/gnustep/gnustep/dev-libs/gdl2/EOControl/EOKeyValueCoding.m,v
retrieving revision 1.13
diff -u -d -r1.13 EOKeyValueCoding.m
--- EOControl/EOKeyValueCoding.m 11 Jul 2003 19:04:05 -0000 1.13
+++ EOControl/EOKeyValueCoding.m 24 Aug 2003 23:24:13 -0000
@@ -1,7 +1,7 @@
/**
EOKeyValueCoding.m <title>EOKeyValueCoding</title>
- Copyright (C) 1996-2002 Free Software Foundation, Inc.
+ Copyright (C) 1996-2002, 2003 Free Software Foundation, Inc.
Author: Mircea Oancea <mircea@jupiter.elcom.pub.ro>
Date: November 1996
@@ -12,8 +12,9 @@
Author: Manuel Guesdon <mguesdon@oxymium.net>
Date: January 2002
- $Revision: 1.13 $
- $Date: 2003/07/11 19:04:05 $
+ Author: David Ayers <d.ayers@inode.at>
+ Date: February 2003
+
<abstract></abstract>
@@ -63,495 +64,407 @@
#include <gnustep/base/GSCategories.h>
#endif
-#include <ctype.h>
-
#include <EOControl/EOKeyValueCoding.h>
#include <EOControl/EONSAddOns.h>
#include <EOControl/EODebug.h>
#include <EOControl/EONull.h>
-#include <ctype.h>
-
-
-/*
- * EOKeyValueCodingAdditions implementation
- */
-
-NSString *EOTargetObjectUserInfoKey = @"EOTargetObjectUserInfoKey";
-NSString *EOUnknownUserInfoKey = @"EOUnknownUserInfoKey";
-
-@implementation NSObject (EOKVCPAdditions2)
+#include <gnustep/base/GSObjCRuntime.h>
-+ (void) flushClassKeyBindings
-{
-}
+static EONull *null = nil;
+static SEL oaiSel;
+static BOOL strictWO;
-+ (void) flushAllKeyBindings
+static inline void
+initialize(void)
{
+ if (null == nil)
+ {
+ null = [EONull null];
+ oaiSel = @selector(objectAtIndex:);
+ strictWO = GSUseStrictWO451Compatibility(nil);
+ }
}
-- (void) unableToSetNullForKey: (NSString *)key
-{
- [NSException raise: NSInvalidArgumentException
- format: @"%@ -- %@ 0x%x: Given nil value to set for key \"%@\"",
- NSStringFromSelector(_cmd), NSStringFromClass([self class]),
- self, key];
-}
+/* This macro is only used locally in defined places so for the sake
+ of efficiency, we don't use the do {} while (0) pattern. */
+#define INITIALIZE if (null == nil) initialize();
-/** if key is a bidirectional rel, use addObject:toBothSidesOfRelationship
otherwise call takeValue:forKey: **/
-- (void)smartTakeValue: (id)anObject
- forKey: (NSString *)aKey
+/*
+ * This dummy class exists to provide a replacement implementation for
+ * NSObject -unableToSetNilForKey:, which calls -unableToSetNullForKey:
+ * as defined in WO4.5. We need this mechanism as a category cannot
+ * reliably override the category in gnustep-base or Foundation.
+ */
+@interface NilToNull : NSObject
+@end
+@interface NilToNull (SurrpressWarning)
+- (void) unableToSetNullForKey: (NSString *)key;
+@end
+@implementation NilToNull
++ (void)load
{
- [self takeValue: anObject
- forKey: aKey];
-}
+ Class cls;
+ SEL sel;
+ IMP imp;
+ GSMethod method;
-- (void)smartTakeValue: (id)anObject
- forKeyPath: (NSString *)aKeyPath
-{
- NSRange r = [aKeyPath rangeOfString: @"."];
+ imp = NULL;
+ sel = @selector(unableToSetNilForKey:);
+ cls = GSClassFromName("NSObject");
- if (r.length == 0)
+ method = GSGetInstanceMethodNotInherited(self, sel);
+ if (method != METHOD_NULL)
{
- [self smartTakeValue: anObject
- forKey: aKeyPath];
+ imp = method->method_imp;
}
else
{
- NSString *key = [aKeyPath substringToIndex: r.location];
- NSString *path = [aKeyPath substringFromIndex: NSMaxRange(r)];
-
- [[self valueForKey: key] smartTakeValue: anObject
- forKeyPath: path];
+ fprintf(stderr,
+ "%s: Could not find method unableToSetNilForKey: in NilToNil!\n",
+ __FILE__);
+ abort();
}
-}
-- (void)takeStoredValue: value
- forKeyPath: (NSString *)key
-{
- NSArray *pathArray;
- NSString *path;
- id obj = self;
- int i, count;
-
- EOFLOGObjectFnStartCond(@"EOKVC");
-
- pathArray = [key componentsSeparatedByString:@"."];
- count = [pathArray count];
-
- for (i = 0; i < (count - 1); i++)
+ method = GSGetInstanceMethodNotInherited(cls, sel);
+ if (method != METHOD_NULL)
{
- path = [pathArray objectAtIndex: i];
- obj = [obj valueForKey: path];
+ method->method_imp = imp;
}
-
- path = [pathArray lastObject];
- [obj takeStoredValue: value forKey: path];
-
- EOFLOGObjectFnStopCond(@"EOKVC");
-}
-
-- (id)storedValueForKeyPath: (NSString *)key
-{
- NSArray *pathArray = nil;
- NSString *path;
- id obj = self;
- int i, count;
- EOFLOGObjectFnStartCond(@"EOKVC");
- pathArray = [key componentsSeparatedByString:@"."];
- count = [pathArray count];
-
- for(i=0; i < (count-1); i++)
+ else
{
- path = [pathArray objectAtIndex:i];
- obj = [obj valueForKey:path];
+ fprintf(stderr,
+ "%s: Could not find method unableToSetNilForKey: in NSObject!\n",
+ __FILE__);
+ abort();
}
-
- path = [pathArray lastObject];
- obj=[obj storedValueForKey:path];
- EOFLOGObjectFnStopCond(@"EOKVC");
- return obj;
+ GSFlushMethodCacheForClass(cls);
}
-#if !FOUNDATION_HAS_KVC
-- (void)takeStoredValuesFromDictionary: (NSDictionary *)dictionary
+- (void) unableToSetNilForKey: (NSString *)key
{
- NSEnumerator *keyEnum;
- id key;
- id val;
- EONull *null;
-
- EOFLOGObjectFnStartCond(@"EOKVC");
-
- keyEnum = [dictionary keyEnumerator];
-
- null = (EONull *)[EONull null];
-
- while ((key = [keyEnum nextObject]))
- {
- val = [dictionary objectForKey: key];
-
- if (val == null)
- val = nil;
-
- [self takeStoredValue: val forKey: key];
- }
-
- EOFLOGObjectFnStopCond(@"EOKVC");
+ [self unableToSetNullForKey: key];
}
-#endif /* !FOUNDATION_HAS_KVC */
+@end
-- (NSDictionary *)storedValuesForKeyPaths: (NSArray *)keyPaths
+@implementation NSObject (_EOKeyValueCodingCompatibility)
+/* See EODeprecated.h. */
++ (void) flushClassKeyBindings
{
- NSDictionary *values = nil;
- int i, n;
- NSMutableArray *newKeyPaths = nil;
- NSMutableArray *newVals = nil;
- EONull *null;
-
- EOFLOGObjectFnStartCond(@"EOKVC");
-
- n = [keyPaths count];
-
- newKeyPaths = [[[NSMutableArray alloc] initWithCapacity: n]
- autorelease];
- newVals = [[[NSMutableArray alloc] initWithCapacity: n]
- autorelease];
- null = (EONull *)[EONull null];
-
- for (i = 0; i < n; i++)
- {
- id keyPath = [keyPaths objectAtIndex: i];
- id val = nil;
-
- NS_DURING //DEBUG Only ?
- {
- val = [self storedValueForKeyPath: keyPath];
- }
- NS_HANDLER
- {
- NSLog(@"EXCEPTION %@", localException);
- NSDebugMLog(@"EXCEPTION %@", localException);
- [localException raise];
- }
- NS_ENDHANDLER;
-
- if (val == nil)
- val = null;
-
- [newKeyPaths addObject: keyPath];
- [newVals addObject: val];
- }
-
- values = [NSDictionary dictionaryWithObjects: newVals
- forKeys: newKeyPaths];
- EOFLOGObjectFnStopCond(@"EOKVC");
-
- return values;
}
-- (NSDictionary *)valuesForKeyPaths: (NSArray *)keyPaths
+/* See header file for documentation. */
++ (void) flushAllKeyBindings
{
- NSDictionary *values = nil;
- int i;
- int n;
- NSMutableArray *newKeyPaths;
- NSMutableArray *newVals;
- EONull *null;
-
- EOFLOGObjectFnStartCond(@"EOKVC");
-
- n = [keyPaths count];
- newKeyPaths = [[[NSMutableArray alloc] initWithCapacity: n]
- autorelease];
- newVals = [[[NSMutableArray alloc] initWithCapacity: n]
- autorelease];
- null = (EONull *)[EONull null];
-
- for (i = 0; i < n; i++)
- {
- id keyPath = [keyPaths objectAtIndex: i];
- id val = nil;
-
- NS_DURING //DEBUG Only ?
- {
- val = [self valueForKeyPath: keyPath];
- }
- NS_HANDLER
- {
- NSLog(@"EXCEPTION %@", localException);
- NSDebugMLog(@"EXCEPTION %@",localException);
- [localException raise];
- }
- NS_ENDHANDLER;
-
- if (val == nil)
- val = null;
-
- [newKeyPaths addObject: keyPath];
- [newVals addObject: val];
- }
-
- values = [NSDictionary dictionaryWithObjects: newVals
- forKeys: newKeyPaths];
-
- EOFLOGObjectFnStopCond(@"EOKVC");
-
- return values;
}
+/* See header file for documentation. */
+- (void) unableToSetNullForKey: (NSString *)key
+{
+ [NSException raise: NSInvalidArgumentException
+ format: @"%@ -- %@ 0x%x: Given nil value to set for key \"%@\"",
+ NSStringFromSelector(_cmd), NSStringFromClass([self class]),
+ self, key];
+}
@end
@implementation NSArray (EOKeyValueCoding)
+/**
+ * EOKeyValueCoding protocol<br/>
+ * This overrides NSObjects implementation of this method.
+ * Generally this method returns an array of objects
+ * returned by invoking [NSObject-valueForKey:]
+ * for each item in the receiver, substituting EONull for nil.
+ * Keys formated like "@function.someKey" are resolved by invoking
+ * [NSArray-computeFuncionWithKey:] "someKey" on the reviever.
+ * The following functions are supported by default:
+ * <list>
+ * <item>@sum -> -computeSumForKey:</item>
+ * <item>@avg -> -computeAvgForKey:</item>
+ * <item>@max -> -computeMaxForKey:</item>
+ * <item>@min -> -computeMinForKey:</item>
+ * <item>@count -> -computeCountForKey:</item>
+ * </list>
+ * As a special case the @count function does not require a key,
+ * in fact, any key supplied is ignored.
+ * As another special case the key "count" is not forwarded to each object
+ * of the receiver but returns the number of objects of the receiver.<br/>
+ * There is no special handling of EONull. Therefore expect exceptions
+ * on EONull not responding to decimalValue and compare: when the are
+ * used with this mechanism.
+ */
- (id)valueForKey: (NSString *)key
{
- id result = nil;
- const char *str;
-
- EOFLOGObjectFnStartCond(@"EOKVC");
- //EOFLOGObjectLevelArgs(@"EOKVC", @"key=%@",
- // key);
+ id result;
- str=[key cString];
+ INITIALIZE;
- if (str && *str == '@')
+ EOFLOGObjectFnStartCond(@"EOKVC");
+ if ([key isEqualToString: @"count"] || [key isEqualToString: @"@count"])
{
- if ([key length] > 1)
- {
- if ([key isEqualToString: @"@count"]) //the only known case because
we haven't implemented custom operators
- result = [super valueForKey: @"count"];
- else // for unknwon case: call computeXXForKey:nil
- {
- NSMutableString *selString = [NSMutableString
stringWithCapacity:10];
- SEL computeSelector;
- char l = str[1];
-
- if (islower(l))
- l = toupper(l);
-
- [selString appendString: @"compute"];
- [selString appendString: [NSString stringWithCString: &l
- length: 1]];
- [selString appendString: [NSString stringWithCString: &str[2]]];
- [selString appendString: @"ForKey:"];
-
- computeSelector = NSSelectorFromString(selString);
- result = [self performSelector: computeSelector
- withObject: nil];
- }
- }
+ result = [NSDecimalNumber numberWithUnsignedInt: [self count]];
}
- else if ([key isEqualToString: @"count"]) //Special case: Apple Doc is
wrong; here we return -count
+ else if ([key hasPrefix:@"@"])
{
- static BOOL warnedCount = NO;
- if (warnedCount == NO)
- {
- warnedCount = YES;
- NSWarnLog(@"use of special 'count' key may works differently with
only foundation base", "");
- }
- result = [super valueForKey: key];
+ NSString *selStr;
+ SEL sel;
+ NSRange r;
+
+ r = [key rangeOfString:@"."];
+ NSAssert(r.location!=NSNotFound,
+ @"Invalid computational key structure");
+ r.length = r.location - 1; /* set length of key (w/o @) */
+ r.location = 1; /* remove leading '@' */
+ selStr = [NSString stringWithFormat: @"compute%@ForKey:",
+ [[key substringWithRange: r] capitalizedString]];
+ sel = NSSelectorFromString(selStr);
+ NSAssert(sel!=NULL,@"Invalid computational key");
+
+ result = [self performSelector: sel /* skip located '.' */
+ withObject: [key substringFromIndex: NSMaxRange(r) + 1]];
}
else
{
result = [self resultsOfPerformingSelector: @selector(valueForKey:)
withObject: key
- defaultResult: [EONull null]];
+ defaultResult: null];
}
EOFLOGObjectFnStopCond(@"EOKVC");
-
return result;
}
+/**
+ * EOKeyValueCoding protocol<br/>
+ * Returns the object returned by invoking [NSObject-valueForKeyPath:]
+ * on the object returned by invoking [NSObject-valueForKey:]
+ * on the reciever with the first key component supplied by the key path,
+ * with rest of the key path.<br/>
+ * If the first component starts with "@", the first component includes the key
+ * of the computational key component and as the form "@function.key".
+ * If there is only one key component, this method invokes
+ * [NSObject-valueForKey:] in the receiver with that component.
+ * All computational components are expected to specifiy a key with the
+ * exception of @count, in which case the key maybe omitted.
+ * Unlike the reference implementation GDL2 allows you to continue the keyPath
+ * in a meaningfull way after @count but the path must then contain a key as
+ * the computational key structure implies.
+ * (i.e. you may use "@count.self.decimalValue") The actual key "self" is
+ * infact ignored during the computation, but the formal structure must be
+ * maintained.<br/>
+ * It should be mentioned that the reference implementation
+ * would return the result of "@count" independent
+ * of any additional key paths, even if they were meaningless like
+ * "@count.bla.strange". GDL2 will raise, if the object returned by
+ * valueForKey:@"count.bla" (which generally is an NSDecimalNumber) raises on
+ * valueForKey:@"strange".
+ */
- (id)valueForKeyPath: (NSString *)keyPath
{
- id result = nil;
- const char *str;
+ NSRange r;
+ id result;
EOFLOGObjectFnStartCond(@"EOKVC");
- //EOFLOGObjectLevelArgs(@"EOKVC", @"keyPath=%@",
- // keyPath);
-
- str = [keyPath cString];
-
- if (str && *str == '@')
+ r = [keyPath rangeOfString: @"."];
+ if ([keyPath hasPrefix: @"@"] == YES &&
+ [keyPath isEqualToString: @"@count"] == NO)
{
- if ([keyPath length] > 1)
- {
- NSMutableString *selString = [NSMutableString stringWithCapacity: 10];
- NSArray *pathArray = [keyPath componentsSeparatedByString: @"."];
- NSString *fn = [pathArray objectAtIndex:0];
- NSString *key = nil;
- SEL computeSelector;
- char l;
-
- str = [fn cString];
- l = str[1];
-
- if (islower(l))
- l = toupper(l);
-
- [selString appendString: @"compute"];
- [selString appendString: [NSString stringWithCString: &l
- length: 1]];
- [selString appendString: [NSString stringWithCString: &str[2]]];
- [selString appendString: @"ForKey:"];
-
- computeSelector = NSSelectorFromString(selString);
-
- if ([pathArray count] > 1)
- {
- key = [pathArray objectAtIndex: 1];
-
- result = [self performSelector: computeSelector
- withObject: key];
- }
- else
- result = [self performSelector: computeSelector
- withObject: @""];
+ NSRange rr;
+ unsigned length;
- if (result && [pathArray count] > 2)
- {
- NSArray *rightKeyPathArray
- = [pathArray subarrayWithRange:
- NSMakeRange(2, [pathArray count] - 2)];
- NSString *rightKeyPath
- = [rightKeyPathArray componentsJoinedByString: @"."];
+ length = [keyPath length];
+ NSAssert1(r.location!=NSNotFound && length > r.location,
+ @"invalid computational keyPath:%@", keyPath);
- result = [result valueForKeyPath: rightKeyPath];
- }
- }
+ rr.location = NSMaxRange(r);
+ rr.length = length - rr.location;
+ r = [keyPath rangeOfString: @"."
+ options: 0
+ range: rr];
}
- else if ([keyPath isEqualToString: @"count"]) //Special case: Apple Doc is
wrong; here we return -count
+
+ if (r.length == 0)
{
- static BOOL warnedCount = NO;
- if (warnedCount == NO)
- {
- warnedCount = YES;
- NSWarnLog(@"use of special 'count' key may works differently with
only foundation base", "");
- }
- result = [super valueForKeyPath: keyPath];
+ result = [self valueForKey: keyPath];
}
- else
- result = [self resultsOfPerformingSelector: @selector(valueForKeyPath:)
- withObject: keyPath
- defaultResult: [EONull null]];
+ else
+ {
+ NSString *key = [keyPath substringToIndex: r.location];
+ NSString *path = [keyPath substringFromIndex: NSMaxRange(r)];
- EOFLOGObjectFnStopCond(@"EOKVC");
+ result = [[self valueForKey: key] valueForKeyPath: path];
+ }
+ EOFLOGObjectFnStopCond(@"EOKVC");
return result;
}
+/**
+ * Iterates over the objects of the receiver send each object valueForKey:
+ * with the parameter. The decimalValue of the returned object is accumalted.
+ * An empty array returns NSDecimalNumber 0.
+ */
- (id)computeSumForKey: (NSString *)key
{
- NSEnumerator *arrayEnum;
- NSDecimalNumber *item, *ret;
+ NSDecimalNumber *ret;
+ NSDecimal result, left, right;
+ NSRoundingMode mode;
+ unsigned int i, count;
+ IMP oai;
- EOFLOGObjectFnStartCond(@"EOKVC");
+ INITIALIZE;
- arrayEnum = [self objectEnumerator];
- ret = [NSDecimalNumber zero];
+ EOFLOGObjectFnStartCond(@"EOKVC");
+ mode = [[NSDecimalNumber defaultBehavior] roundingMode];
+ oai = [self methodForSelector: oaiSel];
+ count = [self count];
+ NSDecimalFromComponents(&result, 0, 0, NO);
- while ((item = [arrayEnum nextObject]))
- ret=[ret decimalNumberByAdding: [item valueForKey:key]];
+ for (i=0; i<count; i++)
+ {
+ left = result;
+ right = [[(*oai)(self, oaiSel, i) valueForKey: key] decimalValue];
+ NSDecimalAdd(&result, &left, &right, mode);
+ }
+ ret = [NSDecimalNumber decimalNumberWithDecimal: result];
EOFLOGObjectFnStopCond(@"EOKVC");
-
return ret;
}
+/**
+ * Iterates over the objects of the receiver send each object valueForKey:
+ * with the parameter. The decimalValue of the returned object is accumalted
+ * and then divided by number of objects contained by the receiver as returned
+ * by [NSArray-coung]. An empty array returns NSDecimalNumber 0.
+ */
- (id)computeAvgForKey: (NSString *)key
{
- NSEnumerator *arrayEnum;
- NSDecimalNumber *item, *ret;
+ NSDecimalNumber *ret;
+ NSDecimal result, left, right;
+ NSRoundingMode mode;
+ unsigned int i, count;
+ IMP oai;
- EOFLOGObjectFnStartCond(@"EOKVC");
+ INITIALIZE;
- arrayEnum = [self objectEnumerator];
- ret = [NSDecimalNumber zero];
+ EOFLOGObjectFnStartCond(@"EOKVC");
+ mode = [[NSDecimalNumber defaultBehavior] roundingMode];
+ oai = [self methodForSelector: oaiSel];
+ count = [self count];
+ NSDecimalFromComponents(&result, 0, 0, NO);
- while ((item = [arrayEnum nextObject]))
- ret=[ret decimalNumberByAdding: [item valueForKey:key]];
+ for (i=0; i<count; i++)
+ {
+ left = result;
+ right = [[(*oai)(self, oaiSel, i) valueForKey: key] decimalValue];
+ NSDecimalAdd(&result, &left, &right, mode);
+ }
- ret = [ret decimalNumberByDividingBy:
- [NSDecimalNumber decimalNumberWithMantissa: [self count]
- exponent: 0
- isNegative:NO]];
+ left = result;
+ NSDecimalFromComponents(&right, (unsigned long long) count, 0, NO);
+ NSDecimalDivide(&result, &left, &right, mode);
+
+ ret = [NSDecimalNumber decimalNumberWithDecimal: result];
EOFLOGObjectFnStopCond(@"EOKVC");
-
return ret;
}
- (id)computeCountForKey: (NSString *)key
{
- id ret;
+ id result;
EOFLOGObjectFnStartCond(@"EOKVC");
-
- ret = [NSNumber numberWithInt: [self count]];
+ result = [NSDecimalNumber numberWithUnsignedInt: [self count]];
EOFLOGObjectFnStopCond(@"EOKVC");
-
- return ret;
+ return result;
}
- (id)computeMaxForKey: (NSString *)key
{
- NSEnumerator *arrayEnum;
- NSDecimalNumber *item, *max;
- id value = nil;
-
- EOFLOGObjectFnStartCond(@"EOKVC");
+ id result, resultVal;
+ unsigned int i, count;;
- arrayEnum = [self objectEnumerator];
+ INITIALIZE;
- item = [arrayEnum nextObject];
- value = [item valueForKey:key];
- max = value;
+ EOFLOGObjectFnStartCond(@"EOKVC");
+ result = nil;
+ resultVal = nil;
+ count = [self count];
- if (item != nil)
+ if (count > 0)
{
- while ((item = [arrayEnum nextObject]))
+ id current,currentVal;
+ IMP oai;
+
+ oai = [self methodForSelector: oaiSel];
+ for(i=0; i<count && (resultVal == nil || resultVal == null); i++)
{
- value = [item valueForKey:key];
- if ([max compare: value] == NSOrderedAscending)
- max = value;
+ result = (*oai)(self, oaiSel, i);
+ resultVal = [result valueForKey: key];
+ }
+ for (; i<count; i++)
+ {
+ current = (*oai)(self, oaiSel, i);
+ currentVal = [current valueForKey: key];
+
+ if (currentVal == nil || currentVal == null) continue;
+
+ if ([resultVal compare: currentVal] == NSOrderedAscending)
+ {
+ result = current;
+ resultVal = currentVal;
+ }
}
}
EOFLOGObjectFnStopCond(@"EOKVC");
-
- return max;
+ return result;
}
- (id)computeMinForKey: (NSString *)key
{
- NSEnumerator *arrayEnum;
- NSDecimalNumber *item, *min;
- id value = nil;
+ id result, resultVal;
+ unsigned int i, count;
- EOFLOGObjectFnStartCond(@"EOKVC");
+ INITIALIZE;
- arrayEnum = [self objectEnumerator];
- item = [arrayEnum nextObject];
- value = [item valueForKey:key];
- min = value;
+ EOFLOGObjectFnStartCond(@"EOKVC");
+ result = nil;
+ resultVal = nil;
+ count = [self count];
- if (item != nil)
+ if (count > 0)
{
- while ((item = [arrayEnum nextObject]))
+ id current, currentVal;
+ IMP oai;
+
+ oai = [self methodForSelector: oaiSel];
+ for(i=0; i<count && (resultVal == nil || resultVal == null); i++)
{
- value = [item valueForKey:key];
- if ([min compare: value] == NSOrderedDescending)
- min = value;
+ result = (*oai)(self, oaiSel, i);
+ resultVal = [result valueForKey: key];
+ }
+ for (; i<count; i++)
+ {
+ current = (*oai)(self, oaiSel, i);
+ currentVal = [current valueForKey: key];
+
+ if (currentVal == nil || currentVal == null) continue;
+
+ if ([resultVal compare: currentVal] == NSOrderedDescending)
+ {
+ result = current;
+ resultVal = currentVal;
+ }
}
}
EOFLOGObjectFnStopCond(@"EOKVC");
-
- return min;
+ return result;
}
@end
@@ -559,61 +472,80 @@
@implementation NSDictionary (EOKeyValueCoding)
-#if !FOUNDATION_HAS_KVC
-- (id)valueForKey:(NSString *)key
+/**
+ * Returns the object stored in the dictionary for this key.
+ * Unlike Foundation, this method may return objects for keys other than
+ * those explicitly stored in the receiver. These special keys are
+ * 'count', 'allKeys' and 'allValues'.
+ * We override the implementation to account for these
+ * special keys.
+ */
+- (id)valueForKey: (NSString *)key
{
id value;
EOFLOGObjectFnStartCond(@"EOKVC");
- EOFLOGObjectLevelArgs(@"EOKVC", @"key=%@",
- key);
+ //EOFLOGObjectLevelArgs(@"EOKVC", @"key=%@",
+ // key);
value = [self objectForKey: key];
if (!value)
{
if ([key isEqualToString: @"allValues"])
- {
- static BOOL warnedAllValues = NO;
- if (warnedAllValues == NO)
+ {
+#ifdef NeXT_Foundation_LIBRARY
+ static BOOL warnedValuesKeys = NO;
+ if (warnedValuesKeys == NO)
{
- warnedAllValues = YES;
- NSWarnLog(@"use of special 'allValues' key works differently
with only foundation base", "");
+ warnedValuesKeys = YES;
+ NSWarnMLog(@"Foundation does not return a value for the special
'allValues' key", "");
}
-
- value = [self allValues];
- }
+#endif
+ value = [self allValues];
+ }
else if ([key isEqualToString: @"allKeys"])
- {
+ {
+#ifdef NeXT_Foundation_LIBRARY
static BOOL warnedAllKeys = NO;
if (warnedAllKeys == NO)
{
warnedAllKeys = YES;
- NSWarnLog(@"use of special 'allKeys' key works differently with
only foundation base", "");
+ NSWarnMLog(@"Foundation does not return a value for the special
'allKeys' key", "");
}
-
- value = [self allKeys];
- }
+#endif
+ value = [self allKeys];
+ }
else if ([key isEqualToString: @"count"])
- {
+ {
+#ifdef NeXT_Foundation_LIBRARY
static BOOL warnedCount = NO;
if (warnedCount == NO)
{
warnedCount = YES;
- NSWarnLog(@"use of special 'count' key works differently with
only foundation base", "");
+ NSWarnMLog(@"Foundation does not return a value for the special
'count' key", "");
}
-
- value = [NSNumber numberWithInt: [self count]];
- }
+#endif
+ value = [NSNumber numberWithUnsignedInt: [self count]];
+ }
}
- //EOFLOGObjectLevelArgs(@"EOKVC", @"key=%@ value: %p (class %@)",
+ //EOFLOGObjectLevelArgs(@"EOKVC", @"key=%@ value: %p (class=%@)",
// key, value, [value class]);
EOFLOGObjectFnStopCond(@"EOKVC");
return value;
}
+/**
+ * Returns the object stored in the dictionary for this key.
+ * Unlike Foundation, this method may return objects for keys other than
+ * those explicitly stored in the receiver. These special keys are
+ * 'count', 'allKeys' and 'allValues'.
+ * We do not simply invoke [NSDictionary-valueForKey:]
+ * to avoid recursions in subclasses that might implement
+ * [NSDictionary-valueForKey:] by calling [NSDictionary-storedValueForKey:]
+ */
- (id)storedValueForKey: (NSString *)key
{
id value;
@@ -627,11 +559,17 @@
if (!value)
{
if ([key isEqualToString: @"allValues"])
- value = [self allValues];
+ {
+ value = [self allValues];
+ }
else if ([key isEqualToString: @"allKeys"])
- value = [self allKeys];
+ {
+ value = [self allKeys];
+ }
else if ([key isEqualToString: @"count"])
- value = [NSNumber numberWithInt: [self count]];
+ {
+ value = [NSNumber numberWithUnsignedInt: [self count]];
+ }
}
//EOFLOGObjectLevelArgs(@"EOKVC", @"key=%@ value: %p (class=%@)",
@@ -640,17 +578,24 @@
return value;
}
-#endif /* !FOUNDATION_HAS_KVC */ //MG
+/**
+ * First checks whether the entire keyPath is contained as a key
+ * in the receiver before invoking super's implementation.
+ * (The special quoted key handling will probably be moved
+ * to a GSWDictionary subclass to be used by GSWDisplayGroup.)
+ */
- (id)valueForKeyPath: (NSString*)keyPath
{
- id value = nil;
+ id value = nil;
+
+ INITIALIZE;
EOFLOGObjectFnStartCond(@"EOKVC");
//EOFLOGObjectLevelArgs(@"EOKVC", @"keyPath=\"%@\"",
// keyPath);
- if ([keyPath hasPrefix: @"'"]) //user defined composed key
+ if ([keyPath hasPrefix: @"'"] && strictWO == NO) //user defined composed key
{
NSMutableArray *keyPathArray = [[[[keyPath stringByDeletingPrefix: @"'"]
componentsSeparatedByString: @"."]
@@ -704,16 +649,15 @@
}
else
{
- //return super valueForKeyPath:keyPath only if there's no object for
entire key keyPath
+ /*
+ * Return super valueForKeyPath: only
+ * if there's no object for entire key keyPath
+ */
value = [self objectForKey: keyPath];
EOFLOGObjectLevelArgs(@"EOKVC",@"keyPath=%@ tmpValue: %p (class=%@)",
keyPath,value,[value class]);
- /* if([value isEqual:[EONull null]] == YES) //???
- value=nil;
- else */
-
if (!value)
value = [super valueForKeyPath: keyPath];
}
@@ -724,17 +668,24 @@
return value;
}
-//MG #endif /* !FOUNDATION_HAS_KVC */
+/**
+ * First checks whether the entire keyPath is contained as a key
+ * in the receiver before invoking super's implementation.
+ * (The special quoted key handling will probably be moved
+ * to a GSWDictionary subclass to be used by GSWDisplayGroup.)
+ */
- (id)storedValueForKeyPath: (NSString*)keyPath
{
id value = nil;
+ INITIALIZE;
+
EOFLOGObjectFnStartCond(@"EOKVC");
//EOFLOGObjectLevelArgs(@"EOKVC",@"keyPath=\"%@\"",
// keyPath);
- if ([keyPath hasPrefix: @"'"]) //user defined composed key
+ if ([keyPath hasPrefix: @"'"] && strictWO == NO) //user defined composed key
{
NSMutableArray *keyPathArray = [[[[keyPath stringByDeletingPrefix: @"'"]
componentsSeparatedByString: @"."]
@@ -788,16 +739,15 @@
}
else
{
- //return super valueForKeyPath:keyPath only if there's no object for
entire key keyPath
+ /*
+ * Return super valueForKeyPath: only
+ * if there's no object for entire key keyPath
+ */
value = [self objectForKey: keyPath];
//EOFLOGObjectLevelArgs(@"EOKVC",@"keyPath=%@ tmpValue: %p (class=%@)",
// keyPath,value,[value class]);
- /* if([value isEqual:[EONull null]] == YES) //???
- value=nil;
- else */
-
if (!value)
value = [super storedValueForKeyPath: keyPath];
}
@@ -818,38 +768,15 @@
isSmart: (BOOL)smartFlag;
@end
-@implementation NSMutableDictionary (EOKeyValueCoding)
-
-#if !FOUNDATION_HAS_KVC
-- (void)takeValue: (id)value
- forKey: (NSString *)key
-{
- EOFLOGObjectFnStartCond(@"EOKVC");
-
- if (value)
- [self setObject: value
- forKey: key];
- else
- [self removeObjectForKey: key];
-
- EOFLOGObjectFnStopCond(@"EOKVC");
-}
-
-- (void)takeStoredValue: (id)value
- forKey: (NSString *)key
-{
- EOFLOGObjectFnStartCond(@"EOKVC");
-
- if (value)
- [self setObject: value
- forKey: key];
- else
- [self removeObjectForKey: key];
-
- EOFLOGObjectFnStopCond(@"EOKVC");
-}
-#endif /* !FOUNDATION_HAS_KVC */
+@implementation NSMutableDictionary (EOKVCGNUstepExtensions)
+/**
+ * Method to augment the NSKeyValueCoding implementation
+ * to account for added functionality such as quoted key paths.
+ * (The special quoted key handling will probably be moved
+ * to a GSWDictionary subclass to be used by GSWDisplayGroup.
+ * this method then becomes obsolete.)
+ */
- (void)smartTakeValue: (id)value
forKeyPath: (NSString*)keyPath
{
@@ -858,7 +785,13 @@
isSmart:YES];
}
-//#if !FOUNDATION_HAS_KVC
+/**
+ * Overrides gnustep-base and Foundations implementation
+ * to account for added functionality such as quoted key paths.
+ * (The special quoted key handling will probably be moved
+ * to a GSWDictionary subclass to be used by GSWDisplayGroup.
+ * this method then becomes obsolete.)
+ */
- (void)takeValue: (id)value
forKeyPath: (NSString *)keyPath
{
@@ -866,8 +799,14 @@
forKeyPath:keyPath
isSmart:NO];
}
-//#endif /* !FOUNDATION_HAS_KVC */
+/**
+ * Support method to augment the NSKeyValueCoding implementation
+ * to account for added functionality such as quoted key paths.
+ * (The special quoted key handling will probably be moved
+ * to a GSWDictionary subclass to be used by GSWDisplayGroup.
+ * this method then becomes obsolete.)
+ */
- (void)takeValue: (id)value
forKeyPath: (NSString *)keyPath
isSmart: (BOOL)smartFlag
@@ -876,7 +815,9 @@
//EOFLOGObjectLevelArgs(@"EOKVC", @"keyPath=\"%@\"",
// keyPath);
- if ([keyPath hasPrefix: @"'"]) //user defined composed key
+ INITIALIZE;
+
+ if ([keyPath hasPrefix: @"'"] && strictWO == NO) //user defined composed key
{
NSMutableArray *keyPathArray = [[[[keyPath stringByDeletingPrefix: @"'"]
componentsSeparatedByString: @"."]
@@ -947,16 +888,26 @@
}
else
{
- if (value)
- [self setObject: value
- forKey: keyPath];
+ if (value == nil)
+ {
+ [self removeObjectForKey: keyPath];
+ }
else
- [self removeObjectForKey: keyPath];
- }
+ {
+ [self setObject: value forKey: keyPath];
+ }
+ }
EOFLOGObjectFnStopCond(@"EOKVC");
}
+/**
+ * Calls [NSMutableDictionary-setObject:forKey:] using the full keyPath
+ * as a key, if the value is non nil. Otherwise calls
+ * [NSDictionary-removeObjectForKey:] with the full keyPath.
+ * (The special quoted key handling will probably be moved
+ * to a GSWDictionary subclass to be used by GSWDisplayGroup.)
+ */
- (void)takeStoredValue: (id)value
forKeyPath: (NSString *)keyPath
{
@@ -1039,5 +990,226 @@
EOFLOGObjectFnStopCond(@"EOKVC");
}
+
+@end
+
+@implementation NSObject (EOKVCGNUstepExtensions)
+
+/**
+ * This is a GDL2 extension. This convenience method iterates over
+ * the supplied keyPaths and determines the corresponding values by invoking
+ * valueForKeyPath: on the receiver. The results are returned an NSDictionary
+ * with the keyPaths as keys and the returned values as the dictionary's
+ * values. If valueForKeyPath: returns nil, it is replaced by the shared
+ * EONull instance.
+ */
+- (NSDictionary *)valuesForKeyPaths: (NSArray *)keyPaths
+{
+ NSDictionary *values = nil;
+ int i;
+ int n;
+ NSMutableArray *newKeyPaths;
+ NSMutableArray *newVals;
+
+ INITIALIZE;
+
+ EOFLOGObjectFnStartCond(@"EOKVC");
+
+ n = [keyPaths count];
+ newKeyPaths = AUTORELEASE([[NSMutableArray alloc] initWithCapacity: n]);
+ newVals = AUTORELEASE([[NSMutableArray alloc] initWithCapacity: n]);
+
+ for (i = 0; i < n; i++)
+ {
+ id keyPath = [keyPaths objectAtIndex: i];
+ id val = nil;
+
+ NS_DURING //DEBUG Only ?
+ {
+ val = [self valueForKeyPath: keyPath];
+ }
+ NS_HANDLER
+ {
+ NSLog(@"KVC:%@ EXCEPTION %@",
+ NSStringFromSelector(_cmd), localException);
+ NSDebugMLog(@"KVC:%@ EXCEPTION %@",
+ NSStringFromSelector(_cmd), localException);
+ [localException raise];
+ }
+ NS_ENDHANDLER;
+
+ if (val == nil)
+ {
+ val = null;
+ }
+
+ [newKeyPaths addObject: keyPath];
+ [newVals addObject: val];
+ }
+
+ values = [NSDictionary dictionaryWithObjects: newVals
+ forKeys: newKeyPaths];
+
+ EOFLOGObjectFnStopCond(@"EOKVC");
+
+ return values;
+}
+
+/**
+ * This is a GDL2 extension. This convenience method retrieves the object
+ * obtained by invoking valueForKey: on each path component until the one
+ * next to the last. It then invokes takeStoredValue:forKey: on that object
+ * with the last path component as the key.
+ */
+- (void)takeStoredValue: value
+ forKeyPath: (NSString *)key
+{
+ NSArray *pathArray;
+ NSString *path;
+ id obj = self;
+ int i, count;
+
+ EOFLOGObjectFnStartCond(@"EOKVC");
+
+ pathArray = [key componentsSeparatedByString:@"."];
+ count = [pathArray count];
+
+ for (i = 0; i < (count - 1); i++)
+ {
+ path = [pathArray objectAtIndex: i];
+ obj = [obj valueForKey: path];
+ }
+
+ path = [pathArray lastObject];
+ [obj takeStoredValue: value forKey: path];
+
+ EOFLOGObjectFnStopCond(@"EOKVC");
+}
+
+/**
+ * This is a GDL2 extension. This convenience method retrieves the object
+ * obtained by invoking valueForKey: on each path component until the one
+ * next to the last. It then invokes storedValue:forKey: on that object
+ * with the last path component as the key, returning the result.
+ */
+- (id)storedValueForKeyPath: (NSString *)key
+{
+ NSArray *pathArray = nil;
+ NSString *path;
+ id obj = self;
+ int i, count;
+ EOFLOGObjectFnStartCond(@"EOKVC");
+ pathArray = [key componentsSeparatedByString:@"."];
+ count = [pathArray count];
+
+ for(i=0; i < (count-1); i++)
+ {
+ path = [pathArray objectAtIndex:i];
+ obj = [obj valueForKey:path];
+ }
+
+ path = [pathArray lastObject];
+ obj=[obj storedValueForKey:path];
+ EOFLOGObjectFnStopCond(@"EOKVC");
+ return obj;
+}
+
+/**
+ * This is a GDL2 extension. This convenience method iterates over
+ * the supplied keyPaths and determines the corresponding values by invoking
+ * storedValueForKeyPath: on the receiver. The results are returned an
+ * NSDictionary with the keyPaths as keys and the returned values as the
+ * dictionary's values. If storedValueForKeyPath: returns nil, it is replaced
+ * by the shared EONull instance.
+ */
+- (NSDictionary *)storedValuesForKeyPaths: (NSArray *)keyPaths
+{
+ NSDictionary *values = nil;
+ int i, n;
+ NSMutableArray *newKeyPaths = nil;
+ NSMutableArray *newVals = nil;
+
+ INITIALIZE;
+
+ EOFLOGObjectFnStartCond(@"EOKVC");
+
+ n = [keyPaths count];
+
+ newKeyPaths = [[[NSMutableArray alloc] initWithCapacity: n]
+ autorelease];
+ newVals = [[[NSMutableArray alloc] initWithCapacity: n]
+ autorelease];
+
+ for (i = 0; i < n; i++)
+ {
+ id keyPath = [keyPaths objectAtIndex: i];
+ id val = nil;
+
+ NS_DURING //DEBUG Only ?
+ {
+ val = [self storedValueForKeyPath: keyPath];
+ }
+ NS_HANDLER
+ {
+ NSLog(@"EXCEPTION %@", localException);
+ NSDebugMLog(@"EXCEPTION %@", localException);
+ [localException raise];
+ }
+ NS_ENDHANDLER;
+
+ if (val == nil)
+ val = null;
+
+ [newKeyPaths addObject: keyPath];
+ [newVals addObject: val];
+ }
+
+ values = [NSDictionary dictionaryWithObjects: newVals
+ forKeys: newKeyPaths];
+ EOFLOGObjectFnStopCond(@"EOKVC");
+
+ return values;
+}
+
+/**
+ * This is a GDL2 extension. Simply invokes takeValue:forKey:.
+ * This method provides a hook for EOGenericRecords KVC implementation,
+ * which takes relationship definitions into account.
+ */
+- (void)smartTakeValue: (id)anObject
+ forKey: (NSString *)aKey
+{
+ [self takeValue: anObject
+ forKey: aKey];
+}
+
+/**
+ * This is a GDL2 extension. This convenience method invokes
+ * smartTakeValue:forKeyPath on the object returned by valueForKey: with
+ * the first path component.
+ * obtained by invoking valueForKey: on each path component until the one
+ * next to the last. It then invokes storedValue:forKey: on that object
+ * with the last path component as the key, returning the result.
+ */
+- (void)smartTakeValue: (id)anObject
+ forKeyPath: (NSString *)aKeyPath
+{
+ NSRange r = [aKeyPath rangeOfString: @"."];
+
+ if (r.length == 0)
+ {
+ [self smartTakeValue: anObject
+ forKey: aKeyPath];
+ }
+ else
+ {
+ NSString *key = [aKeyPath substringToIndex: r.location];
+ NSString *path = [aKeyPath substringFromIndex: NSMaxRange(r)];
+
+ [[self valueForKey: key] smartTakeValue: anObject
+ forKeyPath: path];
+ }
+}
+
@end
Index: EOControl/EONull.h
===================================================================
RCS file: /cvsroot/gnustep/gnustep/dev-libs/gdl2/EOControl/EONull.h,v
retrieving revision 1.3
diff -u -d -r1.3 EONull.h
--- EOControl/EONull.h 31 Mar 2003 00:24:15 -0000 1.3
+++ EOControl/EONull.h 24 Aug 2003 23:24:13 -0000
@@ -28,20 +28,22 @@
#define __EONull_h__
#ifndef NeXT_Foundation_LIBRARY
-#include <Foundation/NSObject.h>
+#include <Foundation/NSNull.h>
#else
#include <Foundation/Foundation.h>
#endif
-#include <EOControl/EODefines.h>
+#include "EODefines.h"
-@interface EONull : NSObject <NSCopying>
-/* Type is ommited to avoid compiler warnings
- triggerd by NSNull. */
-+ null;
-@end /* EONull */
+@class NSString;
-@interface NSObject (EONull)
+#define EONull NSNull
+
+@interface EONull (EOSQLFormatting)
+- (NSString *)sqlString;
+@end
+
+@interface NSObject (EONullTest)
- (BOOL)isEONull;
- (BOOL)isNotEONull;
@end
@@ -49,3 +51,4 @@
GDL2CONTROL_EXPORT BOOL isNilOrEONull(id v);
#endif /* __EONull_h__ */
+
Index: EOControl/EONull.m
===================================================================
RCS file: /cvsroot/gnustep/gnustep/dev-libs/gdl2/EOControl/EONull.m,v
retrieving revision 1.7
diff -u -d -r1.7 EONull.m
--- EOControl/EONull.m 11 Jul 2003 19:04:05 -0000 1.7
+++ EOControl/EONull.m 24 Aug 2003 23:24:13 -0000
@@ -39,10 +39,7 @@
RCS_ID("$Id: EONull.m,v 1.7 2003/07/11 19:04:05 ayers Exp $")
#ifndef NeXT_Foundation_LIBRARY
-#include <Foundation/NSNull.h>
-#include <Foundation/NSString.h>
-#include <Foundation/NSException.h>
-#include <Foundation/NSDebug.h>
+#include <Foundation/NSString.h>
#else
#include <Foundation/Foundation.h>
#endif
@@ -55,74 +52,7 @@
#include <EOControl/EODebug.h>
-@implementation EONull
-
-static EONull *sharedEONull = nil;
-
-+ (void) initialize
-{
- sharedEONull = (EONull *)[NSNull null];
-}
-
-+ null
-{
- return sharedEONull;
-}
-
-+ (id) allocWithZone:(NSZone *)zone
-{
- return sharedEONull;
-}
-
-- (id) copy
-{
- NSAssert1(NO,@"EONull instance received:%@",NSStringFromSelector(_cmd));
- return sharedEONull;
-}
-
-- (id) copyWithZone: (NSZone *)zone
-{
- NSAssert1(NO,@"EONull instance received:%@",NSStringFromSelector(_cmd));
- return sharedEONull;
-}
-
-- (id) retain
-{
- NSAssert1(NO,@"EONull instance received:%@",NSStringFromSelector(_cmd));
- return sharedEONull;
-}
-
-- (id) autorelease
-{
- NSAssert1(NO,@"EONull instance received:%@",NSStringFromSelector(_cmd));
- return sharedEONull;
-}
-
-- (void) release
-{
- NSAssert1(NO,@"EONull instance received:%@",NSStringFromSelector(_cmd));
-}
-
-- (void) dealloc
-{
- NSAssert1(NO,@"EONull instance received:%@",NSStringFromSelector(_cmd));
-}
-
-- (id)valueForKey: (NSString *)key
-{
- NSAssert1(NO,@"EONull instance received:%@",NSStringFromSelector(_cmd));
- return nil;
-}
-
-- (NSString *) sqlString
-{
- NSAssert1(NO,@"EONull instance received:%@",NSStringFromSelector(_cmd));
- return nil;
-}
-
-@end /* EONull */
-
-@implementation NSNull (EOSQLFormatting)
+@implementation EONull (EOSQLFormatting)
- (NSString *)sqlString
{
@@ -145,7 +75,7 @@
- (BOOL)isEONull
{
- return (((id)self) == sharedEONull || (((id)self) == [NSNull null]));
+ return ((id)self == [NSNull null]);
}
- (BOOL)isNotEONull
@@ -159,3 +89,35 @@
{
return ((!v) || [v isEONull]);
}
+
+/*
+ * We keep this class to support NSClassFromString() which
+ * scripting libraries my depend on. Note that this is
+ * not a fail-safe implementation. You should rely on
+ * [EONull+null] and pointer comparison. Do not rely on
+ * [obj isKindOfClass: NSClassFromString(@"EONull")]
+ * or similar constructs. They will return wrong results.
+ * This is a small backdraw from using the new extension classes
+ * in base / Foundation.
+ */
+#undef EONull
+@interface EONull : NSNull
+@end
+@implementation EONull
++ (Class) class
+{
+ return [NSNull class];
+}
+
++ (id) allocWithZone: (NSZone *)zone
+{
+ return [NSNull null];
+}
+
++ null
+{
+ return [NSNull null];
+}
+
+@end
+
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- Re: [RFA/gdl2] EOKeyValueCoding && EONull,
David Ayers <=