/** GSWAssociation.m - GSWeb: Class GSWAssociation Copyright (C) 1999-2004 Free Software Foundation, Inc. Written by: Manuel Guesdon Date: Jan 1999 $Revision$ $Date$ This file is part of the GNUstep Web Library. 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. **/ #include "config.h" RCS_ID("$Id$") #include "GSWeb.h" #include "GSWKeyValueAssociation.h" #include "GSWConstantValueAssociation.h" #include "GSWBindingNameAssociation.h" #include #include #include #include #include //#ifdef TCSDB //#include //#endif //#if HAVE_GDL2 //#include //#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]; } //-------------------------------------------------------------------- // init -(id)init { if ((self=[super init])) { } _negate = NO; return self; } -(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]; int length = 0; NSString * tmpStr = nil; if (! value) { if (_negate) { return YES; } return NO; } if ([value isKindOfClass: NSNumberClass]) { if (_negate) { return (! [value boolValue]); } return [value boolValue]; } if ([value isKindOfClass: NSStringClass]) { 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; } return NO; } } if ([tmpStr isEqual:@"0"]) { if (_negate) { return YES; } return NO; } if (_negate) { return NO; } return YES; } if (_negate) { return NO; } 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); } /* //==================================================================== @implementation GSWAssociation (GSWAssociationOldFn) //-------------------------------------------------------------------- // value -(id)value { GSWContext* context=[[GSWApplication application] context]; [self valueInComponent:GSWContext_component(context)]; } //-------------------------------------------------------------------- // setValue:inComponent: //OldFn -(void)setValue:(id)value { GSWContext* context=[[GSWApplication application] context]; [self setValue:(id)value inComponent:GSWContext_component(context)]; } @end */ //==================================================================== // 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 { //OK [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