mirror of
https://github.com/gnustep/libs-gsweb.git
synced 2025-02-21 02:41:04 +00:00
fix EOValidatedObjectUserInfoKey value git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gsweb/trunk@37945 72102866-910b-0410-8b05-ffd578937521
903 lines
27 KiB
Objective-C
903 lines
27 KiB
Objective-C
/** GSWAssociation.m - <title>GSWeb: Class GSWAssociation</title>
|
|
|
|
Copyright (C) 1999-2004 Free Software Foundation, Inc.
|
|
|
|
Written by: Manuel Guesdon <mguesdon@orange-concept.com>
|
|
Date: Jan 1999
|
|
|
|
$Revision$
|
|
$Date$
|
|
|
|
This file is part of the GNUstep Web Library.
|
|
|
|
<license>
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
License along with this library; if not, write to the Free
|
|
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
</license>
|
|
**/
|
|
|
|
#include "config.h"
|
|
|
|
#include "GSWeb.h"
|
|
#include "GSWPrivate.h"
|
|
#include "GSWKeyValueAssociation.h"
|
|
#include "GSWConstantValueAssociation.h"
|
|
#include "GSWBindingNameAssociation.h"
|
|
#include <math.h>
|
|
#include <GNUstepBase/NSObject+GNUstepBase.h>
|
|
#include <GNUstepBase/NSString+GNUstepBase.h>
|
|
|
|
#include <limits.h>
|
|
#include <float.h>
|
|
|
|
//#ifdef TCSDB
|
|
//#include <TCSimpleDB/TCSimpleDB.h>
|
|
//#endif
|
|
//#if HAVE_GDL2
|
|
//#include <EOControl/EOKeyValueCoding.h>
|
|
//#endif
|
|
|
|
static NSDictionary* localMinMaxDictionary=nil;
|
|
static NSMutableDictionary* associationsHandlerClasses=nil;
|
|
static NSLock* associationsLock=nil;
|
|
static NSMutableArray* associationsLogsHandlerClasses=nil;
|
|
static Class NSNumberClass = Nil;
|
|
static Class NSStringClass = Nil;
|
|
|
|
//====================================================================
|
|
@implementation GSWAssociation
|
|
|
|
+(void)initialize
|
|
{
|
|
if (self==[GSWAssociation class])
|
|
{
|
|
associationsLock=[NSLock new];
|
|
NSNumberClass = [NSNumber class];
|
|
NSStringClass = [NSString class];
|
|
|
|
if (!localMinMaxDictionary)
|
|
{
|
|
localMinMaxDictionary=[[NSDictionary dictionaryWithObjectsAndKeys:
|
|
[NSNumber numberWithShort:SCHAR_MIN],@"SCHAR_MIN",
|
|
[NSNumber numberWithShort:SCHAR_MAX],@"SCHAR_MAX",
|
|
[NSNumber numberWithShort:UCHAR_MAX],@"UCHAR_MAX",
|
|
[NSNumber numberWithShort:CHAR_MIN],@"CHAR_MIN",
|
|
[NSNumber numberWithShort:CHAR_MAX],@"CHAR_MAX",
|
|
[NSNumber numberWithShort:SHRT_MIN],@"SHRT_MIN",
|
|
[NSNumber numberWithShort:SHRT_MAX],@"SHRT_MAX",
|
|
[NSNumber numberWithUnsignedInt:0],@"USHRT_MIN",
|
|
[NSNumber numberWithUnsignedInt:USHRT_MAX],@"USHRT_MAX",
|
|
[NSNumber numberWithInt:INT_MIN],@"INT_MIN",
|
|
[NSNumber numberWithInt:INT_MAX],@"INT_MAX",
|
|
[NSNumber numberWithUnsignedInt:0],@"UINT_MIN",
|
|
[NSNumber numberWithUnsignedInt:UINT_MAX],@"UINT_MAX",
|
|
[NSNumber numberWithLong:LONG_MIN],@"LONG_MIN",
|
|
[NSNumber numberWithLong:LONG_MAX],@"LONG_MAX",
|
|
[NSNumber numberWithUnsignedLong:0],@"ULONG_MIN",
|
|
[NSNumber numberWithUnsignedLong:ULONG_MAX],@"ULONG_MAX",
|
|
#ifdef LONG_LONG_MAX
|
|
[NSNumber numberWithLongLong:LONG_LONG_MIN],@"LONG_LONG_MIN",
|
|
[NSNumber numberWithLongLong:LONG_LONG_MAX],@"LONG_LONG_MAX",
|
|
#endif
|
|
#ifdef ULONG_LONG_MAX
|
|
[NSNumber numberWithUnsignedLongLong:0],@"ULONG_LONG_MIN",
|
|
[NSNumber numberWithUnsignedLongLong:ULONG_LONG_MAX],@"ULONG_LONG_MAX",
|
|
#endif
|
|
[NSNumber numberWithFloat:FLT_MIN],@"FLOAT_MIN",
|
|
[NSNumber numberWithFloat:FLT_MAX],@"FLOAT_MAX",
|
|
[NSNumber numberWithFloat:DBL_MIN],@"DOUBLE_MIN",
|
|
[NSNumber numberWithFloat:DBL_MAX],@"DOUBLE_MAX",
|
|
nil,nil]
|
|
retain];
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
+(void)dealloc
|
|
{
|
|
DESTROY(localMinMaxDictionary);
|
|
DESTROY(associationsHandlerClasses);
|
|
DESTROY(associationsLogsHandlerClasses);
|
|
DESTROY(associationsLock);
|
|
|
|
[super dealloc];
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
-(void)dealloc
|
|
{
|
|
DESTROY(_bindingName);
|
|
DESTROY(_declarationName);
|
|
DESTROY(_declarationType);
|
|
[super dealloc];
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// YES if we negate the result before returnig it.
|
|
-(BOOL)negate
|
|
{
|
|
return _negate;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
-(void) setNegate:(BOOL) yn
|
|
{
|
|
_negate = yn;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
- (BOOL)_hasBindingInParent:(GSWComponent*) parent
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
-(id)copyWithZone:(NSZone*)zone;
|
|
{
|
|
GSWAssociation* clone = [[isa allocWithZone:zone] init];
|
|
clone->_debugEnabled=_debugEnabled;
|
|
[clone setDebugEnabledForBinding:_bindingName
|
|
declarationName:_declarationName
|
|
declarationType:_declarationType];
|
|
return clone;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
-(NSString*)description
|
|
{
|
|
[self notImplemented: _cmd]; //TODOFN
|
|
return [super description];
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
-(NSString*)bindingName
|
|
{
|
|
return _bindingName;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
-(NSString*)declarationName
|
|
{
|
|
return _declarationName;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
-(NSString*)declarationType
|
|
{
|
|
return _declarationType;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// isValueConstant
|
|
|
|
-(BOOL)isValueConstant
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// isValueSettable
|
|
|
|
- (BOOL)isValueSettable
|
|
{
|
|
return NO;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
- (BOOL) isValueSettableInComponent:(GSWComponent*) comp
|
|
{
|
|
return [self isValueSettable];
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
- (BOOL) isValueConstantInComponent:(GSWComponent*) comp
|
|
{
|
|
return [self isValueConstant];
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
// setValue:inComponent:
|
|
-(void)setValue:(id)value
|
|
inComponent:(GSWComponent*)component
|
|
{
|
|
[self subclassResponsibility:_cmd];
|
|
}
|
|
|
|
- (void) _setValueNoValidation:(id) aValue inComponent:(GSWComponent*) component
|
|
{
|
|
[self setValue:aValue inComponent:component];
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// valueInComponent:
|
|
-(id)valueInComponent:(GSWComponent*)component;
|
|
{
|
|
return [self subclassResponsibility:_cmd];
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// added in WO5?
|
|
// they call it booleanValueInComponent:
|
|
- (BOOL) boolValueInComponent:(GSWComponent*)component
|
|
{
|
|
id value = [self valueInComponent: component];
|
|
|
|
if (value==nil)
|
|
{
|
|
if (_negate)
|
|
return YES;
|
|
else
|
|
return NO;
|
|
}
|
|
if ([value isKindOfClass: NSNumberClass])
|
|
{
|
|
if (_negate)
|
|
return (![value boolValue]);
|
|
else
|
|
return [value boolValue];
|
|
}
|
|
else if ([value isKindOfClass: NSStringClass])
|
|
{
|
|
NSString* tmpStr = nil;
|
|
int length = [value length];
|
|
if (length >= 2 && length <= 5)
|
|
{
|
|
tmpStr = [value lowercaseString];
|
|
if ([tmpStr isEqual:@"no"]
|
|
|| [tmpStr isEqual:@"false"]
|
|
|| [tmpStr isEqual:@"nil"]
|
|
|| [tmpStr isEqual:@"null"])
|
|
{
|
|
if (_negate)
|
|
return YES;
|
|
else
|
|
return NO;
|
|
}
|
|
}
|
|
if ([tmpStr isEqual:@"0"])
|
|
{
|
|
if (_negate)
|
|
return YES;
|
|
else
|
|
return NO;
|
|
}
|
|
if (_negate)
|
|
return NO;
|
|
else
|
|
return YES;
|
|
}
|
|
if (_negate)
|
|
return NO;
|
|
else
|
|
return YES;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
// associationWithKeyPath:
|
|
|
|
+(GSWAssociation*)associationWithKeyPath:(NSString*)keyPath
|
|
{
|
|
GSWAssociation * newAssoc = nil;
|
|
BOOL doNegate = NO;
|
|
NSString * newPath = keyPath;
|
|
|
|
if (newPath) {
|
|
doNegate = [newPath hasPrefix:@"!"];
|
|
if (doNegate) {
|
|
newPath = [newPath stringByDeletingPrefix:@"!"];
|
|
}
|
|
if ([newPath hasPrefix:@"^"] || (!WOStrictFlag && [newPath hasPrefix:@"~"])) {
|
|
newAssoc = [[[GSWBindingNameAssociation alloc] initWithKeyPath: newPath] autorelease];
|
|
} else {
|
|
newAssoc = [[[GSWKeyValueAssociation alloc]initWithKeyPath: newPath] autorelease];
|
|
}
|
|
if (doNegate) {
|
|
[newAssoc setNegate:YES]; // default is NO so we may safe a call here
|
|
}
|
|
return newAssoc;
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
// associationWithValue:
|
|
|
|
+(GSWAssociation*)associationWithValue:(id)value
|
|
{
|
|
//OK
|
|
return [[[GSWConstantValueAssociation alloc]initWithValue:value] autorelease];
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// associationFromString:
|
|
//NDFN
|
|
+(GSWAssociation*)associationFromString:(NSString*)string
|
|
{
|
|
GSWAssociation* assoc=nil;
|
|
|
|
if ([string length]<=0)
|
|
assoc=[self associationWithValue:string];
|
|
else
|
|
{
|
|
NSString* trimmedString=[string stringByTrimmingSpaces];
|
|
if ([trimmedString isEqualToString:NSTYES])
|
|
{
|
|
assoc=[self associationWithValue:GSWNumberYes];
|
|
}
|
|
else if ([trimmedString isEqualToString:NSTNO])
|
|
{
|
|
assoc=[self associationWithValue:GSWNumberNo];
|
|
}
|
|
else if ([trimmedString hasPrefix:@"^"])
|
|
{
|
|
assoc=[self associationWithKeyPath:trimmedString];
|
|
}
|
|
else if ([trimmedString hasPrefix:@"\""])
|
|
{
|
|
if ([trimmedString hasSuffix:@"\""])
|
|
{
|
|
assoc=[self associationWithValue:[[trimmedString stringByDeletingPrefix:@"\""] stringByDeletingSuffix:@"\""]];
|
|
}
|
|
else
|
|
{
|
|
ExceptionRaise(@"GSWAssociation",@"String '%@' start with a \" but doesn't finish with a \"",
|
|
trimmedString);
|
|
}
|
|
}
|
|
else if ([trimmedString hasPrefix:@"\'"])
|
|
{
|
|
if ([trimmedString hasSuffix:@"\'"])
|
|
{
|
|
assoc=[self associationWithValue:[[trimmedString stringByDeletingPrefix:@"\'"] stringByDeletingSuffix:@"\'"]];
|
|
}
|
|
else
|
|
{
|
|
ExceptionRaise(@"GSWAssociation",@"String '%@' starts with a \"'\" but does not end with a \"'\"",
|
|
trimmedString);
|
|
}
|
|
}
|
|
else if ([trimmedString hasPrefix:@"#"])
|
|
{
|
|
NSString* numberString=[trimmedString stringByDeletingPrefix:@"#"];
|
|
//char* cString=[numberString lossyCString];//TODO
|
|
const char* cString=[numberString UTF8String];
|
|
char* endPtr=NULL;
|
|
int value=strtol(cString,&endPtr,16);
|
|
if (endPtr && *endPtr)
|
|
{
|
|
ExceptionRaise(@"GSWAssociation",@"String '%@' start with a '#' but doesn't countain an hexadecimal number (on %dth Character)",
|
|
trimmedString,
|
|
(int)(endPtr-cString+1));
|
|
}
|
|
assoc=[self associationWithValue:GSWIntNumber(value)];
|
|
}
|
|
else
|
|
{
|
|
NSNumber* limit=[localMinMaxDictionary objectForKey:trimmedString];
|
|
if (limit)
|
|
{
|
|
assoc=[self associationWithValue:limit];
|
|
}
|
|
else
|
|
{
|
|
NSCharacterSet* cset=[NSCharacterSet characterSetWithCharactersInString:@"-+0123456789"];
|
|
NSRange firstCharRange=[trimmedString rangeOfCharacterFromSet:cset
|
|
options:0
|
|
range:NSMakeRange(0,1)];
|
|
|
|
if (firstCharRange.length==0 || firstCharRange.location!=0)
|
|
{
|
|
assoc=[self associationWithKeyPath:trimmedString];
|
|
}
|
|
else
|
|
{
|
|
//char* cString=[trimmedString lossyCString];//TODO
|
|
const char* cString=[trimmedString UTF8String];
|
|
char* endPtr=NULL;
|
|
int value=strtol(cString,&endPtr,10);
|
|
|
|
if (endPtr && *endPtr)
|
|
{
|
|
ExceptionRaise(@"GSWAssociation",
|
|
@"String '%@' must be a good number",
|
|
trimmedString);
|
|
}
|
|
assoc=[self associationWithValue:GSWIntNumber(value)];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return assoc;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
+(void)setClasse:(Class)class
|
|
forHandler:(NSString*)handler
|
|
{
|
|
LoggedLockBeforeDate(associationsLock,GSW_LOCK_LIMIT);
|
|
if (!associationsHandlerClasses)
|
|
{
|
|
if (class)
|
|
associationsHandlerClasses=[NSMutableDictionary new];
|
|
}
|
|
if (class)
|
|
[associationsHandlerClasses setObject:class
|
|
forKey:handler];
|
|
else if (associationsHandlerClasses)
|
|
[associationsHandlerClasses removeObjectForKey:handler];
|
|
LoggedUnlock(associationsLock);
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
+(void)addLogHandlerClasse:(Class)class
|
|
{
|
|
LoggedLockBeforeDate(associationsLock,GSW_LOCK_LIMIT);
|
|
if (!associationsLogsHandlerClasses)
|
|
{
|
|
if (class)
|
|
associationsLogsHandlerClasses=[NSMutableArray new];
|
|
}
|
|
if (class)
|
|
[associationsLogsHandlerClasses addObject:class];
|
|
LoggedUnlock(associationsLock);
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
+(void)removeLogHandlerClasse:(Class)class
|
|
{
|
|
LoggedLockBeforeDate(associationsLock,GSW_LOCK_LIMIT);
|
|
if (associationsHandlerClasses)
|
|
{
|
|
if (class)
|
|
[associationsLogsHandlerClasses removeObject:class];
|
|
}
|
|
LoggedUnlock(associationsLock);
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// returns the binding String as in the wod.
|
|
// override in subclasses
|
|
- (NSString*) bindingInComponent:(GSWComponent*) component
|
|
{
|
|
return nil;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
-(BOOL)isImplementedForComponent:(NSObject*)component
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
-(NSString*)keyPath
|
|
{
|
|
[self subclassResponsibility:_cmd];
|
|
return nil;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
-(void)logValue:(id)value
|
|
forSet:(BOOL)set
|
|
{
|
|
if (_debugEnabled)
|
|
{
|
|
if (associationsLogsHandlerClasses)
|
|
{
|
|
LoggedLockBeforeDate(associationsLock,GSW_LOCK_LIMIT);
|
|
NS_DURING
|
|
{
|
|
int i=0;
|
|
Class class=Nil;
|
|
int handlerCount=[associationsLogsHandlerClasses count];
|
|
NSString* debugDescription=[self debugDescription];
|
|
for(i=0;i<handlerCount;i++)
|
|
{
|
|
class=[associationsLogsHandlerClasses objectAtIndex:i];
|
|
if (set)
|
|
[class logSetValueForDeclarationNamed:_declarationName
|
|
type:_declarationType
|
|
bindingNamed:_bindingName
|
|
associationDescription:debugDescription
|
|
value:value];
|
|
else
|
|
[class logTakeValueForDeclarationNamed:_declarationName
|
|
type:_declarationType
|
|
bindingNamed:_bindingName
|
|
associationDescription:debugDescription
|
|
value:value];
|
|
}
|
|
}
|
|
NS_HANDLER
|
|
{
|
|
LoggedUnlock(associationsLock);
|
|
[localException raise];
|
|
}
|
|
NS_ENDHANDLER;
|
|
LoggedUnlock(associationsLock);
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
-(void)_logPullValue:(id)value
|
|
inComponent:(GSWComponent*) component
|
|
{
|
|
[GSWApp logTakeValueForDeclarationNamed:_declarationName
|
|
type:_declarationType
|
|
bindingNamed:_bindingName
|
|
associationDescription:[self debugDescription]
|
|
value:value];
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
-(void)_logPushValue:(id)value
|
|
inComponent:(GSWComponent*) component
|
|
{
|
|
[GSWApp logSetValueForDeclarationNamed:_declarationName
|
|
type:_declarationType
|
|
bindingNamed:_bindingName
|
|
associationDescription:[self debugDescription]
|
|
value:value];
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
-(void)logTakeValue:(id)value
|
|
{
|
|
[self logValue:value
|
|
forSet:NO];
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
-(void)logSetValue:(id)value
|
|
{
|
|
[self logValue:value
|
|
forSet:YES];
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
-(void)logSynchronizeForValue:(id)value
|
|
inComponent:(GSWComponent*)component
|
|
componentToParent:(BOOL)componentToParent
|
|
{
|
|
if (associationsHandlerClasses)
|
|
{
|
|
LoggedLockBeforeDate(associationsLock,GSW_LOCK_LIMIT);
|
|
NS_DURING
|
|
{
|
|
int i=0;
|
|
Class class=Nil;
|
|
int handlerCount=[associationsLogsHandlerClasses count];
|
|
for(i=0;i<handlerCount;i++)
|
|
{
|
|
class=[associationsLogsHandlerClasses objectAtIndex:i];
|
|
if (componentToParent)
|
|
[class logSynchronizeComponentToParentForValue:value
|
|
association:self
|
|
inComponent:component];
|
|
else
|
|
[class logSynchronizeParentToComponentForValue:value
|
|
association:self
|
|
inComponent:component];
|
|
}
|
|
}
|
|
NS_HANDLER
|
|
{
|
|
LoggedUnlock(associationsLock);
|
|
[localException raise];
|
|
}
|
|
NS_ENDHANDLER;
|
|
LoggedUnlock(associationsLock);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
-(void)logSynchronizeComponentToParentForValue:(id)value
|
|
inComponent:(GSWComponent*)component
|
|
{
|
|
[self logSynchronizeForValue:value
|
|
inComponent:component
|
|
componentToParent:YES];
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
-(void)logSynchronizeParentToComponentForValue:(id)value
|
|
inComponent:(GSWComponent*)component
|
|
{
|
|
[self logSynchronizeForValue:value
|
|
inComponent:component
|
|
componentToParent:NO];
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
-(NSString*)debugDescription
|
|
{
|
|
return NSStringFromClass([self class]);
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
-(void)setDebugEnabledForBinding:(NSString*)bindingName
|
|
declarationName:(NSString*)declarationName
|
|
declarationType:(NSString*)declarationType
|
|
{
|
|
_debugEnabled=YES;
|
|
ASSIGN(_bindingName,bindingName);
|
|
ASSIGN(_declarationName,declarationName);
|
|
ASSIGN(_declarationType,declarationType);
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
+(id)valueInComponent:(GSWComponent*)object
|
|
forKeyPath:(NSString*)keyPath
|
|
{
|
|
static id EONullNull=nil;
|
|
//TODO MultiThread Protection ?
|
|
if (!EONullNull)
|
|
EONullNull=[NSNull null];
|
|
id retValue=nil;
|
|
|
|
if (keyPath && object && object!=EONullNull)
|
|
{
|
|
NS_DURING
|
|
{
|
|
retValue=[object valueForKeyPath:keyPath];
|
|
}
|
|
NS_HANDLER
|
|
{
|
|
NSLog(@"Attempt to get %@ -%@ raised an exception (%@)",
|
|
[object class],
|
|
keyPath,
|
|
localException);
|
|
localException = [localException exceptionByAddingToUserInfoKey:@"Invalid Ivars/Methods"
|
|
format:@"-[%@ %@]",
|
|
[object class],
|
|
keyPath];
|
|
|
|
[localException raise];
|
|
}
|
|
NS_ENDHANDLER;
|
|
if (retValue==EONullNull)
|
|
retValue=nil;
|
|
}
|
|
|
|
return retValue;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
+(void)setValue:(id)value
|
|
inComponent:(GSWComponent*)object
|
|
forKeyPath:(NSString*)keyPath
|
|
{
|
|
id tmpObject = nil;
|
|
NSString *tmpKey = nil;
|
|
|
|
if (keyPath)
|
|
{
|
|
NSRange r = [keyPath rangeOfString: @"."];
|
|
|
|
if (r.length == 0)
|
|
{
|
|
tmpObject = object;
|
|
tmpKey = keyPath;
|
|
}
|
|
else
|
|
{
|
|
NSString *key = [keyPath substringToIndex: r.location];
|
|
tmpObject = [object valueForKey: key];
|
|
tmpKey = [keyPath substringFromIndex: NSMaxRange(r)];
|
|
}
|
|
if (tmpObject) //&& [object isKindOfClass:[GSWComponent class]]
|
|
{
|
|
NSError * outError = nil;
|
|
|
|
BOOL ok = [tmpObject validateValue:&value
|
|
forKey:tmpKey
|
|
error:&outError];
|
|
if (ok == NO)
|
|
{
|
|
NSException * exception=nil;
|
|
NSDictionary * uInfo;
|
|
NSString * errorStr = @"unknown reason";
|
|
|
|
uInfo = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
tmpObject, @"EOValidatedObjectUserInfoKey", //Target Object
|
|
keyPath, @"EOValidatedPropertyUserInfoKey", //Target Object Key Path
|
|
nil];
|
|
|
|
if (outError
|
|
&& [outError userInfo])
|
|
{
|
|
errorStr = [[outError userInfo] valueForKey:NSLocalizedDescriptionKey];
|
|
}
|
|
|
|
exception=[NSException exceptionWithName:@"EOValidationException"
|
|
reason:errorStr
|
|
userInfo:uInfo];
|
|
|
|
[object validationFailedWithException:exception
|
|
value:value
|
|
keyPath:keyPath];
|
|
}
|
|
else
|
|
{
|
|
// all fine, set the value
|
|
[tmpObject setValue:value
|
|
forKey:tmpKey];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@end
|
|
|
|
//===================================================================================
|
|
@implementation NSDictionary (GSWAssociation)
|
|
|
|
-(BOOL)isAssociationDebugEnabledInComponent:(GSWComponent*)component
|
|
{
|
|
BOOL debug=NO;
|
|
GSWAssociation* debugAssociation=[self objectForKey:@"GSWDebug"];
|
|
if (debugAssociation)
|
|
{
|
|
id value=[debugAssociation valueInComponent:component];
|
|
debug=boolValueWithDefaultFor(value,NO);
|
|
}
|
|
return debug;
|
|
}
|
|
|
|
-(void)associationsSetDebugEnabled
|
|
{
|
|
NSEnumerator* enumerator=nil;
|
|
id key=nil;
|
|
id association=nil;
|
|
enumerator = [self keyEnumerator];
|
|
while ((key = [enumerator nextObject]))
|
|
{
|
|
association=[self objectForKey:key];
|
|
[association setDebugEnabledForBinding:@""
|
|
declarationName:key
|
|
declarationType:@""]; //TODO
|
|
}
|
|
}
|
|
|
|
-(void)associationsSetValuesFromObject:(id)from
|
|
inObject:(id)to
|
|
{
|
|
NSEnumerator *enumerator = nil;
|
|
id key=nil;
|
|
id varValue=nil;
|
|
id var=nil;
|
|
|
|
enumerator = [self keyEnumerator];
|
|
while ((key = [enumerator nextObject]))
|
|
{
|
|
var=[self objectForKey:key];
|
|
if ([var isKindOfClass:[GSWAssociation class]])
|
|
varValue=[var valueInComponent:from];
|
|
else
|
|
varValue=var;
|
|
|
|
if (![key isKindOfClass:[GSWAssociation class]])
|
|
key=[GSWAssociation associationWithKeyPath:key];
|
|
[key setValue:varValue
|
|
inComponent:to];
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
-(NSDictionary*)associationsWithoutPrefix:(NSString*)prefix
|
|
removeFrom:(NSMutableDictionary*)removeFrom
|
|
{
|
|
NSMutableDictionary* newAssociation=nil;
|
|
NSEnumerator *enumerator = nil;
|
|
id key=nil;
|
|
id varKey=nil;
|
|
id varKeyAssociation=nil;
|
|
id value=nil;
|
|
|
|
newAssociation=(NSMutableDictionary*)[NSMutableDictionary dictionary];
|
|
enumerator = [self keyEnumerator];
|
|
while ((key = [enumerator nextObject]))
|
|
{
|
|
if ([key hasPrefix:prefix])
|
|
{
|
|
value=[self objectForKey:key];
|
|
varKey=[key stringByDeletingPrefix:prefix];
|
|
varKeyAssociation=[GSWAssociation associationWithKeyPath:varKey];
|
|
[newAssociation setObject:value
|
|
forKey:varKeyAssociation];
|
|
[removeFrom removeObjectForKey:key];
|
|
}
|
|
}
|
|
newAssociation=[NSDictionary dictionaryWithDictionary:newAssociation];
|
|
|
|
return newAssociation;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
-(NSDictionary*)dictionaryByReplacingStringsWithAssociations
|
|
{
|
|
NSMutableDictionary* newDictionary=[NSMutableDictionary dictionary];
|
|
NSEnumerator* enumerator=[self keyEnumerator];
|
|
id key=nil;
|
|
id value=nil;
|
|
id newValue=nil;
|
|
while ((key=[enumerator nextObject]))
|
|
{
|
|
value=[self objectForKey:key];
|
|
if ([value isKindOfClass:[NSString class]])
|
|
{
|
|
newValue=[GSWAssociation associationFromString:value];
|
|
NSAssert(newValue,@"Nil value");
|
|
}
|
|
else if ([value isKindOfClass:[NSArray class]])
|
|
{
|
|
newValue=[value arrayByReplacingStringsWithAssociations];
|
|
NSAssert(newValue,@"Nil value");
|
|
}
|
|
else if ([value isKindOfClass:[NSDictionary class]])
|
|
{
|
|
newValue=[value dictionaryByReplacingStringsWithAssociations];
|
|
NSAssert(newValue,@"Nil value");
|
|
}
|
|
else
|
|
newValue=value;
|
|
[newDictionary setObject:newValue
|
|
forKey:key];
|
|
}
|
|
return [NSDictionary dictionaryWithDictionary:newDictionary];
|
|
}
|
|
|
|
@end
|
|
|
|
//===================================================================================
|
|
@implementation NSArray (GSWAssociation)
|
|
-(NSArray*)arrayByReplacingStringsWithAssociations
|
|
{
|
|
NSMutableArray* newArray=[NSMutableArray array];
|
|
int count=[self count];
|
|
int i=0;
|
|
id value=nil;
|
|
id newValue=nil;
|
|
IMP oaiIMP=NULL;
|
|
for(i=0;i<count;i++)
|
|
{
|
|
value=GSWeb_objectAtIndexWithImpPtr(self,&oaiIMP,i);
|
|
if ([value isKindOfClass:[NSString class]])
|
|
{
|
|
newValue=[GSWAssociation associationFromString:value];
|
|
}
|
|
else if ([value isKindOfClass:[NSArray class]])
|
|
{
|
|
newValue=[value arrayByReplacingStringsWithAssociations];
|
|
}
|
|
else if ([value isKindOfClass:[NSDictionary class]])
|
|
{
|
|
newValue=[value dictionaryByReplacingStringsWithAssociations];
|
|
}
|
|
else
|
|
newValue=value;
|
|
[newArray addObject:newValue];
|
|
}
|
|
return [NSArray arrayWithArray:newArray];
|
|
}
|
|
@end
|