/** EOAttribute.m
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; else if ([self isFlattened]) return [[_definitionArray realAttribute] valueClassName]; else return nil; } /** *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; else if ([self isFlattened]) return [[_definitionArray realAttribute] externalType]; else return nil; } /** *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 nil; } - (void)setParent: (id)parent { [self willChange]; _parent = parent; _flags.isParentAnEOEntity = [_parent isKindOfClass: GDL2_EOEntityClass];//?? } - (void)setEntity:(EOEntity*)entity { if(_parent !=entity) { if(_parent != nil && self == [_parent attributeNamed:[self name]]) [_parent removeAttribute:self]; [self setParent:entity]; } } /** * Returns YES if the attribute references aProperty, NO otherwise. */ - (BOOL)referencesProperty:(id)aProperty { if (_definitionArray==nil) return NO; else return [_definitionArray referencesObject:aProperty]; } - (NSString*)relationshipPath { if([self isFlattened]) { NSMutableString* s=[NSMutableString string]; int count = [_definitionArray count] - 1; int i=0; for(i = 0; i < count; i++) { if (i>0) [s appendString:@"."]; [s appendString:[[_definitionArray objectAtIndex:i] name]]; } return [NSString stringWithString:s]; } else return nil; } - (EOAttribute*)targetAttribute { if([self isFlattened]) return [_definitionArray lastObject]; else return nil; } @end @implementation EOAttribute (EOAttributeSQLExpression) /** * Returns the value to use in an EOSQLExpression. **/ - (NSString *) valueForSQLExpression: (EOSQLExpression *)sqlExpression { NSString *value=nil; if (sqlExpression != nil) value=[sqlExpression sqlStringForAttribute:self]; else 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%p: 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%p: \"%@\" 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) { [[self validateName: name] raise]; AUTORELEASE(RETAIN(_name)); [self willChange]; ASSIGNCOPY(_name, name); if (_flags.isParentAnEOEntity) [_parent _setIsEdited]; } } - (void)setPrototype: (EOAttribute *)prototype { if(_prototype != prototype && ![_prototypeName isEqualToString:[prototype name]]) { [self willChange]; _flags.protoOverride = 0; ASSIGN(_prototypeName, [prototype name]); if (_prototypeName != nil) { ASSIGN(_prototype,[[self _parentModel]prototypeAttributeNamed:_prototypeName]); if(_prototype == nil) ASSIGN(_prototype,prototype); [self _updateFromPrototype]; } else { DESTROY(_prototype); }; }; } - (void)setColumnName: (NSString *)columnName { if (columnName!=nil || _columnName!=nil) { [self willChange]; ASSIGNCOPY(_columnName, columnName); DESTROY(_definitionArray); [_parent _setIsEdited]; [self _setOverrideForKeyEnum: EOAttributeProtoOverrideBits_columnName]; } } -(id)_normalizeDefinition: (id)definition path: (NSArray*)path { id result=nil; if ([definition isKindOfClass:[NSString class]]) result=definition; else if ([definition isKindOfClass:GDL2_EOAttributeClass]) { EOAttribute* attribute = (EOAttribute*)definition; if (attribute == self) result=nil; else { if ([attribute isDerived]) { result = [self _normalizeDefinition:[attribute _definitionArray] path:path]; } else if ([path count] == 0) { result = attribute; } else { EOExpressionArray* exprArray = [EOExpressionArray expressionArray]; [exprArray setInfix:@"."]; if (path!=nil) [exprArray addObjectsFromArray:path]; [exprArray addObject:attribute]; result = exprArray; } } } else if ([(EOExpressionArray*)definition _isPropertyPath]) { int count = [(EOExpressionArray*)definition count]; int i=0; EOExpressionArray* exprArray = [EOExpressionArray expressionArray]; [exprArray setInfix:@"."]; if (path!=nil) [exprArray addObjectsFromArray:path]; for(i=0; i < count-1; i++) [exprArray addObject:[(EOExpressionArray*)definition objectAtIndex:i]]; EOAttribute* attribute = [(EOExpressionArray*)definition lastObject]; if ([attribute isDerived]) { result=[self _normalizeDefinition:[attribute _definitionArray] path:exprArray]; } else { [exprArray addObject:attribute]; result = exprArray; } } else { int count = [(EOExpressionArray*)definition count]; int i=0; EOExpressionArray* exprArray = [EOExpressionArray expressionArray]; for(i = 0; i < count; i++) { id aDef = [self _normalizeDefinition:[(EOExpressionArray*)definition objectAtIndex:i] path:path]; if (aDef == nil) { result=nil; break; } else if ([aDef isKindOfClass:[EOExpressionArray class]] && ![(EOExpressionArray*)aDef _isPropertyPath]) { int aDefCount = [(EOExpressionArray*)aDef count]; int j=0; for(j=0;jAn 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!=nil || _definitionArray!=nil) { [self willChange]; [self _setDefinitionWithoutFlushingCaches: definition]; [_parent _setIsEdited]; } } - (void)setReadOnly: (BOOL)yn { if (yn!=_flags.isReadOnly) { if(!yn && ([self isDerived] && ![self isFlattened])) [NSException raise: NSInvalidArgumentException format: @"%@ -- %@ 0x%p: cannot set to NO while the attribute is derived but not flattened.", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self]; [self willChange]; _flags.isReadOnly = yn; [self _setOverrideForKeyEnum: EOAttributeProtoOverrideBits_readOnly]; _flags.isNonUpdateableInitialized = NO; } } - (void)setExternalType: (NSString *)type { if (_externalType==nil || ![_externalType isEqualToString:type]) { [self willChange]; ASSIGNCOPY(_externalType, type); [_parent _setIsEdited]; [self _setOverrideForKeyEnum: EOAttributeProtoOverrideBits_externalType]; } } - (void)setValueType: (NSString *)type { if (_valueType==nil || ![_valueType isEqualToString:type]) { [self willChange]; ASSIGNCOPY(_valueType, type); if ([_valueType length]==1) _valueTypeCharacter = [_valueType characterAtIndex:0]; else _valueTypeCharacter = '\0'; [self _setOverrideForKeyEnum: EOAttributeProtoOverrideBits_valueType]; } } - (void)setValueClassName: (NSString *)name { [self willChange]; ASSIGNCOPY(_valueClassName, name); _valueClass = NSClassFromString(_valueClassName); _flags.isAttributeValueInitialized = NO; [self _setOverrideForKeyEnum: EOAttributeProtoOverrideBits_valueClassName]; } - (void)setWidth: (unsigned)length { [self willChange]; _width = length; [self _setOverrideForKeyEnum: EOAttributeProtoOverrideBits_width]; } - (void)setPrecision: (unsigned)precision { [self willChange]; _precision = precision; [self _setOverrideForKeyEnum: EOAttributeProtoOverrideBits_precision]; } - (void)setScale: (int)scale { [self willChange]; _scale = scale; [self _setOverrideForKeyEnum: EOAttributeProtoOverrideBits_scale]; } - (void)setAllowsNull: (BOOL)allowsNull { if (allowsNull!=_flags.allowsNull) { [self willChange]; _flags.allowsNull = allowsNull; [self _setOverrideForKeyEnum: EOAttributeProtoOverrideBits_allowsNull]; } } - (void)setWriteFormat: (NSString *)string { [self willChange]; ASSIGNCOPY(_writeFormat, string); [self _setOverrideForKeyEnum: EOAttributeProtoOverrideBits_writeFormat]; } - (void)setReadFormat: (NSString *)string { [self willChange]; ASSIGNCOPY(_readFormat, string); [self _setOverrideForKeyEnum: EOAttributeProtoOverrideBits_readFormat]; } - (void)setParameterDirection: (EOParameterDirection)parameterDirection { [self willChange]; _parameterDirection = parameterDirection; [self _setOverrideForKeyEnum: EOAttributeProtoOverrideBits_parameterDirection]; } - (void)setUserInfo: (NSDictionary *)dictionary { //OK [self willChange]; ASSIGN(_userInfo, dictionary); [_parent _setIsEdited]; [self _setOverrideForKeyEnum: EOAttributeProtoOverrideBits_userInfo]; } - (void)setInternalInfo: (NSDictionary *)dictionary { //OK [self willChange]; ASSIGN(_internalInfo, dictionary); [_parent _setIsEdited]; [self _setOverrideForKeyEnum: EOAttributeProtoOverrideBits_internalInfo]; } - (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); [self _setOverrideForKeyEnum: EOAttributeProtoOverrideBits_serverTimeZone]; } @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 factory 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 factory 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); [self _setOverrideForKeyEnum: EOAttributeProtoOverrideBits_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; [self _setOverrideForKeyEnum: EOAttributeProtoOverrideBits_factoryMethodArgumentType]; } @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; }; - (void)_setDefinitionWithoutFlushingCaches: (NSString *)definition { if (_parent != nil) { [self willChange]; DESTROY(_columnName); if (definition==nil) { DESTROY(_definitionArray); } else { EOExpressionArray* exprArray = [_parent _parseDescription: definition isFormat: NO arguments: NULL]; if (exprArray!=nil) { if (![exprArray isKindOfClass:[EOExpressionArray class]]) exprArray=[EOExpressionArray arrayWithObject:exprArray]; exprArray = [self _normalizeDefinition: exprArray path: nil]; } ASSIGN(_definitionArray,exprArray); [self _removeFromEntityArray:[_parent primaryKeyAttributes] selector:@selector(setPrimaryKeyAttributes:)]; } } } - (EOModel*)_parentModel { return [_parent model]; } - (void)_removeFromEntityArray:(NSArray*)entityArray selector:(SEL)setSelector { if ([entityArray indexOfObject:self]!=NSNotFound) { NSMutableArray* a = AUTORELEASE([entityArray mutableCopy]); [a removeObjectIdenticalTo:self]; [[self entity] performSelector:setSelector withObject:a]; } } -(EOExpressionArray*)_objectForPList:(NSDictionary*)pList { EOExpressionArray* result=nil; if ([pList isKindOfClass:[NSString class]]) result=(EOExpressionArray*)pList; else if(![pList isKindOfClass:[NSDictionary class]]) result=nil; else { NSDictionary* pListDict = (NSDictionary*)pList; NSString* tmpString=nil; tmpString=[pListDict objectForKey:@"name"]; if (tmpString!=nil) result=[[self entity] _parsePropertyName:tmpString]; else { tmpString=[pListDict objectForKey:@"path"]; if (tmpString!=nil) result=[[self entity]_parsePropertyName:tmpString]; else { NSArray* array=[pListDict objectForKey:@"array"]; if (array==nil) result=nil; else { int count = [array count]; EOExpressionArray* exprArray = [EOExpressionArray expressionArray]; tmpString=[pListDict objectForKey:@"prefix"]; if (tmpString!=nil) [exprArray setPrefix:tmpString]; tmpString=[pListDict objectForKey:@"infix"]; if (tmpString!=nil) [exprArray setInfix:tmpString]; tmpString=[pListDict objectForKey:@"suffix"]; if (tmpString!=nil) [exprArray setSuffix:tmpString]; if (count>0) { int i=0; for(i=0;i