/** EOAttribute.m
Returns the name of the class values of this attribute * are represented by. The standard classes are NSNumber, * NSString, NSData and NSDate for the corresponding * [-adaptorValueType]. A model can define more specific * classes like NSDecimalNumber, NSCalendarDate and NSImage * or custom classes which implement a factory method * specified by [-valueFactoryMethodName] to create instances * with the data supplied by the data source.
*If the valueClassName has not been set explicitly and the * reciever [-isFlattened], the valueClassName of the flattened * attribute is returned.
*Otherwise, if the reciever has a prototype then the * valueClassName of the prototype is returned.
*If all that fails, this method returns nil.
*See also:[setValueClassName:]
*/ - (NSString *)valueClassName { if (_valueClassName) return _valueClassName; if ([self isFlattened]) return [[_definitionArray realAttribute] valueClassName]; return [_prototype valueClassName]; } /** *Returns the adaptor specific name of externalType. This is * the name use during SQL generation.
*If the externalType has not been set explicitly and the * reciever [-isFlattened], the valueClassName of the flattened * attribute is returned.
*Otherwise, if the reciever has a prototype then the * externalType of the prototype is returned.
*If all that fails, this method returns nil.
*/ - (NSString *)externalType { if (_externalType) return _externalType; if ([self isFlattened]) return [[_definitionArray realAttribute] externalType]; return [_prototype externalType]; } /** *Returns a one character string identifiying the underlying * C type of an NSNumber [-valueType]. The legal values in GDL2 are:
*If the valueType has not been set explicitly and the * reciever [-isFlattened], the valueClassName of the flattened * attribute is returned.
*Otherwise, if the reciever has a prototype then the * valueType of the prototype is returned.
*If all that fails, this method returns nil.
*/ - (NSString *)valueType { if (_valueType) return _valueType; else if([self isFlattened]) return [[_definitionArray realAttribute] valueType]; else return [_prototype valueType]; } - (void)setParent: (id)parent { //OK [self willChange]; _parent = parent; _flags.isParentAnEOEntity = [_parent isKindOfClass: [EOEntity class]];//?? } /** * Returns YES if the attribute references aProperty, NO otherwise. */ - (BOOL)referencesProperty:(id)aProperty { if (!_definitionArray) { return NO; } else { // _definitionArray is an EOExpressionArray return [_definitionArray referencesObject:aProperty]; } } @end @implementation EOAttribute (EOAttributeSQLExpression) /** * Returns the value to use in an EOSQLExpression. **/ - (NSString *) valueForSQLExpression: (EOSQLExpression *)sqlExpression { NSString *value=nil; if (sqlExpression != nil) { return [sqlExpression sqlStringForAttribute:self]; } if (_definitionArray) value = [_definitionArray valueForSQLExpression: sqlExpression]; else value = [self name]; return value; } @end @implementation EOAttribute (EOAttributeEditing) - (NSException *)validateName:(NSString *)name { NSArray *storedProcedures; const char *p, *s = [name cString]; int exc = 0; if ([_name isEqual:name]) return nil; if (!name || ![name length]) exc++; if (!exc) { p = s; while (*p) { if (!isalnum(*p) && *p != '@' && *p != '#' && *p != '_' && *p != '$') { exc++; break; } p++; } if (!exc && *s == '$') exc++; if (exc) return [NSException exceptionWithName: NSInvalidArgumentException reason: [NSString stringWithFormat:@"%@ -- %@ 0x%x: argument \"%@\" contains invalid char '%c'", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self, name, *p] userInfo: nil]; if ([[self entity] _hasAttributeNamed:name]) exc++; else if ((storedProcedures = [[[self entity] model] storedProcedures])) { NSEnumerator *stEnum = [storedProcedures objectEnumerator]; EOStoredProcedure *st; while ((st = [stEnum nextObject])) { NSEnumerator *attrEnum; EOAttribute *attr; attrEnum = [[st arguments] objectEnumerator]; while ((attr = [attrEnum nextObject])) { if ([name isEqualToString: [attr name]]) { exc++; break; } } if (exc) break; } } } if (exc) { return [NSException exceptionWithName: NSInvalidArgumentException reason: [NSString stringWithFormat: @"%@ -- %@ 0x%x: \"%@\" already used in the model", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self, name] userInfo: nil]; } return nil; } - (void)setName: (NSString *)name { if ([_name isEqual: name]==NO) { NSString *oldName = nil; [[self validateName: name] raise]; oldName = AUTORELEASE(RETAIN(_name)); [self willChange]; ASSIGNCOPY(_name, name); if (_flags.isParentAnEOEntity) { [_parent _setIsEdited]; } } } - (void)setPrototype: (EOAttribute *)prototype { [self willChange]; ASSIGN(_prototype, prototype); } - (void)setColumnName: (NSString *)columnName { //seems OK [self willChange]; ASSIGNCOPY(_columnName, columnName); DESTROY(_definitionArray); [_parent _setIsEdited]; [self _setOverrideForKeyEnum:1]; } - (void)_setDefinitionWithoutFlushingCaches: (NSString *)definition { EOExpressionArray *expressionArray=nil; [self willChange]; expressionArray = [_parent _parseDescription: definition isFormat: NO arguments: NULL]; expressionArray = [self _normalizeDefinition: expressionArray path: nil]; /* //TODO finish l un est code entity primaryKeyAttributes (code) ?? [self _removeFromEntityArray:code selector:setPrimaryKeyAttributes: */ ASSIGN(_definitionArray, expressionArray); } -(id)_normalizeDefinition: (EOExpressionArray*)definition path: (id)path { //TODO /* definition _isPropertyPath //NO count object atindex self _normalizeDefinition:ret path:NSArray() adddobject if attribute if isderived //NO ?? ret attr return nexexp */ return definition; } /** *Sets the definition of a derived attribute.
*An EOAttribute can either reference column from the entites * external representation or define a derived attribute such a * cacluclated value or a key path. The values to these attributes * are cached in memory.
*
To set the definition of an attribute, the attribute must * already be contained by its parent entity.
*Setting the the definition clears the column name.
*/ - (void)setDefinition:(NSString *)definition { if(definition) { [self willChange]; [self _setDefinitionWithoutFlushingCaches: definition]; DESTROY(_columnName); [_parent _setIsEdited]; } } - (void)setReadOnly: (BOOL)yn { if(!yn && ([self isDerived] && ![self isFlattened])) [NSException raise: NSInvalidArgumentException format: @"%@ -- %@ 0x%x: cannot set to NO while the attribute is derived but not flattened.", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self]; [self willChange]; _flags.isReadOnly = yn; } - (void)setExternalType: (NSString *)type { //OK [self willChange]; ASSIGNCOPY(_externalType, type); [_parent _setIsEdited]; [self _setOverrideForKeyEnum: 0];//TODO } - (void)setValueType: (NSString *)type { //OK [self willChange]; ASSIGNCOPY(_valueType, type); if ([_valueType length]==1) _valueTypeCharacter = [_valueType characterAtIndex:0]; else _valueTypeCharacter = '\0'; [self _setOverrideForKeyEnum: 4];//TODO } - (void)setValueClassName: (NSString *)name { [self willChange]; ASSIGNCOPY(_valueClassName, name); _valueClass = NSClassFromString(_valueClassName); _flags.isAttributeValueInitialized = NO; [self _setOverrideForKeyEnum: 3];//TODO } - (void)setWidth: (unsigned)length { [self willChange]; _width = length; } - (void)setPrecision: (unsigned)precision { [self willChange]; _precision = precision; } - (void)setScale: (int)scale { [self willChange]; _scale = scale; } - (void)setAllowsNull: (BOOL)allowsNull { //OK [self willChange]; _flags.allowsNull = allowsNull; [self _setOverrideForKeyEnum: 15];//TODO } - (void)setWriteFormat: (NSString *)string { [self willChange]; ASSIGNCOPY(_writeFormat, string); } - (void)setReadFormat: (NSString *)string { [self willChange]; ASSIGNCOPY(_readFormat, string); } - (void)setParameterDirection: (EOParameterDirection)parameterDirection { [self willChange]; _parameterDirection = parameterDirection; } - (void)setUserInfo: (NSDictionary *)dictionary { //OK [self willChange]; ASSIGN(_userInfo, dictionary); [_parent _setIsEdited]; [self _setOverrideForKeyEnum: 10];//TODO } - (void)setInternalInfo: (NSDictionary *)dictionary { //OK [self willChange]; ASSIGN(_internalInfo, dictionary); [_parent _setIsEdited]; [self _setOverrideForKeyEnum: 10]; //TODO } - (void)setDocComment: (NSString *)docComment { //OK [self willChange]; ASSIGNCOPY(_docComment, docComment); [_parent _setIsEdited]; } @end @implementation EOAttribute (EOBeautifier) /*+ Make the name conform to the Next naming style NAME -> name, FIRST_NAME -> firstName +*/ - (void)beautifyName { NSArray *listItems; NSString *newString=[NSMutableString string]; int anz,i; EOFLOGObjectFnStartOrCond2(@"ModelingClasses", @"EOAttribute"); // Makes the receiver's name conform to a standard convention. Names that conform to this style are all lower-case except for the initial letter of each embedded word other than the first, which is upper case. Thus, "NAME" becomes "name", and "FIRST_NAME" becomes "firstName". if ((_name) && ([_name length]>0)) { listItems = [_name componentsSeparatedByString: @"_"]; newString = [newString stringByAppendingString: [[listItems objectAtIndex: 0] lowercaseString]]; anz = [listItems count]; for(i = 1; i < anz; i++) { newString = [newString stringByAppendingString: [[listItems objectAtIndex: i] capitalizedString]]; } //#warning add all components (attributes, ...) // Exception abfangen NS_DURING { [self setName: newString]; } NS_HANDLER { NSLog(@"%@ in Class: EOAttribute , Method: beautifyName >> error : %@", [localException name], [localException reason]); } NS_ENDHANDLER; } EOFLOGObjectFnStopOrCond2(@"ModelingClasses", @"EOAttribute"); } @end @implementation EOAttribute (EOCalendarDateSupport) - (NSTimeZone *)serverTimeZone { if (_serverTimeZone) return _serverTimeZone; return [_prototype serverTimeZone]; } @end @implementation EOAttribute (EOCalendarDateSupportEditing) - (void)setServerTimeZone: (NSTimeZone *)tz { [self willChange]; ASSIGN(_serverTimeZone, tz); } @end @implementation EOAttribute (EOAttributeValueCreation) /** * Returns an NSData or a custom-class value object * from the supplied set of bytes. * The Adaptor calls this method during value creation * when fetching objects from the database. * For efficiency, the returned value is NOT autoreleased. * * NB: The documentation of the reference implementation * mistakenly claims that it returns an NSString. **/ - (id)newValueForBytes: (const void *)bytes length: (int)length { NSData *value = nil; Class valueClass = [self _valueClass]; if (valueClass != Nil && valueClass != GDL2_NSDataClass) { switch (_argumentType) { case EOFactoryMethodArgumentIsNSData: { //For efficiency reasons, the returned value is NOT autoreleased ! value = [GDL2_alloc(NSData) initWithBytes: bytes length: length]; // If we have a value factiry method, call it to get the final value if(_valueFactoryMethod != NULL) { NSData* tmp = value; // valueFactoryMethod returns an autoreleased value value = [(id)valueClass performSelector: _valueFactoryMethod withObject: value]; if (value != tmp) { RETAIN(value); RELEASE(tmp); }; }; break; } case EOFactoryMethodArgumentIsBytes: { NSMethodSignature *aSignature = nil; NSInvocation *anInvocation = nil; // TODO: verify with WO NSAssert2(_valueFactoryMethod, @"No _valueFactoryMethod (valueFactoryMethodName=%@) in attribute %@", _valueFactoryMethodName,self); // First find signature for method aSignature = [valueClass methodSignatureForSelector: _valueFactoryMethod]; // Create the invocation object anInvocation = [NSInvocation invocationWithMethodSignature: aSignature]; // Put the selector [anInvocation setSelector: _valueFactoryMethod]; // The target is the custom value class [anInvocation setTarget: valueClass]; // arguments are buffer pointer and length [anInvocation setArgument: &bytes atIndex: 2]; [anInvocation setArgument: &length atIndex: 3]; // Let's invoke the method [anInvocation invoke]; // Get the returned value [anInvocation getReturnValue: &value]; //For efficiency reasons, the returned value is NOT autoreleased ! // valueFactoryMethod returns an autoreleased value RETAIN(value); break; } case EOFactoryMethodArgumentIsNSString: // TODO: verify with WO break; } } if(!value) { //For efficiency reasons, the returned value is NOT autoreleased ! value = [GDL2_alloc(NSData) initWithBytes: bytes length: length]; } return value; } /** * Returns a NSString or a custom-class value object * from the supplied set of bytes using encoding. * The Adaptor calls this method during value creation * when fetching objects from the database. * For efficiency, the returned value is NOT autoreleased. **/ - (id)newValueForBytes: (const void *)bytes length: (int)length encoding: (NSStringEncoding)encoding { NSString* value = nil; Class valueClass = [self _valueClass]; if (valueClass != Nil && valueClass != GDL2_NSStringClass) { switch (_argumentType) { case EOFactoryMethodArgumentIsNSString: { NSString *string = nil; string = [(GDL2_alloc(NSString)) initWithBytes: bytes length: length encoding: encoding]; // If we have a value factiry method, call it to get the final value if(_valueFactoryMethod != NULL) { value = [((id)valueClass) performSelector: _valueFactoryMethod withObject: string]; if ( value != string) { //For efficiency reasons, the returned value is NOT autoreleased ! RETAIN(value); RELEASE(string); }; } else { //For efficiency reasons, the returned value is NOT autoreleased ! value = string; }; return value; //break; } case EOFactoryMethodArgumentIsBytes: { NSMethodSignature *aSignature = nil; NSInvocation *anInvocation = nil; // TODO: verify with WO NSAssert2(_valueFactoryMethod, @"No _valueFactoryMethod (valueFactoryMethodName=%@) in attribute %@", _valueFactoryMethodName,self); // First find signature for method aSignature = [valueClass methodSignatureForSelector: _valueFactoryMethod]; // Create the invocation object anInvocation = [NSInvocation invocationWithMethodSignature: aSignature]; // Put the selector [anInvocation setSelector: _valueFactoryMethod]; // The target is the custom value class [anInvocation setTarget: valueClass]; // arguments are buffer pointer, length and encoding [anInvocation setArgument: &bytes atIndex: 2]; [anInvocation setArgument: &length atIndex: 3]; [anInvocation setArgument: &encoding atIndex: 4]; // Let's invoke the method [anInvocation invoke]; // Get the returned value [anInvocation getReturnValue: &value]; //For efficiency reasons, the returned value is NOT autoreleased ! // valueFactoryMethod returns an autoreleased value RETAIN(value); return value; //break; } case EOFactoryMethodArgumentIsNSData: // TODO: verify with WO break; } } if(!value) { //For efficiency reasons, the returned value is NOT autoreleased ! value = [(GDL2_alloc(NSString)) initWithBytes: bytes length: length encoding: encoding]; } return value; } /** * Returns an NSCalendarDate object * from the supplied time information. * The Adaptor calls this method during value creation * when fetching objects from the database. * For efficiency, the returned value is NOT autoreleased. * Milliseconds are dropped since they cannot be easily be stored in * NSCalendarDate. **/ - (NSCalendarDate *)newDateForYear: (int)year month: (unsigned)month day: (unsigned)day hour: (unsigned)hour minute: (unsigned)minute second: (unsigned)second millisecond: (unsigned)millisecond timezone: (NSTimeZone *)timezone zone: (NSZone *)zone { NSCalendarDate *date = nil; // FIXME: extend initializer to include Milliseconds //For efficiency reasons, the returned value is NOT autoreleased ! date = [(GDL2_allocWithZone(NSCalendarDate,zone)) initWithYear: year month: month day: day hour: hour minute: minute second: second timeZone: timezone]; return date; } /** *Returns the name of the method to use for creating a custom class * value for this attribute.
* See Also: [-valueFactoryMethod], [-newValueForBytes:length:] */ - (NSString *)valueFactoryMethodName { return _valueFactoryMethodName; } /** *Returns the selector of the method to use for creating a custom class * value for this attribute.
*Default implementation returns selector for name returned by * [-valueFactoryMethodName] or NULL if no selector is found.
* * See Also: [-valueFactoryMethodName], [-newValueForBytes:length:] */ - (SEL)valueFactoryMethod { return _valueFactoryMethod; } /** * Depending on [-adaptorValueType] this method checks whether the value * is a NSNumber, NSString, NSData or NSDate instance respectively. * If not, it attempts to retrieve the -adaptorValueConversionMethod * which should be used to convert the value accordingly. If none * has been specified and the -adaptorValueType is EOAdaptorBytesType, * it tries to convert the value by invoking -archiveData. * The EONull instance is not converted. * Returns the converted value. * Note: This implementation currently raises if -adaptorValueType is of * an unknown type or if conversion is necessary but not possible. This * maybe contrary to the reference implementation but it seems like useful * behavior. If this is causing problems please submit a bug report. */ - (id)adaptorValueByConvertingAttributeValue: (id)value { EOAdaptorValueType adaptorValueType = [self adaptorValueType]; // No conversion for an EONull value if (value != GDL2_EONull) { BOOL convert = NO; // Find if we need a conversion switch (adaptorValueType) { case EOAdaptorNumberType: convert = [value isKindOfClass: GDL2_NSNumberClass] ? NO : YES; break; case EOAdaptorCharactersType: convert = [value isKindOfClass: GDL2_NSStringClass] ? NO : YES; break; case EOAdaptorBytesType: convert = [value isKindOfClass: GDL2_NSDataClass] ? NO : YES; break; case EOAdaptorDateType: convert = [value isKindOfClass: GDL2_NSDateClass] ? NO : YES; break; default: [NSException raise: NSInvalidArgumentException format: @"Illegal adaptorValueType: %d", adaptorValueType]; } // Do value need conversion ? if (convert) { SEL sel; sel = [self adaptorValueConversionMethod]; if (sel == 0) { if (adaptorValueType == EOAdaptorBytesType) { value = [value archiveData]; } else { /* This exception might not be conformant, but seems helpful. */ [NSException raise: NSInvalidArgumentException format: @"Value of class: %@ needs conversion " @"yet no conversion method specified. " @"Attribute is %@. adaptorValueType=%d", NSStringFromClass([value class]), self,adaptorValueType]; } } else { value = [value performSelector: sel]; } } }; return value; } /** *Returns method name to use to convert value of a class * different than attribute adaptor value type.
* * See also: [-adaptorValueByConvertingAttributeValue:], * [-adaptorValueConversionMethod] */ - (NSString *)adaptorValueConversionMethodName { return _adaptorValueConversionMethodName; } /** *Returns selector of the method to use to convert value of a class * different than attribute adaptor value type.
*The default implementation returns the selector corresponding to * [-adaptorValueConversionMethodName] or NULL if there's not selector * for the method.
* * See also: [-adaptorValueByConvertingAttributeValue:], * [-adaptorValueConversionMethodName] */ - (SEL)adaptorValueConversionMethod { return _adaptorValueConversionMethod; } /** *Returns an EOAdaptorValueType describing the adaptor * (i.e. database) type of data for this attribute.
* *Returned value can be:
*Set method name to use to convert value of a class * different than attribute adaptor value type.
* * See also: [-adaptorValueByConvertingAttributeValue:], * [-adaptorValueConversionMethod], [-adaptorValueConversionMethodName] */ - (void)setAdaptorValueConversionMethodName: (NSString *)conversionMethodName { [self willChange]; ASSIGNCOPY(_adaptorValueConversionMethodName, conversionMethodName); _adaptorValueConversionMethod = NSSelectorFromString(_adaptorValueConversionMethodName); } /** Set the type of argument needed by the factoryMethod. Type can be: EOFactoryMethodArgumentIsNSData method need one parameter: a NSData EOFactoryMethodArgumentIsNSString method need one parameter: a NSString EOFactoryMethodArgumentIsBytes method need 2 parameters (for data type valueClass): a raw bytes buffer and its length or 3 parameters (for string type valueClass): a raw bytes buffer, its length and the encoding See also: -setValueFactoryMethodName:, -factoryMethodArgumentType **/ - (void)setFactoryMethodArgumentType: (EOFactoryMethodArgumentType)argumentType { [self willChange]; _argumentType = argumentType; } @end @implementation EOAttribute (EOAttributeValueMapping) /** Validates value pointed by valueP, may set changed validated value in valueP and return an validation exception if constraints validation fails. valueP must not be NULL. More details: 1. raise an exception if [self allowsNull] == NO but *valueP is nil or EONull except if attribute is a primaryKey attribute (reason of this process exception is currently unknown). 2. if valueClassName isn't set, return nil and leave *valueP unchanged 3. if it can't find the class by name, log message, return nil and leave *valueP unchanged 4. do the fancy type conversions as necessary (Pretty much the current handling we have) 5. THEN if width is not 0 call adaptorValueByConvertingAttributeValue: on the new value and the if returned value is NSString or NSData validate length with width and return a corresponding exception if it's longer than allowed. **/ - (NSException *)validateValue: (id*)valueP { NSException *exception=nil; NSAssert(valueP, @"No value pointer"); NSDebugMLog(@"In EOAttribute validateValue: value (class=%@) = %@ attribute = %@", [*valueP class],*valueP,self); // First check if value is nil or EONull if (_isNilOrEONull(*valueP)) { // Check if this is not allowed if ([self allowsNull] == NO) { NSArray *pkAttributes = [[self entity] primaryKeyAttributes]; // "Primary key attributes are ignored when enforcing allowsNull // property for attributes. The values could be handled later // by automatic PK-generation later if ([pkAttributes indexOfObjectIdenticalTo: self] == NSNotFound) { exception = [NSException validationExceptionWithFormat: @"attribute '%@' of entity '%@' cannot be nil or EONull ", [self name],[[self entity] name]]; }; } } else // There's a value. { NSString* valueClassName=[self valueClassName]; // if there's no valueClassName, leave the value unchanged // and don't return an exception if (valueClassName) { Class valueClass=[self _valueClass]; // There's a className but no class ! if (!valueClass) { //Log this problem, leave the value unchanged // and don't return an exception NSLog(@"No valueClass for valueClassName '%@' in attribute %@", valueClassName,self); } else { unsigned int width = 0; IMP isKindOfClassIMP=[*valueP methodForSelector:@selector(isKindOfClass:)]; // If the value has not the good class we'll try to convert it if ((*isKindOfClassIMP)(*valueP,@selector(isKindOfClass:), valueClass) == NO) { // Is it a string ? if ((*isKindOfClassIMP)(*valueP,@selector(isKindOfClass:), GDL2_NSStringClass)) { if (valueClass == GDL2_NSNumberClass) { unichar valueTypeCharacter = [self _valueTypeCharacter]; switch(valueTypeCharacter) { case 'i': *valueP = [GDL2_alloc(NSNumber) initWithInt: [*valueP intValue]]; AUTORELEASE(*valueP); break; case 'I': *valueP = [GDL2_alloc(NSNumber) initWithUnsignedInt: [*valueP unsignedIntValue]]; AUTORELEASE(*valueP); break; case 'c': *valueP = [GDL2_alloc(NSNumber) initWithChar: [*valueP intValue]]; AUTORELEASE(*valueP); break; case 'C': *valueP = [GDL2_alloc(NSNumber) initWithUnsignedChar: [*valueP unsignedIntValue]]; AUTORELEASE(*valueP); break; case 's': *valueP = [GDL2_alloc(NSNumber) initWithShort: [*valueP shortValue]]; AUTORELEASE(*valueP); break; case 'S': *valueP = [GDL2_alloc(NSNumber) initWithUnsignedShort: [*valueP unsignedShortValue]]; AUTORELEASE(*valueP); break; case 'l': *valueP = [GDL2_alloc(NSNumber) initWithLong: [*valueP longValue]]; AUTORELEASE(*valueP); break; case 'L': *valueP = [GDL2_alloc(NSNumber) initWithUnsignedLong: [*valueP unsignedLongValue]]; AUTORELEASE(*valueP); break; case 'u': *valueP = [GDL2_alloc(NSNumber) initWithLongLong: [*valueP longLongValue]]; AUTORELEASE(*valueP); break; case 'U': *valueP = [GDL2_alloc(NSNumber) initWithUnsignedLongLong: [*valueP unsignedLongLongValue]]; AUTORELEASE(*valueP); break; case 'f': *valueP = [GDL2_alloc(NSNumber) initWithFloat: [*valueP floatValue]]; AUTORELEASE(*valueP); break; default: *valueP = [GDL2_alloc(NSNumber) initWithDouble: [*valueP doubleValue]]; AUTORELEASE(*valueP); break; }; } else if (valueClass == GDL2_NSDecimalNumberClass) { *valueP = [GDL2_alloc(NSDecimalNumber) initWithString: *valueP]; AUTORELEASE(*valueP); } else if (valueClass == GDL2_NSDataClass) { //TODO Verify here. *valueP = [*valueP dataUsingEncoding: [NSString defaultCStringEncoding]]; } else if (valueClass == GDL2_NSCalendarDateClass) { *valueP = AUTORELEASE([(GDL2_alloc(NSCalendarDate)) initWithString: *valueP]); } } }; // Now, test width if any width = [self width]; if (width>0) { // First convert value to adaptor value id testValue = [self adaptorValueByConvertingAttributeValue: *valueP]; if (testValue) { IMP testIsKindOfClassIMP=[testValue methodForSelector:@selector(isKindOfClass:)]; // We can test NSString and NSData type only if ((*testIsKindOfClassIMP)(testValue,@selector(isKindOfClass:), GDL2_NSStringClass) || (*testIsKindOfClassIMP)(testValue,@selector(isKindOfClass:), GDL2_NSDataClass)) { unsigned int testValueLength = [testValue length]; if (testValueLength > width) { exception = [NSException validationExceptionWithFormat: @"Value %@ for attribute '%@' is too large", testValue,[self name]]; }; }; }; }; } } } return exception; } @end @implementation NSObject (EOCustomClassArchiving) + objectWithArchiveData: (NSData *)data { return [NSUnarchiver unarchiveObjectWithData:data]; } - (NSData *)archiveData { return [NSArchiver archivedDataWithRootObject:self]; } @end @implementation EOAttribute (EOAttributePrivate) - (EOAttribute *)realAttribute { return _realAttribute; } - (EOExpressionArray *)_definitionArray { return _definitionArray; } - (Class)_valueClass { if (_valueClass) return _valueClass; else if ([self isFlattened]) return [[_definitionArray realAttribute] _valueClass]; else return [_prototype _valueClass]; } /* * This method returns the valueType as a unichar character. * The value of the instance variable get set implicitly * if the valueType is set explicitly with a legal value. * Otherwise the effective valueType of reciever is used. * TODO: Once this has been set later implicit changes to the * valueType via flattend attrubutes or prototypes will not * be honored. Value validation can be a hot spot so this method * (or rather it's only use in validateValue:) should remain efficient. */ - (unichar)_valueTypeCharacter { unichar valueTypeCharacter = _valueTypeCharacter; if (valueTypeCharacter == '\0') { NSString* valueType = [self valueType]; if ([valueType length] == 1) valueTypeCharacter = [valueType characterAtIndex:0]; } return valueTypeCharacter; }; @end @implementation EOAttribute (EOAttributePrivate2) - (BOOL) _hasAnyOverrides { [self notImplemented: _cmd]; //TODO return NO; } - (void) _resetPrototype { [self notImplemented: _cmd]; //TODO } - (void) _updateFromPrototype { [self notImplemented: _cmd]; //TODO } - (void) _setOverrideForKeyEnum: (int)keyEnum { //[self notImplemented:_cmd]; //TODO } - (BOOL) _isKeyEnumOverriden: (int)param0 { [self notImplemented: _cmd]; //TODO return NO; } - (BOOL) _isKeyEnumDefinedByPrototype: (int)param0 { [self notImplemented: _cmd]; //TODO return NO; } @end