mirror of
https://github.com/gnustep/libs-gdl2.git
synced 2025-02-14 23:51:40 +00:00
get rid of RCSID/RCS_ID as shortly discussed with David and Manuel git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gdl2/trunk@37928 72102866-910b-0410-8b05-ffd578937521
2302 lines
65 KiB
Objective-C
2302 lines
65 KiB
Objective-C
/**
|
|
EOAttribute.m <title>EOAttribute Class</title>
|
|
|
|
Copyright (C) 2000-2002,2003,2004,2005 Free Software Foundation, Inc.
|
|
|
|
Author: Mirko Viviani <mirko.viviani@gmail.com>
|
|
Date: February 2000
|
|
|
|
Author: Manuel Guesdon <mguesdon@orange-concept.com>
|
|
Date: October 2000
|
|
|
|
$Revision$
|
|
$Date$
|
|
|
|
<abstract></abstract>
|
|
|
|
This file is part of the GNUstep Database 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 3 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; see the file COPYING.LIB.
|
|
If not, write to the Free Software Foundation,
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
</license>
|
|
**/
|
|
|
|
#include "config.h"
|
|
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
|
|
#ifdef GNUSTEP
|
|
#include <Foundation/NSArchiver.h>
|
|
#include <Foundation/NSCalendarDate.h>
|
|
#include <Foundation/NSData.h>
|
|
#include <Foundation/NSDecimalNumber.h>
|
|
#include <Foundation/NSDictionary.h>
|
|
#include <Foundation/NSEnumerator.h>
|
|
#include <Foundation/NSException.h>
|
|
#include <Foundation/NSInvocation.h>
|
|
#include <Foundation/NSObjCRuntime.h>
|
|
#include <Foundation/NSTimeZone.h>
|
|
#include <Foundation/NSValue.h>
|
|
#else
|
|
#include <Foundation/Foundation.h>
|
|
#include <GNUstepBase/GNUstep.h>
|
|
#include <GNUstepBase/GSObjCRuntime.h>
|
|
#include <GNUstepBase/NSDebug+GNUstepBase.h>
|
|
#include <GNUstepBase/NSObject+GNUstepBase.h>
|
|
#endif
|
|
|
|
#include <EOControl/EONull.h>
|
|
#include <EOControl/EOObserver.h>
|
|
#include <EOControl/EODebug.h>
|
|
#include <EOControl/EONSAddOns.h>
|
|
|
|
#include <EOAccess/EOModel.h>
|
|
#include <EOAccess/EOEntity.h>
|
|
#include <EOAccess/EOAttribute.h>
|
|
#include <EOAccess/EOStoredProcedure.h>
|
|
#include <EOAccess/EORelationship.h>
|
|
#include <EOAccess/EOExpressionArray.h>
|
|
#include <EOAccess/EOSQLExpression.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "EOPrivate.h"
|
|
#include "EOEntityPriv.h"
|
|
#include "EOAttributePriv.h"
|
|
|
|
static NSArray* staticPrototypeKeys=nil;
|
|
|
|
@implementation EOAttribute
|
|
|
|
+ (void)initialize
|
|
{
|
|
static BOOL initialized=NO;
|
|
if (!initialized)
|
|
{
|
|
initialized=YES;
|
|
//Order is important (Cf overide ProtoOverrideBits)
|
|
ASSIGN(staticPrototypeKeys,
|
|
([NSArray arrayWithObjects:
|
|
@"externalType", @"columnName", @"readOnly",
|
|
@"valueClassName", @"valueType", @"width",
|
|
@"precision", @"scale", @"writeFormat",
|
|
@"readFormat",@"userInfo", @"serverTimeZone",
|
|
@"valueFactoryMethodName",
|
|
@"adaptorValueConversionMethodName",
|
|
@"factoryMethodArgumentType", @"allowsNull",
|
|
@"parameterDirection", @"_internalInfo", nil]));
|
|
NSAssert(EOATTRIBUTE_PROTO_OVERRIDE_BITS_COUNT==EOAttributeProtoOverrideBits__count,
|
|
@"Mismatch ProtoOverrideBits count");
|
|
NSAssert(EOATTRIBUTE_PROTO_OVERRIDE_BITS_COUNT==[staticPrototypeKeys count],
|
|
@"Mismatch ProtoOverrideBits keys count");
|
|
GDL2_EOAccessPrivateInit();
|
|
}
|
|
}
|
|
|
|
+ (id) attributeWithPropertyList: (NSDictionary *)propertyList
|
|
owner: (id)owner
|
|
{
|
|
return [[[self alloc] initWithPropertyList: propertyList
|
|
owner: owner] autorelease];
|
|
}
|
|
|
|
+ (id) attributeWithParent:(EOEntity *) parent
|
|
definition:(NSString*) def
|
|
{
|
|
EOAttribute * attr = [[[self alloc] init] autorelease];
|
|
|
|
if (attr)
|
|
{
|
|
[attr setName: def];
|
|
[attr setParent: parent];
|
|
[attr setDefinition: def];
|
|
}
|
|
|
|
return attr;
|
|
}
|
|
|
|
- (id) initWithPropertyList: (NSDictionary *)propertyList
|
|
owner: (id)owner
|
|
{
|
|
if ((self = [self init]))
|
|
{
|
|
NSString *tmpString = nil;
|
|
id tmpObject = nil;
|
|
|
|
// set this first so the name can validate against the parent.
|
|
[self setParent: owner];
|
|
|
|
[self setName: [propertyList objectForKey: @"name"]];
|
|
|
|
//Next set prototyName so prototype override can work
|
|
tmpString = [propertyList objectForKey: @"prototypeName"];
|
|
if (tmpString)
|
|
{
|
|
EOAttribute *attr = [[_parent model] prototypeAttributeNamed: tmpString];
|
|
|
|
if (attr)
|
|
[self setPrototype: attr];
|
|
}
|
|
|
|
[self setExternalType: [propertyList objectForKey: @"externalType"]];
|
|
|
|
tmpString = [propertyList objectForKey: @"allowsNull"];
|
|
if (tmpString || _prototypeName==nil)
|
|
[self setAllowsNull: tmpString!=nil && [tmpString boolValue]];
|
|
|
|
[self setValueType: [propertyList objectForKey: @"valueType"]];
|
|
[self setValueClassName: [propertyList objectForKey: @"valueClassName"]];
|
|
|
|
tmpString = [propertyList objectForKey: @"writeFormat"];
|
|
if (tmpString)
|
|
[self setWriteFormat: tmpString];
|
|
else
|
|
{
|
|
tmpString = [propertyList objectForKey: @"updateFormat"];
|
|
if (tmpString)
|
|
[self setWriteFormat: tmpString];
|
|
else
|
|
{
|
|
tmpString = [propertyList objectForKey: @"insertFormat"];
|
|
if (tmpString)
|
|
[self setWriteFormat: tmpString];
|
|
}
|
|
}
|
|
|
|
tmpString = [propertyList objectForKey: @"readFormat"];
|
|
if (tmpString)
|
|
[self setReadFormat: tmpString];
|
|
else
|
|
{
|
|
tmpString = [propertyList objectForKey: @"selectFormat"];
|
|
[self setReadFormat: tmpString];
|
|
}
|
|
|
|
/*
|
|
tmpString = [propertyList objectForKey: @"maximumLength"];
|
|
if (tmpString)
|
|
[self setMaximumLength: [tmpString intValue]];
|
|
*/
|
|
|
|
tmpString = [propertyList objectForKey: @"width"];
|
|
if (tmpString)
|
|
[self setWidth: [tmpString intValue]];
|
|
|
|
tmpString = [propertyList objectForKey: @"valueFactoryMethodName"];
|
|
if (tmpString)
|
|
[self setValueFactoryMethodName: tmpString];
|
|
|
|
tmpString = [propertyList objectForKey: @"adaptorValueConversionMethodName"];
|
|
if (tmpString)
|
|
[self setAdaptorValueConversionMethodName: tmpString];
|
|
|
|
tmpString = [propertyList objectForKey: @"factoryMethodArgumentType"];
|
|
if(tmpString)
|
|
{
|
|
EOFactoryMethodArgumentType argType = EOFactoryMethodArgumentIsBytes;
|
|
|
|
if ([tmpString isEqual: @"EOFactoryMethodArgumentIsNSData"])
|
|
argType = EOFactoryMethodArgumentIsNSData;
|
|
else if ([tmpString isEqual: @"EOFactoryMethodArgumentIsNSString"])
|
|
argType = EOFactoryMethodArgumentIsNSString;
|
|
|
|
[self setFactoryMethodArgumentType: argType];
|
|
}
|
|
|
|
tmpString = [propertyList objectForKey: @"precision"];
|
|
if (tmpString)
|
|
[self setPrecision: [tmpString intValue]];
|
|
|
|
tmpString = [propertyList objectForKey: @"scale"];
|
|
if (tmpString)
|
|
[self setScale: [tmpString intValue]];
|
|
|
|
tmpString = [propertyList objectForKey: @"serverTimeZone"];
|
|
if (tmpString)
|
|
[self setServerTimeZone: [NSTimeZone timeZoneWithName: tmpString]];
|
|
|
|
tmpString = [propertyList objectForKey: @"parameterDirection"];
|
|
if (tmpString)
|
|
{
|
|
EOParameterDirection tmpDir = [tmpString intValue];
|
|
EOParameterDirection eDirection = EOVoid;
|
|
|
|
if ([tmpString isEqual: @"in"] || tmpDir == EOInParameter)
|
|
eDirection = EOInParameter;
|
|
else if ([tmpString isEqual: @"out"] || tmpDir == EOOutParameter)
|
|
eDirection = EOOutParameter;
|
|
else if ([tmpString isEqual: @"inout"] || tmpDir == EOInOutParameter)
|
|
eDirection = EOInOutParameter;
|
|
|
|
[self setParameterDirection: eDirection];
|
|
}
|
|
|
|
tmpObject = [propertyList objectForKey: @"userInfo"];
|
|
|
|
if (tmpObject)
|
|
[self setUserInfo: tmpObject];
|
|
else
|
|
{
|
|
tmpObject = [propertyList objectForKey: @"userDictionary"];
|
|
|
|
if (tmpObject)
|
|
[self setUserInfo: tmpObject];
|
|
}
|
|
|
|
tmpObject = [propertyList objectForKey: @"internalInfo"];
|
|
|
|
if (tmpObject)
|
|
[self setInternalInfo: tmpObject];
|
|
|
|
tmpString = [propertyList objectForKey: @"docComment"];
|
|
|
|
if (tmpString)
|
|
[self setDocComment: tmpString];
|
|
|
|
tmpString = [propertyList objectForKey: @"isReadOnly"];
|
|
|
|
[self setReadOnly: [tmpString boolValue]];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)awakeWithPropertyList: (NSDictionary *)propertyList
|
|
{
|
|
NSString *definition = [propertyList objectForKey: @"definition"];
|
|
if (definition)
|
|
{
|
|
[self _setDefinitionWithoutFlushingCaches: definition];
|
|
[_parent _setIsEdited];
|
|
}
|
|
else
|
|
{
|
|
NSString *columnName=[propertyList objectForKey: @"columnName"];
|
|
if (columnName)
|
|
[self setColumnName: columnName];
|
|
else
|
|
{
|
|
NSString *externalName=[propertyList objectForKey: @"externalName"];
|
|
if ([externalName isKindOfClass:[NSString class]])
|
|
[self setColumnName: externalName];
|
|
else if ([externalName isKindOfClass:[NSDictionary class]])
|
|
ASSIGN(_definitionArray,[self _objectForPList:(NSDictionary*)externalName]);
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)encodeIntoPropertyList: (NSMutableDictionary *)propertyList
|
|
{
|
|
if (_name)
|
|
[propertyList setObject: _name forKey: @"name"];
|
|
if (_prototype)
|
|
[propertyList setObject: [_prototype name] forKey: @"prototypeName"];
|
|
if (_serverTimeZone)
|
|
[propertyList setObject: [_serverTimeZone name]
|
|
forKey: @"serverTimeZone"];
|
|
if (_columnName)
|
|
[propertyList setObject: _columnName forKey: @"columnName"];
|
|
if (_definitionArray)
|
|
[propertyList setObject: [_definitionArray definition]
|
|
forKey: @"definition"];
|
|
if (_externalType)
|
|
[propertyList setObject: _externalType forKey: @"externalType"];
|
|
if (_valueClassName)
|
|
[propertyList setObject: _valueClassName forKey: @"valueClassName"];
|
|
if (_valueType)
|
|
[propertyList setObject: _valueType forKey: @"valueType"];
|
|
|
|
if (_valueFactoryMethodName)
|
|
{
|
|
NSString *methodArg = nil;
|
|
|
|
[propertyList setObject: _valueFactoryMethodName
|
|
forKey: @"valueFactoryMethodName"];
|
|
|
|
switch (_argumentType)
|
|
{
|
|
case EOFactoryMethodArgumentIsNSData:
|
|
methodArg = @"EOFactoryMethodArgumentIsNSData";
|
|
break;
|
|
case EOFactoryMethodArgumentIsNSString:
|
|
methodArg = @"EOFactoryMethodArgumentIsNSString";
|
|
break;
|
|
case EOFactoryMethodArgumentIsBytes:
|
|
methodArg = @"EOFactoryMethodArgumentIsBytes";
|
|
break;
|
|
default:
|
|
methodArg = nil;
|
|
[NSException raise: NSInternalInconsistencyException
|
|
format: @"%@ -- %@ 0x%p: Invalid value for _argumentType:%d",
|
|
NSStringFromSelector(_cmd),
|
|
NSStringFromClass([self class]),
|
|
self, _argumentType];
|
|
}
|
|
|
|
[propertyList setObject: methodArg
|
|
forKey: @"factoryMethodArgumentType"];
|
|
}
|
|
|
|
if (_adaptorValueConversionMethodName)
|
|
[propertyList setObject: _adaptorValueConversionMethodName
|
|
forKey: @"adaptorValueConversionMethodName"];
|
|
|
|
if (_readFormat)
|
|
[propertyList setObject: _readFormat forKey: @"readFormat"];
|
|
if (_writeFormat)
|
|
[propertyList setObject: _writeFormat forKey: @"writeFormat"];
|
|
if (_width > 0)
|
|
[propertyList setObject: [NSString stringWithFormat:@"%u", _width]
|
|
forKey: @"width"];
|
|
if (_precision > 0)
|
|
[propertyList setObject: [NSString stringWithFormat:@"%hu", _precision]
|
|
forKey: @"precision"];
|
|
if (_scale != 0)
|
|
[propertyList setObject: [NSString stringWithFormat:@"%hi", _scale]
|
|
forKey: @"scale"];
|
|
|
|
if (_parameterDirection != 0)
|
|
[propertyList setObject: [NSString stringWithFormat:@"%d",
|
|
(int)_parameterDirection]
|
|
forKey: @"parameterDirection"];
|
|
|
|
if (_userInfo)
|
|
[propertyList setObject: _userInfo forKey: @"userInfo"];
|
|
if (_docComment)
|
|
[propertyList setObject: _docComment forKey: @"docComment"];
|
|
|
|
if (_flags.isReadOnly)
|
|
[propertyList setObject: @"Y"
|
|
forKey: @"isReadOnly"];
|
|
if (_flags.allowsNull)
|
|
[propertyList setObject: @"Y"
|
|
forKey: @"allowsNull"];
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
DESTROY(_name);
|
|
DESTROY(_prototypeName);
|
|
DESTROY(_prototype);
|
|
DESTROY(_columnName);
|
|
DESTROY(_externalType);
|
|
DESTROY(_valueType);
|
|
DESTROY(_valueClassName);
|
|
DESTROY(_readFormat);
|
|
DESTROY(_writeFormat);
|
|
DESTROY(_serverTimeZone);
|
|
DESTROY(_valueFactoryMethodName);
|
|
DESTROY(_adaptorValueConversionMethodName);
|
|
DESTROY(_sourceToDestinationKeyMap);
|
|
DESTROY(_userInfo);
|
|
DESTROY(_internalInfo);
|
|
DESTROY(_docComment);
|
|
|
|
[super dealloc];
|
|
}
|
|
|
|
- (NSUInteger)hash
|
|
{
|
|
return [_name hash];
|
|
}
|
|
|
|
- (NSString *)description
|
|
{
|
|
NSString *dscr = [NSString stringWithFormat: @"<%s %p - name=%@ entity=%@ columnName=%@ definition=%@ ",
|
|
object_getClassName(self),
|
|
(void*)self,
|
|
[self name],
|
|
[[self entity] name],
|
|
[self columnName],
|
|
[self definition]];
|
|
|
|
dscr = [dscr stringByAppendingFormat: @"valueClassName=%@ valueType=%@ externalType=%@ allowsNull=%s isReadOnly=%s isDerived=%s isFlattened=%s>",
|
|
[self valueClassName],
|
|
[self valueType],
|
|
[self externalType],
|
|
([self allowsNull] ? "YES" : "NO"),
|
|
([self isReadOnly] ? "YES" : "NO"),
|
|
([self isDerived] ? "YES" : "NO"),
|
|
([self isFlattened] ? "YES" : "NO")];
|
|
|
|
return dscr;
|
|
}
|
|
|
|
/* We override NSObjects default implementation
|
|
as attributes cannot be copied */
|
|
- (id)copyWithZone:(NSZone *)zone
|
|
{
|
|
[self notImplemented: _cmd];
|
|
return nil;
|
|
}
|
|
|
|
/**
|
|
4.5 Docs say:
|
|
Returns the entity that owns the attribute, or nil if this attribute is acting as an argument for a
|
|
stored procedure.
|
|
|
|
5.x returns the parent
|
|
*/
|
|
- (EOEntity *)entity
|
|
{
|
|
if (_flags.isParentAnEOEntity)
|
|
return _parent;
|
|
else
|
|
return nil;
|
|
}
|
|
|
|
- (NSString *)name
|
|
{
|
|
return _name;
|
|
}
|
|
|
|
- (NSString *)columnName
|
|
{
|
|
if (_columnName)
|
|
return _columnName;
|
|
|
|
return [_prototype columnName];
|
|
}
|
|
|
|
- (NSString *)definition
|
|
{
|
|
return [_definitionArray valueForSQLExpression: nil];
|
|
}
|
|
|
|
- (NSString *)readFormat
|
|
{
|
|
return _readFormat;
|
|
}
|
|
|
|
- (NSString *)writeFormat
|
|
{
|
|
return _writeFormat;
|
|
}
|
|
|
|
- (NSDictionary *)userInfo
|
|
{
|
|
return _userInfo;
|
|
}
|
|
|
|
- (NSDictionary *)internalInfo
|
|
{
|
|
return _internalInfo;
|
|
}
|
|
|
|
- (NSString *)docComment
|
|
{
|
|
return _docComment;
|
|
}
|
|
|
|
// http://www.omnigroup.com/mailman/archive/eof/1997/003003.html
|
|
/*
|
|
Eric Hermanson wrote:
|
|
The precision is the number of digits including after decimal digits. The scale is after
|
|
decimal digits. Therefore, 12345.67 has a precision of 7 and a scale of 2. The
|
|
difference between precision/scale really does need to be documented a bit better.
|
|
*/
|
|
- (int)scale
|
|
{
|
|
return _scale;
|
|
}
|
|
|
|
- (unsigned)precision
|
|
{
|
|
return _precision;
|
|
}
|
|
|
|
- (unsigned)width
|
|
{
|
|
return _width;
|
|
}
|
|
|
|
- (id)parent
|
|
{
|
|
return _parent;
|
|
}
|
|
|
|
- (EOAttribute *)prototype
|
|
{
|
|
return _prototype;
|
|
}
|
|
|
|
- (NSString *)prototypeName
|
|
{
|
|
return [_prototype name];
|
|
}
|
|
|
|
- (EOParameterDirection)parameterDirection
|
|
{
|
|
return _parameterDirection;
|
|
}
|
|
|
|
- (BOOL)allowsNull
|
|
{
|
|
return _flags.allowsNull;
|
|
}
|
|
|
|
- (EOStoredProcedure *)storedProcedure
|
|
{
|
|
if ([_parent isKindOfClass: [EOStoredProcedure class]])
|
|
return _parent;
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (BOOL)isReadOnly
|
|
{
|
|
if (_flags.isReadOnly)
|
|
return YES;
|
|
else if ([self isDerived] && ![self isFlattened])
|
|
return YES;
|
|
else
|
|
return NO;
|
|
}
|
|
|
|
/**
|
|
* Return NO when the attribute corresponds to one SQL column in its entity
|
|
* associated table return YES otherwise.
|
|
* An attribute with a definition such as
|
|
* "anotherAttributeName * 2" is derived.
|
|
* A Flattened attribute is also a derived attributes.
|
|
**/
|
|
- (BOOL)isDerived
|
|
{
|
|
return (_definitionArray==nil ? NO : YES);
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns YES if the attribute is flattened, NO otherwise.
|
|
* A flattened attribute is an attribute with a definition
|
|
* using a relationship to another entity.
|
|
* A Flattened attribute is also a derived attribute.
|
|
**/
|
|
- (BOOL)isFlattened
|
|
{
|
|
//TODO cahe result ?
|
|
BOOL isFlattened=NO;
|
|
if (_definitionArray!=nil)
|
|
{
|
|
int definitionArrayCount=[_definitionArray count];
|
|
if (definitionArrayCount>=2)
|
|
{
|
|
BOOL cont=YES;
|
|
int i=0;
|
|
for(i=0;i<definitionArrayCount-1;i++)
|
|
{
|
|
id d=[_definitionArray objectAtIndex:i];
|
|
if (![d isKindOfClass:GDL2_EORelationshipClass])
|
|
{
|
|
cont=NO;
|
|
break;
|
|
}
|
|
}
|
|
if (cont)
|
|
isFlattened=[[_definitionArray lastObject]isKindOfClass:GDL2_EOAttributeClass];
|
|
}
|
|
}
|
|
return isFlattened;
|
|
}
|
|
|
|
/**
|
|
* <p>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.</p>
|
|
* <p>If the valueClassName has not been set explicitly and the
|
|
* reciever [-isFlattened], the valueClassName of the flattened
|
|
* attribute is returned.</p>
|
|
* <p>Otherwise, if the reciever has a prototype then the
|
|
* valueClassName of the prototype is returned.</p>
|
|
* <p>If all that fails, this method returns nil.</p>
|
|
* <p>See also:[setValueClassName:]</p>
|
|
*/
|
|
- (NSString *)valueClassName
|
|
{
|
|
if (_valueClassName)
|
|
return _valueClassName;
|
|
else if ([self isFlattened])
|
|
return [[_definitionArray realAttribute] valueClassName];
|
|
else
|
|
return nil;
|
|
}
|
|
|
|
/**
|
|
* <p>Returns the adaptor specific name of externalType. This is
|
|
* the name use during SQL generation.</p>
|
|
* <p>If the externalType has not been set explicitly and the
|
|
* reciever [-isFlattened], the valueClassName of the flattened
|
|
* attribute is returned.</p>
|
|
* <p>Otherwise, if the reciever has a prototype then the
|
|
* externalType of the prototype is returned.</p>
|
|
* <p>If all that fails, this method returns nil.</p>
|
|
*/
|
|
- (NSString *)externalType
|
|
{
|
|
if (_externalType)
|
|
return _externalType;
|
|
else if ([self isFlattened])
|
|
return [[_definitionArray realAttribute] externalType];
|
|
else
|
|
return nil;
|
|
}
|
|
|
|
/**
|
|
* <p>Returns a one character string identifiying the underlying
|
|
* C type of an NSNumber [-valueType]. The legal values in GDL2 are:</p>
|
|
* <list>
|
|
* <item>@"c": char</item>
|
|
* <item>@"C": unsigned char</item>
|
|
* <item>@"s": short</item>
|
|
* <item>@"S": unsigned short</item>
|
|
* <item>@"i": int</item>
|
|
* <item>@"I": unsigned int</item>
|
|
* <item>@"l": long</item>
|
|
* <item>@"L": unsigned long</item>
|
|
* <item>@"u": long long</item>
|
|
* <item>@"U": unsigned long long</item>
|
|
* <item>@"f": float</item>
|
|
* <item>@"d": double</item>
|
|
* </list>
|
|
* <p>If the valueType has not been set explicitly and the
|
|
* reciever [-isFlattened], the valueClassName of the flattened
|
|
* attribute is returned.</p>
|
|
* <p>Otherwise, if the reciever has a prototype then the
|
|
* valueType of the prototype is returned.</p>
|
|
* <p>If all that fails, this method returns nil.</p>
|
|
*/
|
|
- (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;j<aDefCount;j++)
|
|
[exprArray addObject:[(EOExpressionArray*)aDef objectAtIndex:j]];
|
|
}
|
|
else
|
|
{
|
|
[exprArray addObject:aDef];
|
|
}
|
|
}
|
|
|
|
result = exprArray;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/**
|
|
* <p>Sets the definition of a derived attribute.</p>
|
|
* <p>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.<p>
|
|
* <p>To set the definition of an attribute, the attribute must
|
|
* already be contained by its parent entity.</p>
|
|
* <p>Setting the the definition clears the column name.</p>
|
|
*/
|
|
- (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;
|
|
}
|
|
|
|
/**
|
|
* <p>Returns the name of the method to use for creating a custom class
|
|
* value for this attribute.</p>
|
|
* See Also: [-valueFactoryMethod], [-newValueForBytes:length:]
|
|
*/
|
|
- (NSString *)valueFactoryMethodName
|
|
{
|
|
return _valueFactoryMethodName;
|
|
}
|
|
|
|
/**
|
|
* <p>Returns the selector of the method to use for creating a custom class
|
|
* value for this attribute.</p>
|
|
* <p>Default implementation returns selector for name returned by
|
|
* [-valueFactoryMethodName] or NULL if no selector is found.</p>
|
|
*
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* <p>Returns method name to use to convert value of a class
|
|
* different than attribute adaptor value type.</p>
|
|
*
|
|
* See also: [-adaptorValueByConvertingAttributeValue:],
|
|
* [-adaptorValueConversionMethod]
|
|
*/
|
|
- (NSString *)adaptorValueConversionMethodName
|
|
{
|
|
return _adaptorValueConversionMethodName;
|
|
}
|
|
|
|
/**
|
|
* <p>Returns selector of the method to use to convert value of a class
|
|
* different than attribute adaptor value type.</p>
|
|
* <p>The default implementation returns the selector corresponding to
|
|
* [-adaptorValueConversionMethodName] or NULL if there's not selector
|
|
* for the method.</p>
|
|
*
|
|
* See also: [-adaptorValueByConvertingAttributeValue:],
|
|
* [-adaptorValueConversionMethodName]
|
|
*/
|
|
- (SEL)adaptorValueConversionMethod
|
|
{
|
|
return _adaptorValueConversionMethod;
|
|
}
|
|
|
|
/**
|
|
* <p>Returns an EOAdaptorValueType describing the adaptor
|
|
* (i.e. database) type of data for this attribute.</p>
|
|
*
|
|
* <p>Returned value can be:</p>
|
|
* <list>
|
|
* <item>EOAdaptorBytesType
|
|
* Raw bytes (default type)</item>
|
|
* <item>EOAdaptorNumberType
|
|
* Number value (attribute valueClass is kind of NSNumber) </item>
|
|
* <item>EOAdaptorCharactersType
|
|
* String value (attribute valueClass is kind of NSString)</item>
|
|
* <item>EOAdaptorDateType
|
|
* Date value (attribute valueClass is kind of NSDate)</item>
|
|
* </list>
|
|
*/
|
|
- (EOAdaptorValueType)adaptorValueType
|
|
{
|
|
if (!_flags.isAttributeValueInitialized)
|
|
{
|
|
Class adaptorClasses[] = { GDL2_NSNumberClass,
|
|
GDL2_NSStringClass,
|
|
GDL2_NSDateClass };
|
|
EOAdaptorValueType values[] = { EOAdaptorNumberType,
|
|
EOAdaptorCharactersType,
|
|
EOAdaptorDateType };
|
|
Class valueClass = Nil;
|
|
int i = 0;
|
|
|
|
_adaptorValueType = EOAdaptorBytesType;
|
|
|
|
for ( i = 0; i < 3 && !_flags.isAttributeValueInitialized; i++)
|
|
{
|
|
for ( valueClass = [self _valueClass];
|
|
valueClass != Nil;
|
|
valueClass = GSObjCSuper(valueClass))
|
|
{
|
|
if (valueClass == adaptorClasses[i])
|
|
{
|
|
_adaptorValueType=values[i];
|
|
_flags.isAttributeValueInitialized = YES;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
_flags.isAttributeValueInitialized = YES;
|
|
};
|
|
return _adaptorValueType;
|
|
}
|
|
|
|
|
|
/** Returns 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: -valueFactoryMethod, -setFactoryMethodArgumentType:
|
|
**/
|
|
- (EOFactoryMethodArgumentType)factoryMethodArgumentType
|
|
{
|
|
return _argumentType;
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@implementation EOAttribute (EOAttributeValueCreationEditing)
|
|
|
|
/** Set the "factory method" name (the method to invoke to create custom class attribute value).
|
|
This method must be a class method returning an autoreleased value of attribute valueClass.
|
|
|
|
See also: -setFactoryMethodArgumentType:
|
|
**/
|
|
- (void)setValueFactoryMethodName: (NSString *)factoryMethodName
|
|
{
|
|
[self willChange];
|
|
ASSIGNCOPY(_valueFactoryMethodName, factoryMethodName);
|
|
_valueFactoryMethod = NSSelectorFromString(_valueFactoryMethodName);
|
|
|
|
[self _setOverrideForKeyEnum: EOAttributeProtoOverrideBits_valueFactoryMethodName];
|
|
}
|
|
|
|
/**
|
|
* <p>Set method name to use to convert value of a class
|
|
* different than attribute adaptor value type.</p>
|
|
*
|
|
* 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<count;i++)
|
|
{
|
|
EOExpressionArray* part = [self _objectForPList:[array objectAtIndex:i]];
|
|
if (part!=nil)
|
|
[exprArray addObject:part];
|
|
}
|
|
}
|
|
result=exprArray;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
- (void) _setValuesFromTargetAttribute
|
|
{
|
|
if ([self isFlattened])
|
|
{
|
|
EOAttribute* attribute = [_definitionArray lastObject];
|
|
[self setExternalType:[attribute externalType]];
|
|
[self setValueClassName:[attribute valueClassName]];
|
|
[self setValueType:[attribute valueType]];
|
|
[self setWidth:[attribute width]];
|
|
[self setAllowsNull:[attribute allowsNull]];
|
|
[self setReadFormat:[attribute readFormat]];
|
|
[self setWriteFormat:[attribute writeFormat]];
|
|
[self setReadOnly:[attribute isReadOnly]];
|
|
[self setParameterDirection:[attribute parameterDirection]];
|
|
[self setUserInfo:[attribute userInfo]];
|
|
[self setInternalInfo:[attribute internalInfo]];
|
|
switch([attribute adaptorValueType])
|
|
{
|
|
case EOAdaptorNumberType:
|
|
[self setPrecision:[attribute precision]];
|
|
[self setScale:[attribute scale]];
|
|
break;
|
|
case EOAdaptorCharactersType:
|
|
break;
|
|
case EOAdaptorBytesType:
|
|
[self setValueFactoryMethodName:[attribute valueFactoryMethodName]];
|
|
[self setAdaptorValueConversionMethodName:[attribute adaptorValueConversionMethodName]];
|
|
[self setFactoryMethodArgumentType:[attribute factoryMethodArgumentType]];
|
|
break;
|
|
case EOAdaptorDateType:
|
|
[self setServerTimeZone:[attribute serverTimeZone]];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
-(void)_setSourceToDestinationKeyMap:(NSDictionary*)map
|
|
{
|
|
ASSIGN(_sourceToDestinationKeyMap,map);
|
|
}
|
|
|
|
-(NSDictionary*) _sourceToDestinationKeyMap
|
|
{
|
|
if (_sourceToDestinationKeyMap == nil)
|
|
[self _setSourceToDestinationKeyMap:[[self entity]_keyMapForRelationshipPath:[self relationshipPath]]];
|
|
return _sourceToDestinationKeyMap;
|
|
}
|
|
|
|
-(BOOL)_isNonUpdateable
|
|
{
|
|
if (!_flags.isNonUpdateableInitialized)
|
|
{
|
|
_flags.isNonUpdateable = ([self isReadOnly] || [self _isPrimaryKeyClassProperty]);
|
|
_flags.isNonUpdateableInitialized = YES;
|
|
}
|
|
return _flags.isNonUpdateable;
|
|
}
|
|
|
|
-(BOOL)_isPrimaryKeyClassProperty
|
|
{
|
|
NSArray* pkAttrs = [_parent primaryKeyAttributes];
|
|
if ([pkAttrs containsObject:self]
|
|
&& [[_parent classProperties]containsObject:self])
|
|
return YES;
|
|
else
|
|
return NO;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation EOAttribute (EOAttributePrivate2)
|
|
|
|
+ (NSArray*)_prototypeKeys
|
|
{
|
|
return staticPrototypeKeys;
|
|
}
|
|
|
|
+ (NSString*)_keyForEnum:(int)enumValue
|
|
{
|
|
return [staticPrototypeKeys objectAtIndex:enumValue];
|
|
}
|
|
|
|
- (BOOL) _hasAnyOverrides
|
|
{
|
|
return (_flags.protoOverride==0 ? NO : YES);
|
|
}
|
|
|
|
- (void) _resetPrototype
|
|
{
|
|
if (_prototypeName != nil)
|
|
{
|
|
DESTROY(_prototype);
|
|
[self prototype];
|
|
}
|
|
}
|
|
|
|
- (void) _updateFromPrototype
|
|
{
|
|
// backup protoOverride flags
|
|
int protoOverride = _flags.protoOverride;
|
|
|
|
NSMutableArray* overridenProtoKeys=
|
|
[NSMutableArray arrayWithCapacity:EOATTRIBUTE_PROTO_OVERRIDE_BITS_COUNT];
|
|
int i=0;
|
|
|
|
// Get overriden prototype keys
|
|
for(i = 0; i < EOATTRIBUTE_PROTO_OVERRIDE_BITS_COUNT; i++)
|
|
{
|
|
if ((_flags.protoOverride & 1 << i) != 0)
|
|
[overridenProtoKeys addObject:[GDL2_EOAttributeClass _keyForEnum:i]];
|
|
}
|
|
|
|
// Remove overriden from all keys
|
|
NSArray* notOverridenProtoKeys=[[GDL2_EOAttributeClass _prototypeKeys]
|
|
arrayExcludingObjectsInArray:overridenProtoKeys];
|
|
NSDictionary* notOverridenKV=[_prototype valuesForKeys:notOverridenProtoKeys];
|
|
[self takeValuesFromDictionary:notOverridenKV];
|
|
|
|
// restore protoOverride flags
|
|
_flags.protoOverride = protoOverride;
|
|
}
|
|
|
|
- (void) _setOverrideForKeyEnum: (EOAttributeProtoOverrideBits)keyEnum
|
|
{
|
|
_flags.protoOverride|= 1 << keyEnum;
|
|
}
|
|
|
|
- (BOOL) _isKeyEnumOverriden: (EOAttributeProtoOverrideBits)keyEnum
|
|
{
|
|
return ((_prototype != nil && (_flags.protoOverride & 1 << keyEnum) != 0) ? YES : NO);
|
|
}
|
|
|
|
- (BOOL) _isKeyEnumDefinedByPrototype: (EOAttributeProtoOverrideBits)keyEnum
|
|
{
|
|
return ((_prototype != nil && (_flags.protoOverride & 1 << keyEnum) == 0) ? YES : NO);
|
|
}
|
|
|
|
@end
|