/** EOEntity.m EOEntity Class Copyright (C) 2000, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. Author: Mirko Viviani Date: February 2000 Author: Manuel Guesdon Date: October 2000 $Revision$ $Date$ This file is part of the GNUstep Database Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 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. **/ #include "config.h" RCS_ID("$Id$") #include #ifdef GNUSTEP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #else #include #include #include #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "EOPrivate.h" #include "EOEntityPriv.h" #include "EOAttributePriv.h" #include "../EOControl/EOPrivate.h" @interface EOModel (Privat) - (void)_updateCache; @end NSString *EOFetchAllProcedureOperation = @"EOFetchAllProcedureOperation"; NSString *EOFetchWithPrimaryKeyProcedureOperation = @"EOFetchWithPrimaryKeyProcedureOperation"; NSString *EOInsertProcedureOperation = @"EOInsertProcedureOperation"; NSString *EODeleteProcedureOperation = @"EODeleteProcedureOperation"; NSString *EONextPrimaryKeyProcedureOperation = @"EONextPrimaryKeyProcedureOperation"; @implementation EOEntity + (void)initialize { static BOOL initialized=NO; if (!initialized) { initialized=YES; GDL2_EOAccessPrivateInit(); }; }; /* Not documented becuase it is not a public method. */ - (id)initWithPropertyList: (NSDictionary*)propertyList owner: (id)owner { [EOObserverCenter suppressObserverNotification]; EOFLOGObjectLevelArgs(@"EOEntity", @"propertyList=%@", propertyList); NS_DURING { if ((self = [self init]) != nil) { NSArray *array = nil; NSDictionary * aDict = nil; NSString *tmpString = nil; id tmpObject = nil; _flags.updating = YES; /* set this before validation. */ if ([owner isKindOfClass:[EOModel class]]) [self _setModel:owner]; // else _setParent:?? tmpString = [propertyList objectForKey:@"name"]; /* we dont want it to call _updateCache. So we validate and set the name directly, as we haven't been added to the model yet, and this would causes problems. */ [[self validateName:tmpString] raise]; ASSIGN(_name, tmpString); [self setExternalName: [propertyList objectForKey: @"externalName"]]; tmpObject = [propertyList objectForKey: @"externalQuery"]; [self setExternalQuery: tmpObject]; tmpString = [propertyList objectForKey: @"restrictingQualifier"]; EOFLOGObjectLevelArgs(@"EOEntity",@"tmpString=%@",tmpString); if (tmpString) { EOQualifier *restrictingQualifier = [EOQualifier qualifierWithQualifierFormat: tmpString arguments:nil]; [self setRestrictingQualifier: restrictingQualifier]; } tmpString = [propertyList objectForKey: @"mappingQualifier"]; if (tmpString) { NSEmitTODO(); //TODO } tmpObject = [propertyList objectForKey: @"isReadOnly"]; [self setReadOnly: [tmpObject boolValue]]; tmpObject = [propertyList objectForKey: @"cachesObjects"]; [self setCachesObjects: [tmpObject boolValue]]; tmpObject = [propertyList objectForKey: @"userInfo"]; EOFLOGObjectLevelArgs(@"EOEntity", @"tmpObject=%@", tmpObject); if (tmpObject) { [self setUserInfo: tmpObject]; } else { tmpObject = [propertyList objectForKey: @"userDictionary"]; [self setUserInfo: tmpObject]; } tmpObject = [propertyList objectForKey: @"internalInfo"]; EOFLOGObjectLevelArgs(@"EOEntity", @"tmpObject=%@ [%@]", tmpObject, [tmpObject class]); [self _setInternalInfo: tmpObject]; [self setDocComment:[propertyList objectForKey:@"docComment"]]; [self setClassName: [propertyList objectForKey: @"className"]]; tmpObject = [propertyList objectForKey: @"isAbstractEntity"]; [self setIsAbstractEntity: [tmpObject boolValue]]; tmpString = [propertyList objectForKey: @"isFetchable"]; if (tmpString) { NSEmitTODO(); //TODO } array = [propertyList objectForKey: @"attributes"]; EOFLOGObjectLevelArgs(@"EOEntity", @"Attributes: %@", array); if ([array count] > 0) { ASSIGN(_attributes, array); _flags.attributesIsLazy = YES; } array = [propertyList objectForKey: @"attributesUsedForLocking"]; EOFLOGObjectLevelArgs(@"EOEntity", @"attributesUsedForLocking: %@", array); if ([array count] > 0) { ASSIGN(_attributesUsedForLocking, array); _flags.attributesUsedForLockingIsLazy = YES; } array = [propertyList objectForKey: @"primaryKeyAttributes"]; array = [array sortedArrayUsingSelector: @selector(compare:)]; EOFLOGObjectLevelArgs(@"EOEntity", @"primaryKeyAttributes: %@", array); if ([array count] > 0) { ASSIGN(_primaryKeyAttributes, array); _flags.primaryKeyAttributesIsLazy = YES; } /* * Assign them to _classProperties, not _classPropertyNames, * this will be build after */ array = [propertyList objectForKey: @"classProperties"]; EOFLOGObjectLevelArgs(@"EOEntity", @"classProperties: %@", array); if ([array count] > 0) { ASSIGN(_classProperties, array); _flags.classPropertiesIsLazy = YES; } array = [propertyList objectForKey: @"relationships"]; EOFLOGObjectLevelArgs(@"EOEntity", @"relationships: %@", array); if ([array count] > 0) { ASSIGN(_relationships, array); _flags.relationshipsIsLazy = YES; } if ((aDict = [propertyList objectForKey: @"storedProcedureNames"]) != nil) { NSEnumerator * keyEnumerator = [aDict keyEnumerator]; NSString * curentKey; ASSIGN(_storedProcedures, [NSMutableDictionary dictionary]); while ((curentKey = [keyEnumerator nextObject])) { EOStoredProcedure * storedproc; if ((storedproc = [_model storedProcedureNamed:[aDict objectForKey:curentKey]])) { [_storedProcedures setObject:storedproc forKey:curentKey]; } } } tmpString = [propertyList objectForKey: @"maxNumberOfInstancesToBatchFetch"]; EOFLOGObjectLevelArgs(@"EOEntity", @"maxNumberOfInstancesToBatchFetch=%@ [%@]", tmpString, [tmpString class]); if (tmpString) [self setMaxNumberOfInstancesToBatchFetch: [tmpString intValue]]; tmpString=[propertyList objectForKey:@"batchFaultingMaxSize"]; if (tmpString) { NSEmitTODO(); //TODO //[self setBatchFaultingMaxSize: [tmpString intValue]]; } tmpObject = [propertyList objectForKey: @"fetchSpecificationDictionary"]; EOFLOGObjectLevelArgs(@"EOEntity", @"fetchSpecificationDictionary=%@ [%@]", tmpObject, [tmpObject class]); if (tmpObject) { tmpObject = AUTORELEASE([tmpObject mutableCopy]); ASSIGN(_fetchSpecificationDictionary, tmpObject); } else { _fetchSpecificationDictionary = [NSMutableDictionary new]; EOFLOGObjectLevelArgs(@"EOEntity", @"Entity %@ - _fetchSpecificationDictionary %p [RC=%d]:%@", [self name], _fetchSpecificationDictionary, [_fetchSpecificationDictionary retainCount], _fetchSpecificationDictionary); } // load entity's FetchSpecifications { NSDictionary *plist = nil; NSString* fileName; NSString* path; fileName = [NSString stringWithFormat: @"%@.fspec", _name]; path = [(EOModel *)owner path]; path = [path stringByAppendingPathComponent: fileName]; if ([[NSFileManager defaultManager] fileExistsAtPath: path]) plist = [[NSString stringWithContentsOfFile: path] propertyList]; if (plist) { EOKeyValueUnarchiver *unarchiver; NSDictionary *variables; NSEnumerator *variablesEnum; id fetchSpecName; unarchiver = AUTORELEASE([[EOKeyValueUnarchiver alloc] initWithDictionary: [NSDictionary dictionaryWithObject: plist forKey: @"fspecs"]]); variables = [unarchiver decodeObjectForKey: @"fspecs"]; [unarchiver finishInitializationOfObjects]; [unarchiver awakeObjects]; variablesEnum = [variables keyEnumerator]; while ((fetchSpecName = [variablesEnum nextObject])) { id fetchSpec = [variables objectForKey: fetchSpecName]; [self addFetchSpecification: fetchSpec withName: fetchSpecName]; } } } _flags.updating = NO; } } NS_HANDLER { [EOObserverCenter enableObserverNotification]; NSLog(@"exception in EOEntity initWithPropertyList:owner:"); NSLog(@"exception=%@", localException); NSLog(@"exception=%@", localException); [localException raise]; } NS_ENDHANDLER; [EOObserverCenter enableObserverNotification]; return self; } - (void)awakeWithPropertyList: (NSDictionary *)propertyList { NSString *tmp; if ((tmp = [propertyList objectForKey:@"parent"])) { EOEntity *parent = [_model entityNamed:tmp]; /* TODO tests for parents spanning models. */ if (!parent) parent = [[_model modelGroup] entityNamed:tmp]; [parent addSubEntity:self]; } } - (void)encodeIntoPropertyList: (NSMutableDictionary *)propertyList { int i, count; if (_name) [propertyList setObject: _name forKey: @"name"]; if (_className) [propertyList setObject: _className forKey: @"className"]; if (_externalName) [propertyList setObject: _externalName forKey: @"externalName"]; if (_externalQuery) [propertyList setObject: _externalQuery forKey: @"externalQuery"]; if (_userInfo) [propertyList setObject: _userInfo forKey: @"userInfo"]; if (_docComment) [propertyList setObject: _docComment forKey: @"docComment"]; if (_batchCount) [propertyList setObject: [NSString stringWithFormat:@"%d", _batchCount] forKey: @"maxNumberOfInstancesToBatchFetch"]; if (_flags.cachesObjects) [propertyList setObject: @"Y" forKey: @"cachesObjects"]; if (_flags.isAbstractEntity) [propertyList setObject: @"Y" forKey: @"isAbstractEntity"]; if (_parent) [propertyList setObject: [_parent name] forKey: @"parent"]; if ((count = [_attributes count])) { if (_flags.attributesIsLazy) [propertyList setObject: _attributes forKey: @"attributes"]; else { NSMutableArray *attributesPList = [NSMutableArray array]; for (i = 0; i < count; i++) { NSMutableDictionary *attributePList = [NSMutableDictionary dictionary]; [[_attributes objectAtIndex: i] encodeIntoPropertyList: attributePList]; [attributesPList addObject: attributePList]; } [propertyList setObject: attributesPList forKey: @"attributes"]; } } if ((count = [_attributesUsedForLocking count])) { if (_flags.attributesUsedForLockingIsLazy) [propertyList setObject: _attributesUsedForLocking forKey: @"attributesUsedForLocking"]; else { NSMutableArray *attributesUsedForLockingPList = [NSMutableArray array]; for (i = 0; i < count; i++) { NSString *attributePList = [(EOAttribute *)[_attributesUsedForLocking objectAtIndex: i] name]; [attributesUsedForLockingPList addObject: attributePList]; } [propertyList setObject: attributesUsedForLockingPList forKey: @"attributesUsedForLocking"]; } } if ((count = [_classProperties count])) { if (_flags.classPropertiesIsLazy) [propertyList setObject: _classProperties forKey: @"classProperties"]; else { NSMutableArray *classPropertiesPList = [NSMutableArray array]; for (i = 0; i < count; i++) { NSString *classPropertyPList = [(EOAttribute *)[_classProperties objectAtIndex: i] name]; [classPropertiesPList addObject: classPropertyPList]; } [propertyList setObject: classPropertiesPList forKey: @"classProperties"]; } } if ((count = [_primaryKeyAttributes count])) { if (_flags.primaryKeyAttributesIsLazy) [propertyList setObject: _primaryKeyAttributes forKey: @"primaryKeyAttributes"]; else { NSMutableArray *primaryKeyAttributesPList = [NSMutableArray array]; for (i = 0; i < count; i++) { NSString *attributePList= [(EOAttribute *)[_primaryKeyAttributes objectAtIndex: i] name]; [primaryKeyAttributesPList addObject: attributePList]; } [propertyList setObject: primaryKeyAttributesPList forKey: @"primaryKeyAttributes"]; } } { NSArray *relsPlist = [self relationshipsPlist]; if (relsPlist) { [propertyList setObject: relsPlist forKey: @"relationships"]; } } // stored procedures // // storedProcedureNames = {EOInsertProcedure = fooproc; }; if ((_storedProcedures != nil) && ([_storedProcedures count])) { NSString *currentKey = nil; NSEnumerator *keyEnumerator = [[[_storedProcedures allKeys] sortedArrayUsingSelector:@selector(compare:)] objectEnumerator]; NSMutableDictionary * newDict = [NSMutableDictionary dictionary]; while ((currentKey = [keyEnumerator nextObject])) { [newDict setObject:[[_storedProcedures objectForKey:currentKey] name] forKey:currentKey]; } [propertyList setObject: newDict forKey: @"storedProcedureNames"]; } } - (id) init { //OK if ((self = [super init])) { _attributes = [NSMutableArray new]; _subEntities = [NSMutableArray new]; _relationships = [NSMutableArray new]; } return self; } static void performSelectorOnArrayWithEachObjectOfClass(NSArray *arr, SEL selector, id arg, Class class) { int i, c; arr = [arr copy]; for (i = 0, c = [arr count]; i < c; i++) { id obj = [arr objectAtIndex:i]; if ([obj isKindOfClass:class]) { [obj performSelector:selector withObject:arg]; } } RELEASE(arr); } - (void) dealloc { /* these classes may contain NSDictionaries as well as entities, attributes and relationships in the case of delayed instantiation */ performSelectorOnArrayWithEachObjectOfClass(_subEntities, @selector(_setParentEntity:), nil, [EOEntity class]); performSelectorOnArrayWithEachObjectOfClass(_attributes, @selector(setParent:), nil, [EOAttribute class]); performSelectorOnArrayWithEachObjectOfClass(_relationships, @selector(setEntity:), nil, [EORelationship class]); if (_classDescription) [[EOClassDescription class] invalidateClassDescriptionCache]; DESTROY(_adaptorDictionaryInitializer); DESTROY(_instanceDictionaryInitializer); DESTROY(_primaryKeyDictionaryInitializer); DESTROY(_propertyDictionaryInitializer); DESTROY(_snapshotDictionaryInitializer); DESTROY(_attributes); DESTROY(_attributesByName); DESTROY(_attributesToFetch); DESTROY(_attributesToSave); DESTROY(_attributesUsedForLocking); DESTROY(_classDescription); DESTROY(_classForInstances); DESTROY(_className); DESTROY(_classProperties); DESTROY(_classPropertyAttributeNames); DESTROY(_classPropertyNames); DESTROY(_classPropertyToManyRelationshipNames); DESTROY(_classPropertyToOneRelationshipNames); DESTROY(_dbSnapshotKeys); DESTROY(_docComment); DESTROY(_externalName); DESTROY(_externalQuery); DESTROY(_fetchSpecificationDictionary); DESTROY(_fetchSpecificationNames); DESTROY(_hiddenRelationships); DESTROY(_internalInfo); DESTROY(_name); DESTROY(_primaryKeyAttributes); DESTROY(_primaryKeyAttributeNames); DESTROY(_propertiesToFault); // never initialized? DESTROY(_restrictingQualifier); DESTROY(_relationships); DESTROY(_relationshipsByName); DESTROY(_storedProcedures); // never initialized? DESTROY(_snapshotToAdaptorRowSubsetMapping); DESTROY(_subEntities); DESTROY(_userInfo); [super dealloc]; } - (NSString *)description { NSMutableDictionary *plist; plist = [NSMutableDictionary dictionaryWithCapacity: 4]; [self encodeIntoPropertyList: plist]; return [plist description]; } - (NSString *)debugDescription { NSString *dscr = nil; dscr = [NSString stringWithFormat: @"<%s %p - name=%@ className=%@ externalName=%@ externalQuery=%@", object_getClassName(self), (void*)self, _name, _className, _externalName, _externalQuery]; dscr = [dscr stringByAppendingFormat:@" userInfo=%@", _userInfo]; dscr = [dscr stringByAppendingFormat:@" primaryKeyAttributeNames=%@ classPropertyNames=%@>", [self primaryKeyAttributeNames], [self classPropertyNames]]; NSAssert4(!_attributesToFetch || [_attributesToFetch isKindOfClass:[NSArray class]], @"entity %@ attributesToFetch %p is not an NSArray but a %@\n%@", [self name], _attributesToFetch, [_attributesToFetch class], _attributesToFetch); return dscr; } /*----------------------------------------*/ - (NSString *)name { return _name; } - (EOModel *)model { return _model; } - (NSString *)externalName { EOFLOGObjectLevelArgs(@"EOEntity", @"entity %p (%@): external name=%@", self, [self name], _externalName); return _externalName; } - (NSString *)externalQuery { return _externalQuery; } - (EOQualifier *)restrictingQualifier { return _restrictingQualifier; } - (BOOL)isReadOnly { return _flags.isReadOnly; } - (BOOL)cachesObjects { return _flags.cachesObjects; } - (NSString *)className { return _className; } - (NSDictionary *)userInfo { return _userInfo; } - (NSArray *)attributes { if (_flags.attributesIsLazy) { int count = 0; EOFLOGObjectLevelArgs(@"EOEntity", @"START construct attributes on %p", self); count = [_attributes count]; EOFLOGObjectLevelArgs(@"EOEntity", @"Entity %@: Lazy _attributes=%@", [self name], _attributes); if (count > 0) { int i = 0; NSArray *attributePLists = AUTORELEASE(RETAIN(_attributes)); DESTROY(_attributes); DESTROY(_attributesByName); _attributes = [NSMutableArray new]; _attributesByName = [NSMutableDictionary new]; /* if we've already loaded relationships rebuild the name cache */ if (!_flags.relationshipsIsLazy && _relationshipsByName == nil) [self relationshipsByName]; _flags.attributesIsLazy = NO; [EOObserverCenter suppressObserverNotification]; _flags.updating = YES; NS_DURING { NSArray *attrNames = nil; for (i = 0; i < count; i++) { id attrPList = [attributePLists objectAtIndex: i]; EOAttribute *attribute = nil; NSString *attributeName = nil; // this should validate name against its owner via setName: attribute = [EOAttribute attributeWithPropertyList: attrPList owner: self]; attributeName = [attribute name]; // don't call -addAttribute: because it wipes our name cache [_attributes addObject: attribute]; [_attributesByName setObject: attribute forKey: attributeName]; } attrNames = [_attributes resultsOfPerformingSelector: @selector(name)]; count = [attrNames count]; NSAssert(count == [attributePLists count], @"Error during attribute creations"); { int pass = 0; //We'll first awake non derived/flattened attributes for (pass = 0; pass < 2; pass++) { for (i = 0; i < count; i++) { NSString *attrName = [attrNames objectAtIndex: i]; NSDictionary *attrPList = nil; EOAttribute *attribute = nil; id definition = nil; EOFLOGObjectLevelArgs(@"EOEntity", @"XXX attrName=%@", attrName); attrPList = [attributePLists objectAtIndex: i]; definition = [attrPList objectForKey: @"definition"]; if ((pass == 0 && definition == nil) || (pass == 1 && definition != nil)) { attribute = [self attributeNamed: attrName]; EOFLOGObjectLevelArgs(@"EOEntity", @"XXX 2A ATTRIBUTE: self=%p AWAKE attribute=%@", self, attribute); [attribute awakeWithPropertyList: attrPList]; EOFLOGObjectLevelArgs(@"EOEntity", @"XXX 2B ATTRIBUTE: self=%p attribute=%@", self, attribute); } } } } } NS_HANDLER { _flags.updating = NO; [EOObserverCenter enableObserverNotification]; [localException raise]; } NS_ENDHANDLER; _flags.updating = NO; [EOObserverCenter enableObserverNotification]; [_attributes sortUsingSelector: @selector(eoCompareOnName:)];//Very important to have always the same order. } else _flags.attributesIsLazy = NO; EOFLOGObjectLevelArgs(@"EOEntity", @"STOP construct attributes on %p", self); } return _attributes; } - (EOAttribute *)attributeNamed: (NSString *)attributeName { //OK EOAttribute *attribute = nil; NSDictionary *attributesByName = nil; attributesByName = [self attributesByName]; EOFLOGObjectLevelArgs(@"EOEntity", @"attributesByName [%p] (%@)", attributesByName, [attributesByName class]); NSAssert2((!attributesByName || [attributesByName isKindOfClass: [NSDictionary class]]), @"attributesByName is not a NSDictionary but a %@. attributesByName [%p]", [attributesByName class], attributesByName); // EOFLOGObjectLevelArgs(@"EOEntity",@"attributesByName=%@",attributesByName); attribute = [attributesByName objectForKey: attributeName]; return attribute; } /** returns attribute named attributeName (no relationship) **/ - (EOAttribute *)anyAttributeNamed: (NSString *)attributeName { EOAttribute *attr; NSEnumerator *attrEnum; attr = [self attributeNamed:attributeName]; //VERIFY /* I suppose this is intended to find the 'hidden' attributes mentioned in * the documentation of this method, but if these are in -primaryKeyAttributes * they don't appear to be well hidden, and _primaryKeyAttributes is filled * by calling -attributeNamed: and doesn't appear to be modified outside * -primaryKeyAttributes:, so this check appears to be redundant to the one * above. */ if (!attr) { IMP enumNO=NULL; attrEnum = [[self primaryKeyAttributes] objectEnumerator]; while ((attr = GDL2_NextObjectWithImpPtr(attrEnum,&enumNO))) { if ([[attr name] isEqual: attributeName]) return attr; } } return attr; } - (NSArray *)relationships { //OK if (_flags.relationshipsIsLazy) { int count = 0; EOFLOGObjectLevelArgs(@"EOEntity", @"START construct relationships on %p", self); count = [_relationships count]; EOFLOGObjectLevelArgs(@"EOEntity", @"Lazy _relationships=%@", _relationships); if (count > 0) { int i = 0; NSArray *relationshipPLists = _relationships; DESTROY(_relationshipsByName); _relationships = [NSMutableArray new]; _relationshipsByName = [NSMutableDictionary new]; if (!_flags.attributesIsLazy && _attributesByName == nil) [self attributesByName]; _flags.relationshipsIsLazy = NO; [EOObserverCenter suppressObserverNotification]; _flags.updating = YES; NS_DURING { NSArray *relNames = nil; for (i = 0; i < count; i++) { id attrPList = [relationshipPLists objectAtIndex: i]; EORelationship *relationship = nil; NSString *relationshipName = nil; /* this should cause validation to occur. */ relationship= [EORelationship relationshipWithPropertyList: attrPList owner: self]; relationshipName = [relationship name]; [_relationships addObject: relationship]; [_relationshipsByName setObject: relationship forKey: relationshipName]; } EOFLOGObjectLevel(@"EOEntity", @"Rels added"); relNames = [_relationships resultsOfPerformingSelector: @selector(name)]; EOFLOGObjectLevelArgs(@"EOEntity", @"relNames=%@", relNames); count = [relNames count]; EOFLOGObjectLevelArgs(@"EOEntity", @"self=%p rel count=%d", self, count); NSAssert(count == [relationshipPLists count], @"Error during attribute creations"); { int pass = 0; //We'll first awake non flattened relationships for (pass = 0; pass < 2; pass++) { for (i = 0; i < count; i++) { NSDictionary *relPList = [relationshipPLists objectAtIndex: i]; if ([relPList isKindOfClass: [EORelationship class]]) continue; { NSString *relName = [relNames objectAtIndex: i]; EORelationship *relationship; relationship = [self relationshipNamed: relName]; EOFLOGObjectLevelArgs(@"EOEntity", @"relName=%@", relName); if ((pass == 0 && ![relPList objectForKey: @"definition"]) || (pass == 1 && [relPList objectForKey: @"definition"])) { EOFLOGObjectLevelArgs(@"EOEntity", @"XXX REL: self=%p AWAKE relationship=%@", self, relationship); [relationship awakeWithPropertyList: relPList]; } } } } } } NS_HANDLER { EOFLOGObjectLevelArgs(@"EOEntity", @"localException=%@", localException); DESTROY(relationshipPLists); _flags.updating = NO; [EOObserverCenter enableObserverNotification]; [localException raise]; } NS_ENDHANDLER; DESTROY(relationshipPLists); _flags.updating = NO; [EOObserverCenter enableObserverNotification]; } else _flags.relationshipsIsLazy = NO; EOFLOGObjectLevelArgs(@"EOEntity", @"STOP construct relationships on %p", self); } return _relationships; } - (EORelationship *)relationshipNamed: (NSString *)relationshipName { //OK return [[self relationshipsByName] objectForKey: relationshipName]; } - (EORelationship *)anyRelationshipNamed: (NSString *)relationshipName { EORelationship *rel; NSEnumerator *relEnum = nil; rel = [self relationshipNamed: relationshipName]; //VERIFY if (!rel) { EORelationship *tmpRel = nil; IMP enumNO=NULL; relEnum = [_hiddenRelationships objectEnumerator]; while (!rel && (tmpRel = GDL2_NextObjectWithImpPtr(relEnum,&enumNO))) { if ([[tmpRel name] isEqual: relationshipName]) rel = tmpRel; } } return rel; } - (NSArray *)classProperties { if (_flags.classPropertiesIsLazy) { int count = [_classProperties count]; if (count > 0) { NSArray *classPropertiesList = _classProperties; int i; _classProperties = [NSMutableArray new]; _flags.classPropertiesIsLazy = NO; for (i = 0; i < count; i++) { #if 0 NSString *classPropertyName = [classPropertiesList objectAtIndex: i]; #else id classPropertyName = ( [[classPropertiesList objectAtIndex:i] isKindOfClass:[NSString class]] ? [classPropertiesList objectAtIndex:i] : [(EOEntity *)[classPropertiesList objectAtIndex: i] name]); #endif id classProperty = [self attributeNamed: classPropertyName]; if (!classProperty) classProperty = [self relationshipNamed: classPropertyName]; NSAssert4(classProperty, @"No attribute or relationship named '%@' (property at index %d) to use as classProperty in entity name '%@' : %@", classPropertyName, i+1, [self name], self); if ([self isValidClassProperty: classProperty]) [_classProperties addObject: classProperty]; else { //TODO NSAssert2(NO, @"not valid class prop %@ in %@", classProperty, [self name]); } } DESTROY(classPropertiesList); [_classProperties sortUsingSelector: @selector(eoCompareOnName:)]; //Very important to have always the same order. [self _setIsEdited]; //To Clean Buffers } else _flags.classPropertiesIsLazy = NO; } return _classProperties; } - (NSArray *)classPropertyNames { //OK if (!_classPropertyNames) { NSArray *classProperties = [self classProperties]; NSAssert2(!classProperties || [classProperties isKindOfClass: [NSArray class]], @"classProperties is not an NSArray but a %@\n%@", [classProperties class], classProperties); ASSIGN(_classPropertyNames, [classProperties resultsOfPerformingSelector: @selector(name)]); } NSAssert4(!_attributesToFetch || [_attributesToFetch isKindOfClass: [NSArray class]], @"entity %@ attributesToFetch %p is not an NSArray but a %@\n%@", [self name], _attributesToFetch, [_attributesToFetch class], _attributesToFetch); return _classPropertyNames; } - (NSArray *)fetchSpecificationNames { return _fetchSpecificationNames; } - (EOFetchSpecification *)fetchSpecificationNamed: (NSString *)fetchSpecName { return [_fetchSpecificationDictionary objectForKey: fetchSpecName]; } - (NSArray *)sharedObjectFetchSpecificationNames { NSEmitTODO(); //TODO [self notImplemented: _cmd]; return nil; } - (NSArray*)primaryKeyAttributes { //OK if (_flags.primaryKeyAttributesIsLazy) { int count = [_primaryKeyAttributes count]; EOFLOGObjectLevelArgs(@"EOEntity", @"Lazy _primaryKeyAttributes=%@", _primaryKeyAttributes); if (count > 0) { int i = 0; NSArray *primaryKeyAttributes = _primaryKeyAttributes; _primaryKeyAttributes = [NSMutableArray new]; _flags.primaryKeyAttributesIsLazy = NO; for (i = 0; i < count; i++) { NSString *attributeName = [primaryKeyAttributes objectAtIndex: i]; EOAttribute *attribute = [self attributeNamed: attributeName]; NSAssert3(attribute, @"In entity %@: No attribute named %@ " @"to use for locking (attributes: %@)", [self name], attributeName, [[self attributes] resultsOfPerformingSelector: @selector(name)]); if ([self isValidPrimaryKeyAttribute: attribute]) [_primaryKeyAttributes addObject: attribute]; else { NSAssert2(NO, @"not valid pk attribute %@ in %@", attribute, [self name]); } } DESTROY(primaryKeyAttributes); [_primaryKeyAttributes sortUsingSelector: @selector(eoCompareOnName:)]; //Very important to have always the same order. [self _setIsEdited]; //To Clean Buffers } else _flags.primaryKeyAttributesIsLazy = NO; } return _primaryKeyAttributes; } - (NSArray *)primaryKeyAttributeNames { //OK if (!_primaryKeyAttributeNames) { NSArray *primaryKeyAttributes = [self primaryKeyAttributes]; NSArray *primaryKeyAttributeNames = [primaryKeyAttributes resultsOfPerformingSelector: @selector(name)]; primaryKeyAttributeNames = [primaryKeyAttributeNames sortedArrayUsingSelector: @selector(compare:)]; //Not necessary: they are already sorted ASSIGN(_primaryKeyAttributeNames, primaryKeyAttributeNames); } return _primaryKeyAttributeNames; } - (NSArray *)attributesUsedForLocking { if (_flags.attributesUsedForLockingIsLazy) { int count = [_attributesUsedForLocking count]; if (count > 0) { int i = 0; NSArray *attributesUsedForLocking = _attributesUsedForLocking; _attributesUsedForLocking = [NSMutableArray new]; _flags.attributesUsedForLockingIsLazy = NO; for (i = 0; i < count; i++) { NSString *attributeName = [attributesUsedForLocking objectAtIndex: i]; EOAttribute *attribute = [self attributeNamed: attributeName]; if (attribute) { [_attributesUsedForLocking addObject: attribute]; } } DESTROY(attributesUsedForLocking); [self _setIsEdited]; //To Clean Buffers } else _flags.attributesUsedForLockingIsLazy = NO; } return _attributesUsedForLocking; } - (NSArray *)attributesToFetch { //OK NSAssert3(!_attributesToFetch || [_attributesToFetch isKindOfClass: [NSArray class]], @"entity %@ attributesToFetch %p is not an NSArray but a %@", [self name], _attributesToFetch, [_attributesToFetch class]); return [self _attributesToFetch]; } - (EOQualifier *)qualifierForPrimaryKey: (NSDictionary *)row { //OK EOQualifier *qualifier = nil; NSArray *primaryKeyAttributeNames = [self primaryKeyAttributeNames]; int count = [primaryKeyAttributeNames count]; if (count == 1) { //OK NSString *key = [primaryKeyAttributeNames objectAtIndex: 0]; id value = [row objectForKey: key]; qualifier = [EOKeyValueQualifier qualifierWithKey: key operatorSelector: EOQualifierOperatorEqual value: value]; } else { //Seems OK NSMutableArray *array = AUTORELEASE([GDL2_alloc(NSMutableArray) initWithCapacity: count]); IMP pkanOAI=NULL; IMP rowOFK=NULL; IMP arrayAO=NULL; int i; for (i = 0; i < count; i++) { NSString *key = GDL2_ObjectAtIndexWithImpPtr(primaryKeyAttributeNames,&pkanOAI,i); id value = GDL2_ObjectForKeyWithImpPtr(row,&rowOFK,key); GDL2_AddObjectWithImpPtr(array,&arrayAO, [EOKeyValueQualifier qualifierWithKey: key operatorSelector: EOQualifierOperatorEqual value: value]); } qualifier = [EOAndQualifier qualifierWithQualifierArray: array]; } return qualifier; } - (BOOL)isQualifierForPrimaryKey: (EOQualifier *)qualifier { int count = [[self primaryKeyAttributeNames] count]; if (count == 1) { if ([qualifier isKindOfClass: [EOKeyValueQualifier class]] == YES) return YES; else return NO; } else { } //TODO NSEmitTODO(); //TODO [self notImplemented:_cmd]; return NO; } - (NSDictionary *)primaryKeyForRow: (NSDictionary *)row { NSMutableDictionary *dict = nil; int i, count; NSArray *primaryKeyAttributes = [self primaryKeyAttributes]; IMP pkaOAI=NULL; IMP rowOFK=NULL; IMP dictSOFK=NULL; count = [primaryKeyAttributes count]; dict = [NSMutableDictionary dictionaryWithCapacity: count]; for (i = 0; i < count; i++) { EOAttribute *attr = GDL2_ObjectAtIndexWithImpPtr(primaryKeyAttributes,&pkaOAI,i); NSString* attrName = [attr name]; id value = GDL2_ObjectForKeyWithImpPtr(row,&rowOFK,attrName); if (!value) value = GDL2_EONull; GDL2_SetObjectForKeyWithImpPtr(dict,&dictSOFK,value,attrName); } return dict; } - (BOOL)isValidAttributeUsedForLocking: (EOAttribute *)attribute { if (!([attribute isKindOfClass: GDL2_EOAttributeClass] && ([self attributeNamed: [attribute name]] == attribute))) return NO; if ([attribute isDerived]) return NO; return YES; } - (BOOL)isValidPrimaryKeyAttribute: (EOAttribute *)attribute { if (!([attribute isKindOfClass: GDL2_EOAttributeClass] && ([self attributeNamed: [attribute name]] == attribute))) return NO; if ([attribute isDerived]) return NO; return YES; } - (BOOL)isPrimaryKeyValidInObject: (id)object { NSArray *primaryKeyAttributeNames = nil; NSString *key = nil; id value = nil; NSUInteger i, count; BOOL isValid = YES; IMP pkanOAI=NULL; IMP objectVFK=NULL; primaryKeyAttributeNames = [self primaryKeyAttributeNames]; count = [primaryKeyAttributeNames count]; for (i = 0; i < count; i++) { key = GDL2_ObjectAtIndexWithImpPtr(primaryKeyAttributeNames,&pkanOAI,i); value = GDL2_ValueForKeyWithImpPtr(object,&objectVFK,key); // a 0 is NOT a valid PK value! -- dw if ((_isNilOrEONull(value)) || (([value isKindOfClass:[NSNumber class]]) && ([value intValue] == 0))) { return NO; } } return YES; } - (BOOL)isValidClassProperty: (id)property { id thePropertyName; if (!([property isKindOfClass: GDL2_EOAttributeClass] || [property isKindOfClass: [EORelationship class]])) return NO; thePropertyName = [(EOAttribute *)property name]; if ([[self attributesByName] objectForKey: thePropertyName] == property || [[self relationshipsByName] objectForKey: thePropertyName] == property) return YES; return NO; } - (NSArray *)subEntities { return _subEntities; } - (EOEntity *)parentEntity { return _parent; } - (BOOL)isAbstractEntity { return _flags.isAbstractEntity; } - (unsigned int)maxNumberOfInstancesToBatchFetch { return _batchCount; } - (EOGlobalID *)globalIDForRow: (NSDictionary *)row { EOGlobalID *gid = [self globalIDForRow: row isFinal: NO]; NSAssert(gid, @"No gid"); //TODO /* pas toutjur: la suite editingc objectForGlobalID: EODatabaseContext snapshotForGlobalID: if no snpashot: { database recordSnapshot:forGlobalID: self classDescriptionForInstances createInstanceWithEditingContext:globalID:zone: } */ return gid; } - (NSDictionary *)primaryKeyForGlobalID: (EOKeyGlobalID *)gid { //OK NSMutableDictionary *dictionaryForPrimaryKey = nil; if ([gid isKindOfClass: [EOKeyGlobalID class]]) //if ([gid isFinal])//?? or class test ??//TODO { NSArray *primaryKeyAttributeNames = [self primaryKeyAttributeNames]; int count = [primaryKeyAttributeNames count]; if (count > 0) { int i; id *gidkeyValues = [gid keyValues]; if (gidkeyValues) { IMP pkanOAI=NULL; IMP dfpkSOFK=NULL; dictionaryForPrimaryKey = [self _dictionaryForPrimaryKey]; NSAssert1(dictionaryForPrimaryKey, @"No dictionaryForPrimaryKey in entity %@", [self name]); for (i = 0; i < count; i++) { id key = GDL2_ObjectAtIndexWithImpPtr(primaryKeyAttributeNames,&pkanOAI,i); GDL2_SetObjectForKeyWithImpPtr(dictionaryForPrimaryKey,&dfpkSOFK, gidkeyValues[i],key); } } } } else { NSDebugLog(@"EOEntity (%@): primaryKey is *nil* for globalID = %@", _name, gid); } return dictionaryForPrimaryKey; } @end @implementation EOEntity (EOEntityEditing) - (void)setName: (NSString *)name { NSInteger fCount = -1; NSInteger i; EOModel * oldModel = nil; if (name && [name isEqual: _name]) return; [[self validateName: name] raise]; [self willChange]; RETAIN(self); ASSIGN(oldModel,_model); // We have to make sure all references are loaded before we change the name // if somebody finds a better solution, please tell me -- dw [_model referencesToProperty:self]; [_model removeEntity:self]; // update the fetch specifications if (_fetchSpecificationNames) { fCount = [_fetchSpecificationNames count]; } for (i = 0; i < fCount; i++) { EOFetchSpecification * fetchSpec = [self fetchSpecificationNamed:[_fetchSpecificationNames objectAtIndex:i]]; [fetchSpec setEntityName:name]; } ASSIGNCOPY(_name, name); [oldModel addEntity:self]; RELEASE(oldModel); RELEASE(self); [self _setIsEdited]; // this destroys everything. -- dw //[_model _updateCache]; } - (void)setExternalName: (NSString *)name { //OK EOFLOGObjectLevelArgs(@"EOEntity", @"entity %p (%@): external name=%@", self, [self name], name); [self willChange]; ASSIGNCOPY(_externalName,name); [self _setIsEdited]; } - (void)setExternalQuery: (NSString *)query { //OK [self willChange]; ASSIGNCOPY(_externalQuery, query); [self _setIsEdited]; } - (void)setRestrictingQualifier: (EOQualifier *)qualifier { [self willChange]; ASSIGN(_restrictingQualifier, qualifier); } - (void)setReadOnly: (BOOL)flag { //OK [self willChange]; _flags.isReadOnly = flag; } - (void)setCachesObjects: (BOOL)flag { //OK [self willChange]; _flags.cachesObjects = flag; } - (void)addAttribute: (EOAttribute *)attribute { NSString *attributeName = [attribute name]; NSAssert2([[self attributesByName] objectForKey: attributeName] == nil, @"'%@': attribute '%@' already used in the entity", [self name], attributeName); NSAssert2([[self relationshipsByName] objectForKey: attributeName] == nil, @"'%@': attribute '%@' already used in entity as relationship", [self name], attributeName); NSAssert4([attribute parent] == nil, @"'%@': attribute '%@' already owned by '%@' '%@'", [self name], attributeName, NSStringFromClass([[attribute parent] class]), [(EOEntity *)[attribute parent] name]); [self willChange]; [_attributes addObject: attribute]; [self _setIsEdited]; //To clean caches [attribute setParent: self]; } /** * Removes the attribute from the -attributes array, and the * -classProperties, and -primaryKeyAttributes arrays if they contain it. * does not remove any references to the attribute from other properties. * the caller should insure that no such references exist by calling * -referencesProperty: or [EOModel -referencesToProperty:]. */ - (void) removeAttribute: (EOAttribute *)attribute { if (attribute) { [self willChange]; // make sure everything is initialized [self attributes]; [self classProperties]; [self attributesUsedForLocking]; [self primaryKeyAttributes]; [_attributesByName removeObjectForKey:[attribute name]]; [_classProperties removeObject: attribute]; [_attributesUsedForLocking removeObject: attribute]; [_primaryKeyAttributes removeObject:attribute]; [attribute setParent: nil]; [_attributes removeObject: attribute]; [self _setIsEdited];//To clean caches } } - (void)addRelationship: (EORelationship *)relationship { NSString *relationshipName = [relationship name]; if ([[self attributesByName] objectForKey: relationshipName]) [NSException raise: NSInvalidArgumentException format: @"%@ -- %@ 0x%x: \"%@\" already used in the model as attribute", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self, relationshipName]; if ([[self relationshipsByName] objectForKey: relationshipName]) [NSException raise: NSInvalidArgumentException format: @"%@ -- %@ 0x%x: \"%@\" already used in the model", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self, relationshipName]; [self willChange]; [_relationships addObject: relationship]; if (_relationshipsByName == nil) { _relationshipsByName = [NSMutableDictionary new]; } [_relationshipsByName setObject: relationship forKey: relationshipName]; [relationship setEntity: self]; [self _setIsEdited];//To clean caches } /** * Removes the relationship from the -relationships array and * the -classProperties array if it contains the relationship. * The caller should insure that no references to the * relationship exist by calling -referencesProperty: or * [EOModel -referencesToProperty]. */ - (void)removeRelationship: (EORelationship *)relationship { if (relationship) { [self willChange]; [self relationships]; [self classProperties]; [_relationshipsByName removeObjectForKey:[relationship name]]; [_classProperties removeObject: relationship]; [_relationships removeObject: relationship]; /* We call this after adjusting the arrays so that setEntity: has the opportunity to check the relationships before calling removeRelationshipt which would lead to an infinite loop. */ [relationship setEntity:nil]; [self _setIsEdited];//To clean caches } } - (void)addFetchSpecification: (EOFetchSpecification *)fetchSpec withName: (NSString *)name { if (_fetchSpecificationDictionary == nil) { _fetchSpecificationDictionary = [NSMutableDictionary new]; } [self willChange]; [_fetchSpecificationDictionary setObject: fetchSpec forKey: name]; ASSIGN(_fetchSpecificationNames, [[_fetchSpecificationDictionary allKeys] sortedArrayUsingSelector: @selector(compare:)]); } - (void)removeFetchSpecificationNamed: (NSString *)name { [self willChange]; [_fetchSpecificationDictionary removeObjectForKey:name]; ASSIGN(_fetchSpecificationNames, [[_fetchSpecificationDictionary allKeys] sortedArrayUsingSelector: @selector(compare:)]); } - (void)setSharedObjectFetchSpecificationsByName: (NSArray *)names { NSEmitTODO(); //TODO [self notImplemented:_cmd]; } - (void)addSharedObjectFetchSpecificationByName: (NSString *)name { NSEmitTODO(); //TODO [self notImplemented:_cmd]; } - (void)removeSharedObjectFetchSpecificationByName: (NSString *)name { NSEmitTODO(); //TODO [self notImplemented:_cmd]; } - (void)setClassName:(NSString *)name { //OK [self willChange]; if (!name) { NSLog(@"Entity %@ has no class name. Use EOGenericRecord", [self name]); name = @"EOGenericRecord"; } ASSIGNCOPY(_className, name); [self _setIsEdited]; } - (void)setUserInfo: (NSDictionary *)dictionary { //OK [self willChange]; ASSIGN(_userInfo, dictionary); [self _setIsEdited]; } - (BOOL)setClassProperties: (NSArray *)properties { int i, count = [properties count]; for (i = 0; i < count; i++) if (![self isValidClassProperty: [properties objectAtIndex:i]]) return NO; [self willChange]; DESTROY(_classProperties); _classProperties = [[NSMutableArray alloc] initWithArray: properties]; //TODO [self _setIsEdited]; //To clean cache return YES; } - (BOOL)setPrimaryKeyAttributes: (NSArray *)keys { int i, count = [keys count]; for (i = 0; i < count; i++) if (![self isValidPrimaryKeyAttribute: [keys objectAtIndex:i]]) return NO; [self willChange]; DESTROY(_primaryKeyAttributes); _primaryKeyAttributes = [[NSMutableArray alloc] initWithArray: keys]; // TODO [self _setIsEdited];//To clean cache return YES; } - (BOOL) setAttributesUsedForLocking: (NSArray *)attributes { int i, count = [attributes count]; for (i = 0; i < count; i++) if (![self isValidAttributeUsedForLocking: [attributes objectAtIndex: i]]) return NO; [self willChange]; DESTROY(_attributesUsedForLocking); _attributesUsedForLocking = [[NSMutableArray alloc] initWithArray: attributes]; [self _setIsEdited]; //To clean cache return YES; } - (NSException *)validateName: (NSString *)name { const char *p, *s = [name cString]; int exc = 0; NSArray *storedProcedures; if ([_name isEqual:name]) return nil; if (!name || ![name length]) exc++; if (!exc) { p = s; while (*p) { if (!isalnum(*p) && *p != '@' && *p != '#' && *p != '_' && *p != '$') { exc++; break; } p++; } if (!exc && *s == '$') exc++; if (exc) return [NSException exceptionWithName: NSInvalidArgumentException reason: [NSString stringWithFormat:@"%@ -- %@ 0x%x: argument \"%@\" contains invalid char '%c'", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self, name, *p] userInfo: nil]; if ([_model entityNamed: name]) exc++; else if ((storedProcedures = [[self model] storedProcedures])) { NSEnumerator *stEnum = [storedProcedures objectEnumerator]; EOStoredProcedure *st; while ((st = [stEnum nextObject])) { NSEnumerator *attrEnum; EOAttribute *attr; attrEnum = [[st arguments] objectEnumerator]; while ((attr = [attrEnum nextObject])) { if ([name isEqualToString: [attr name]]) { exc++; break; } } if (exc) break; } } } if (exc) { return [NSException exceptionWithName: NSInvalidArgumentException reason: [NSString stringWithFormat: @"%@ -- %@ 0x%x: \"%@\" already used in the model", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self, name] userInfo: nil]; } return nil; } - (void)addSubEntity: (EOEntity *)child { [self willChange]; [_subEntities addObject: child]; [[child parentEntity] removeSubEntity:child]; [child _setParentEntity: self]; } - (void)removeSubEntity: (EOEntity *)child { [self willChange]; if ([child parentEntity] == self) [child _setParentEntity: nil]; [_subEntities removeObject: child]; } - (void)setIsAbstractEntity: (BOOL)flag { //OK [self willChange]; _flags.isAbstractEntity = flag; } - (void)setMaxNumberOfInstancesToBatchFetch: (unsigned int)size { [self willChange]; _batchCount = size; } @end @implementation EOEntity (EOModelReferentialIntegrity) - (BOOL)referencesProperty: (id)property { NSEnumerator *enumerator; EORelationship *rel; EOAttribute *attr; IMP enumNO=NULL; enumerator = [[self attributes] objectEnumerator]; enumNO=NULL; while ((attr = GDL2_NextObjectWithImpPtr(enumerator,&enumNO))) { if ([attr isFlattened] && [[attr realAttribute] isEqual: property]) return YES; } enumerator = [[self relationships] objectEnumerator]; enumNO=NULL; while ((rel = GDL2_NextObjectWithImpPtr(enumerator,&enumNO))) { if ([rel referencesProperty: property]) return YES; } return NO; } - (NSArray *)externalModelsReferenced { NSEmitTODO(); //TODO return nil; // TODO } @end @implementation EOEntity (EOModelBeautifier) - (void)beautifyName { //VERIFY NSString *name = [self name]; [self setName: name]; [[self attributes] makeObjectsPerformSelector: @selector(beautifyName)]; [[self relationships] makeObjectsPerformSelector: @selector(beautifyName)]; [[self flattenedAttributes] makeObjectsPerformSelector: @selector(beautifyName)]; //Turbocat: /* // Make the entity name and all of its components conform // to the Next naming style // NAME -> name, FIRST_NAME -> firstName + NSArray *listItems; NSString *newString=[NSString string]; int anz,i; EOFLOGObjectFnStartOrCond2(@"ModelingClasses",@"EOEntity"); // 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 ergaenzen um alle components (attributes, ...) // Exception abfangen NS_DURING [self setName:newString]; NS_HANDLER NSLog(@"%@ in Class: EOEntity , Method: beautifyName >> error : %@",[localException name],[localException reason]); NS_ENDHANDLER } EOFLOGObjectFnStopOrCond2(@"ModelingClasses",@"EOEntity"); */ } @end @implementation EOEntity (GDL2Extenstions) - (NSString *)docComment { return _docComment; } - (void)setDocComment: (NSString *)docComment { //OK [self willChange]; ASSIGNCOPY(_docComment, docComment); [self _setIsEdited]; } @end @implementation EOEntity (EOStoredProcedures) - (EOStoredProcedure *)storedProcedureForOperation: (NSString *)operation { return [_storedProcedures objectForKey: operation]; } - (void)setStoredProcedure: (EOStoredProcedure *)storedProcedure forOperation: (NSString *)operation { [self willChange]; if (!_storedProcedures) { ASSIGN(_storedProcedures, [NSMutableDictionary dictionary]); } if (storedProcedure != nil) { [_storedProcedures setObject:storedProcedure forKey:operation]; } else { [_storedProcedures removeObjectForKey:operation]; } } @end @implementation EOEntity (EOPrimaryKeyGeneration) - (NSString *)primaryKeyRootName { if (_parent) return [_parent primaryKeyRootName]; return _externalName; } @end @implementation EOEntity (EOEntityClassDescription) - (EOClassDescription *)classDescriptionForInstances { // EOFLOGObjectLevelArgs(@"EOEntity", @"in classDescriptionForInstances"); EOFLOGObjectLevelArgs(@"EOEntity", @"_classDescription=%@", _classDescription); if (!_classDescription) { _classDescription = [[EOEntityClassDescription alloc] initWithEntity: self]; //NO ? NotifyCenter addObserver:EOEntityClassDescription selector:_eoNowMultiThreaded: name:NSWillBecomeMultiThreadedNotification object:nil } return _classDescription; } @end /** Useful private methods made public in GDL2 **/ @implementation EOEntity (EOEntityGDL2Additions) /** Returns attribute (if any) for path **/ - (EOAttribute*) attributeForPath: (NSString*)path { //OK EOAttribute *attribute = nil; NSArray *pathElements = nil; NSString *part = nil; EOEntity *entity = self; int i, count = 0; EOFLOGObjectLevelArgs(@"EOEntity", @"path=%@", path); pathElements = [path componentsSeparatedByString: @"."]; EOFLOGObjectLevelArgs(@"EOEntity", @"pathElements=%@", pathElements); count = [pathElements count]; for (i = 0; i < count - 1; i++) { EORelationship *rel = nil; part = [pathElements objectAtIndex: i]; EOFLOGObjectLevelArgs(@"EOEntity", @"i=%d part=%@", i, part); rel = [entity anyRelationshipNamed: part]; NSAssert2(rel, @"no relationship named %@ in entity %@", part, [entity name]); EOFLOGObjectLevelArgs(@"EOEntity", @"i=%d part=%@ rel=%@", i, part, rel); entity = [rel destinationEntity]; EOFLOGObjectLevelArgs(@"EOEntity", @"entity name=%@", [entity name]); } part = [pathElements lastObject]; EOFLOGObjectLevelArgs(@"EOEntity", @"part=%@", part); attribute = [entity anyAttributeNamed: part]; EOFLOGObjectLevelArgs(@"EOEntity", @"resulting attribute=%@", attribute); return attribute; } /** Returns relationship (if any) for path **/ - (EORelationship*) relationshipForPath: (NSString*)path { //OK ? EORelationship *relationship = nil; EOEntity *entity = self; NSArray *pathElements = nil; int i, count; EOFLOGObjectLevelArgs(@"EOEntity", @"path=%@", path); pathElements = [path componentsSeparatedByString: @"."]; count = [pathElements count]; for (i = 0; i < count; i++) { NSString *part = [pathElements objectAtIndex: i]; relationship = [entity anyRelationshipNamed: part]; EOFLOGObjectLevelArgs(@"EOEntity", @"i=%d part=%@ rel=%@", i, part, relationship); if (relationship) { entity = [relationship destinationEntity]; EOFLOGObjectLevelArgs(@"EOEntity", @"entity name=%@", [entity name]); } else if (i < (count - 1)) // Not the last part { NSAssert2(relationship, @"no relationship named %@ in entity %@", part, [entity name]); } } EOFLOGObjectLevelArgs(@"EOEntity", @"relationship=%@", relationship); return relationship; } @end @implementation EOEntity (EOEntityPrivate) /* private method for finding out if there is an attribute with a name without triggering lazy loading */ - (BOOL) _hasAttributeNamed:(NSString *)name { return [[_attributes valueForKey:@"name"] containsObject:name]; } - (BOOL)isPrototypeEntity { [self notImplemented:_cmd]; return NO; // TODO } /* throws an exception if _model is not nil, and the model argument is not * identical to the _model ivar. As a special case EOModel -removeEntity: * is allowed to call this with a nil model, but removeEntity: is responsible * for any bookeeping. * * in other words, this method should not be used to change an entity's model. */ - (void)_setModel: (EOModel *)model { EOFLOGObjectLevelArgs(@"EOEntity", @"setModel=%p", model); NSAssert4(!_attributesToFetch || [_attributesToFetch isKindOfClass:[NSArray class]], @"entity %@ attributesToFetch %p is not an NSArray but a %@\n%@", [self name], _attributesToFetch, [_attributesToFetch class], _attributesToFetch); NSAssert3((_model == nil || _model == model || model == nil), @"Attempt to set entity: %@ owned by model: %@ to model: @%.", [self name], [_model name], [model name]); _model = model; } /* only for private usage of -addSubEntity: and -removeSubEntity: */ - (void)_setParentEntity: (EOEntity *)parent { [self willChange]; // TODO: verify _parent = parent; } - (NSDictionary *)snapshotForRow: (NSDictionary *)aRow { NSArray *array = [self attributesUsedForLocking]; int i, n = [array count]; NSMutableDictionary *dict = AUTORELEASE([GDL2_alloc(NSMutableDictionary) initWithCapacity: n]); IMP arrayOAI=NULL; IMP dictSOFK=NULL; IMP aRowOFK=NULL; for (i = 0; i < n; i++) { id key = [(EOAttribute *)GDL2_ObjectAtIndexWithImpPtr(array,&arrayOAI,i) name]; GDL2_SetObjectForKeyWithImpPtr(dict,&dictSOFK, GDL2_ObjectForKeyWithImpPtr(aRow,&aRowOFK,key), key); } return dict; } - (Class)_classForInstances { if (!_classForInstances) { NSString *className = nil; Class objectClass = Nil; className = [self className]; EOFLOGObjectLevelArgs(@"EOEntity", @"className=%@", className); objectClass = NSClassFromString(className); if (!objectClass) { NSLog(@"Error: No class named %@", className); } else { EOFLOGObjectLevelArgs(@"EOEntity", @"objectClass=%@", objectClass); ASSIGN(_classForInstances, objectClass); } } return _classForInstances; } - (void)_setInternalInfo: (NSDictionary *)dictionary { //OK [self willChange]; ASSIGN(_internalInfo, dictionary); [self _setIsEdited]; } - (id) globalIDForRow: (NSDictionary*)row isFinal: (BOOL)isFinal { EOKeyGlobalID *globalID = nil; NSArray *primaryKeyAttributeNames = nil; int count = 0; NSAssert([row count] > 0, @"Empty Row."); primaryKeyAttributeNames = [self primaryKeyAttributeNames]; count = [primaryKeyAttributeNames count]; { id keyArray[count]; int i; IMP rowOFK=NULL; IMP pkanOAI=NULL; memset(keyArray, 0, sizeof(id) * count); for (i = 0; i < count; i++) { keyArray[i] = GDL2_ObjectForKeyWithImpPtr(row,&rowOFK, GDL2_ObjectAtIndexWithImpPtr(primaryKeyAttributeNames, &pkanOAI,i)); } globalID = [EOKeyGlobalID globalIDWithEntityName: [self name] keys: keyArray keyCount: count zone: [self zone]]; } //NSEmitTODO(); //TODO //TODO isFinal ?? return globalID; } -(Class)classForObjectWithGlobalID: (EOKeyGlobalID*)globalID { //near OK Class classForInstances = _classForInstances; //TODO:use globalID ?? if (!classForInstances) { classForInstances = [self _classForInstances]; } return _classForInstances; } //DESTROY v later because it may be still in use #define AUTORELEASE_SETNIL(v) { AUTORELEASE(v); v=nil; } - (void)_setIsEdited { if(_flags.updating) return; EOFLOGObjectLevelArgs(@"EOEntity", @"START entity name=%@", [self name]); NSAssert4(!_attributesToFetch || [_attributesToFetch isKindOfClass: [NSArray class]], @"entity %@ attributesToFetch %p is not an NSArray but a %@\n%@", [self name], _attributesToFetch, [_attributesToFetch class], _attributesToFetch); //Destroy cached ivar EOFLOGObjectLevelArgs(@"EOEntity", @"_classPropertyNames: void:%p [%p] %s", (void*)nil, (void*)_classPropertyNames, (_classPropertyNames ? "Not NIL" : "NIL")); AUTORELEASE_SETNIL(_classPropertyNames); EOFLOGObjectLevelArgs(@"EOEntity", @"_primaryKeyAttributeNames: %p %s", (void*)_primaryKeyAttributeNames, (_primaryKeyAttributeNames ? "Not NIL" : "NIL")); AUTORELEASE_SETNIL(_primaryKeyAttributeNames); EOFLOGObjectLevelArgs(@"EOEntity", @"_classPropertyAttributeNames: %p %s", _classPropertyAttributeNames, (_classPropertyAttributeNames ? "Not NIL" : "NIL")); AUTORELEASE_SETNIL(_classPropertyAttributeNames); EOFLOGObjectLevelArgs(@"EOEntity", @"_classPropertyToOneRelationshipNames: %p %s", _classPropertyToOneRelationshipNames, (_classPropertyToOneRelationshipNames ? "Not NIL" : "NIL")); AUTORELEASE_SETNIL(_classPropertyToOneRelationshipNames); EOFLOGObjectLevelArgs(@"EOEntity", @"_classPropertyToManyRelationshipNames: %p %s", _classPropertyToManyRelationshipNames, (_classPropertyToManyRelationshipNames ? "Not NIL" : "NIL")); AUTORELEASE_SETNIL(_classPropertyToManyRelationshipNames); EOFLOGObjectLevelArgs(@"EOEntity", @"_attributesToFetch: %p %s", _attributesToFetch, (_attributesToFetch ? "Not NIL" : "NIL")); AUTORELEASE_SETNIL(_attributesToFetch); EOFLOGObjectLevelArgs(@"EOEntity", @"_dbSnapshotKeys: %p %s", _dbSnapshotKeys, (_dbSnapshotKeys ? "Not NIL" : "NIL")); AUTORELEASE_SETNIL(_dbSnapshotKeys); EOFLOGObjectLevelArgs(@"EOEntity", @"_attributesToSave: %p %s", _attributesToSave, (_attributesToSave ? "Not NIL" : "NIL")); AUTORELEASE_SETNIL(_attributesToSave); EOFLOGObjectLevelArgs(@"EOEntity", @"_propertiesToFault: %p %s", _propertiesToFault, (_propertiesToFault ? "Not NIL" : "NIL")); AUTORELEASE_SETNIL(_propertiesToFault); EOFLOGObjectLevelArgs(@"EOEntity", @"_adaptorDictionaryInitializer: %p %s", _adaptorDictionaryInitializer, (_adaptorDictionaryInitializer ? "Not NIL" : "NIL")); AUTORELEASE_SETNIL(_adaptorDictionaryInitializer); EOFLOGObjectLevelArgs(@"EOEntity", @"_snapshotDictionaryInitializer: %p %s", _snapshotDictionaryInitializer, (_snapshotDictionaryInitializer ? "Not NIL" : "NIL")); AUTORELEASE_SETNIL(_snapshotDictionaryInitializer); EOFLOGObjectLevelArgs(@"EOEntity",@"_primaryKeyDictionaryInitializer: %p %s", _primaryKeyDictionaryInitializer, (_primaryKeyDictionaryInitializer ? "Not NIL" : "NIL")); AUTORELEASE_SETNIL(_primaryKeyDictionaryInitializer); EOFLOGObjectLevelArgs(@"EOEntity",@"_propertyDictionaryInitializer: %p %s", _propertyDictionaryInitializer, (_propertyDictionaryInitializer ? "Not NIL" : "NIL")); AUTORELEASE_SETNIL(_propertyDictionaryInitializer); EOFLOGObjectLevelArgs(@"EOEntity",@"_instanceDictionaryInitializer: %p %s", _instanceDictionaryInitializer, (_instanceDictionaryInitializer ? "Not NIL" : "NIL")); AUTORELEASE_SETNIL(_instanceDictionaryInitializer); EOFLOGObjectLevelArgs(@"EOEntity",@"_relationshipsByName: %p %s", _relationshipsByName, (_relationshipsByName ? "Not NIL" : "NIL")); AUTORELEASE_SETNIL(_relationshipsByName); AUTORELEASE_SETNIL(_attributesByName); //TODO call _flushCache on each attr NSAssert4(!_attributesToFetch || [_attributesToFetch isKindOfClass: [NSArray class]], @"entity %@ attributesToFetch %p is not an NSArray but a %@\n%@", [self name], _attributesToFetch, [_attributesToFetch class], _attributesToFetch); EOFLOGObjectLevelArgs(@"EOEntity", @"STOP%s", ""); } /** Returns attributes by name (only attributes, not relationships) **/ - (NSDictionary*)attributesByName { if (_flags.attributesIsLazy) [self attributes]; if (!_attributesByName) { unsigned int i, c; _attributesByName = [[NSMutableDictionary alloc] init]; for (i = 0, c = [_attributes count]; i < c; i++) { [_attributesByName setObject:[_attributes objectAtIndex:i] forKey:[[_attributes objectAtIndex:i] name]]; } } return _attributesByName; } - (NSDictionary*)relationshipsByName { if (_flags.relationshipsIsLazy) [self relationships]; if (!_relationshipsByName) { unsigned int i, c; _relationshipsByName = [[NSMutableDictionary alloc] init]; for (i = 0, c = [_relationships count]; i < c; i++) { [_relationshipsByName setObject:[_relationships objectAtIndex:i] forKey:[[_relationships objectAtIndex:i] name]]; } } return _relationshipsByName; } - (NSArray*) _allFetchSpecifications { //OK NSDictionary *fetchSpecificationDictionary = [self _fetchSpecificationDictionary]; NSArray *fetchSpecValues = [fetchSpecificationDictionary allValues]; return fetchSpecValues; } - (NSDictionary*) _fetchSpecificationDictionary { if ((!_fetchSpecificationDictionary) && (_model)) { ASSIGN(_fetchSpecificationDictionary, [_model _loadFetchSpecificationDictionaryForEntityNamed:_name]); } return _fetchSpecificationDictionary; } - (void) _loadEntity { [self attributes]; [self relationships]; [self _fetchSpecificationDictionary]; } - (id) parentRelationship { //TODO return [self notImplemented: _cmd]; } - (int) _numberOfRelationships { //OK return [[self relationships] count]; } - (BOOL) _hasReadOnlyAttributes { //OK BOOL hasReadOnlyAttributes = NO; NSArray *attributes = [self attributes]; int i, count=[attributes count]; for (i = 0; !hasReadOnlyAttributes && i < count; i++) hasReadOnlyAttributes = [[attributes objectAtIndex: i] isReadOnly]; return hasReadOnlyAttributes; } - (NSArray*) writableDBSnapshotKeys { //OK NSArray *writableDBSnapshotKeys=nil; if (![self isReadOnly]) { NSArray *attributesToFetch = [self _attributesToFetch]; int i, count = [attributesToFetch count]; IMP atfOAI=NULL; IMP sAO=NULL; NSMutableArray* tmpArray = AUTORELEASE([GDL2_alloc(NSMutableArray) initWithCapacity: count]); NSAssert3(!attributesToFetch || [attributesToFetch isKindOfClass: [NSArray class]], @"entity %@ attributesToFetch is not an NSArray but a %@\n%@", [self name], [attributesToFetch class], attributesToFetch); for (i = 0; i < count; i++) { EOAttribute *attribute = GDL2_ObjectAtIndexWithImpPtr(attributesToFetch,&atfOAI,i); if (![attribute isReadOnly]) GDL2_AddObjectWithImpPtr(tmpArray,&sAO,[attribute name]); } writableDBSnapshotKeys=tmpArray; } else writableDBSnapshotKeys=GDL2_NSArray; return writableDBSnapshotKeys; } - (NSArray*) rootAttributesUsedForLocking { //OK ? NSArray *rootAttributesUsedForLocking = nil; NSArray *attributesUsedForLocking = [self attributesUsedForLocking]; int count = [attributesUsedForLocking count]; if (count>0) { int i=0; NSMutableArray *tmpArray = AUTORELEASE([GDL2_alloc(NSMutableArray) initWithCapacity: count]); IMP auflOAI=NULL; IMP tAO=NULL; for (i = 0; i < count; i++) { EOAttribute *attribute = GDL2_ObjectAtIndexWithImpPtr(attributesUsedForLocking, &auflOAI,i); if (![attribute isDerived]) GDL2_AddObjectWithImpPtr(tmpArray,&tAO,attribute); } rootAttributesUsedForLocking=tmpArray; } else rootAttributesUsedForLocking=GDL2_NSArray; return rootAttributesUsedForLocking; } - (BOOL) isSubEntityOf: (id)param0 { //TODO [self notImplemented: _cmd]; return NO; } - (id) initObject: (id)param0 editingContext: (id)param1 globalID: (id)param2 { //TODO return [self notImplemented: _cmd]; } - (id) allocBiggestObjectWithZone: (NSZone*)zone { //TODO return [self notImplemented: _cmd]; } - (Class) _biggestClass { //OK Class biggestClass = Nil; biggestClass = [self classForObjectWithGlobalID: nil]; return biggestClass; } - (NSArray*) relationshipsPlist { //OK NSMutableArray *relsPlist; if (_flags.relationshipsIsLazy) { relsPlist = _relationships; } else { NSArray *relationships; int relCount; relsPlist = [NSMutableArray array]; relationships = [self relationships]; relCount = [relationships count]; if (relCount > 0) { int i; for (i = 0; i < relCount; i++) { NSMutableDictionary *relPlist = [NSMutableDictionary dictionary]; EORelationship *rel = [relationships objectAtIndex: i]; [rel encodeIntoPropertyList: relPlist]; [relsPlist addObject: relPlist]; } } } return relsPlist; } - (id) rootParent { id prevParent = self; id parent = self; while (parent) { prevParent = parent; parent = [prevParent parentEntity]; } return prevParent; } - (void) _setParent: (id)param0 { //TODO [self notImplemented: _cmd]; } - (NSArray*) _hiddenRelationships { //OK if (!_hiddenRelationships) _hiddenRelationships = [NSMutableArray new]; return _hiddenRelationships; } - (NSArray*) _propertyNames { //OK NSMutableArray *propertyNames = nil; NSArray *attributes = [self attributes]; NSArray *attributeNames = [attributes resultsOfPerformingSelector: @selector(name)]; NSArray *relationships = [self relationships]; NSArray *relationshipNames = [relationships resultsOfPerformingSelector: @selector(name)]; propertyNames = [NSMutableArray arrayWithArray: attributeNames]; [propertyNames addObjectsFromArray: relationshipNames]; return propertyNames; } - (id) _flattenAttribute: (id)param0 relationshipPath: (id)param1 currentAttributes: (id)param2 { //TODO return [self notImplemented: _cmd]; } - (NSString*) snapshotKeyForAttributeName: (NSString*)attributeName { NSString *attName = [self _flattenedAttNameToSnapshotKeyMapping]; if (attName) { NSEmitTODO(); //TODO [self notImplemented: _cmd]; } else attName = attributeName; //TODO-VERIFY return attName; } - (id) _flattenedAttNameToSnapshotKeyMapping { // NSArray *attributesToSave = [self _attributesToSave]; //NSEmitTODO(); //TODO return nil; //[self notImplemented:_cmd]; //TODO } - (EOMKKDSubsetMapping*) _snapshotToAdaptorRowSubsetMapping { if (!_snapshotToAdaptorRowSubsetMapping) { EOMKKDInitializer *snapshotDictionaryInitializer = [self _snapshotDictionaryInitializer]; EOMKKDInitializer *adaptorDictionaryInitializer = [self _adaptorDictionaryInitializer]; EOMKKDSubsetMapping *subsetMapping = [snapshotDictionaryInitializer subsetMappingForSourceDictionaryInitializer: adaptorDictionaryInitializer]; ASSIGN(_snapshotToAdaptorRowSubsetMapping,subsetMapping); } return _snapshotToAdaptorRowSubsetMapping; } - (EOMutableKnownKeyDictionary*) _dictionaryForPrimaryKey { //OK EOMKKDInitializer *primaryKeyDictionaryInitializer = [self _primaryKeyDictionaryInitializer]; EOMutableKnownKeyDictionary *dictionaryForPrimaryKey = [EOMutableKnownKeyDictionary dictionaryWithInitializer: primaryKeyDictionaryInitializer]; return dictionaryForPrimaryKey; } - (EOMutableKnownKeyDictionary*) _dictionaryForProperties { //OK EOMKKDInitializer *propertyDictionaryInitializer = nil; EOMutableKnownKeyDictionary *dictionaryForProperties = nil; propertyDictionaryInitializer = [self _propertyDictionaryInitializer]; dictionaryForProperties = [EOMutableKnownKeyDictionary dictionaryWithInitializer: propertyDictionaryInitializer]; return dictionaryForProperties; } /** returns a new autoreleased mutable dictionary to store properties returns nil if there's no key in the instanceDictionaryInitializer **/ - (EOMutableKnownKeyDictionary*) _dictionaryForInstanceProperties { //OK EOMKKDInitializer *instanceDictionaryInitializer = nil; EOMutableKnownKeyDictionary *dictionaryForProperties = nil; instanceDictionaryInitializer = [self _instanceDictionaryInitializer]; // No need to build the dictionary if there's no key. // The only drawback I see is if someone use extraData feature of MKK dictionary if ([instanceDictionaryInitializer count]>0) { dictionaryForProperties = [EOMutableKnownKeyDictionary dictionaryWithInitializer: instanceDictionaryInitializer]; } return dictionaryForProperties; } - (NSArray*) _relationshipsToFaultForRow: (NSDictionary*)row { NSMutableArray *rels = [NSMutableArray array]; NSArray *classProperties = [self classProperties]; int i, count = [classProperties count]; for (i = 0; i < count; i++) { EORelationship *classProperty = [classProperties objectAtIndex: i]; if ([classProperty isKindOfClass: [EORelationship class]]) { EORelationship *relsubs = [classProperty _substitutionRelationshipForRow: row]; [rels addObject: relsubs]; } } return rels; } - (NSArray*) _classPropertyAttributes { //OK //IMPROVE We can improve this by caching the result.... NSArray *classPropertyAttributes = nil; //Get classProperties (EOAttributes + EORelationships) NSArray *classProperties = [self classProperties]; int count = [classProperties count]; if (count>0) { int i=0; NSMutableArray *tmpArray = AUTORELEASE([GDL2_alloc(NSMutableArray) initWithCapacity: count]); IMP cpOAI=NULL; IMP tAO=NULL; for (i = 0; i < count; i++) { id object = GDL2_ObjectAtIndexWithImpPtr(classProperties,&cpOAI,i); if ([object isKindOfClass: GDL2_EOAttributeClass]) GDL2_AddObjectWithImpPtr(tmpArray,&tAO,object); } classPropertyAttributes = tmpArray; } else classPropertyAttributes=GDL2_NSArray; return classPropertyAttributes; } - (NSArray*) _attributesToSave { //Near OK EOFLOGObjectLevelArgs(@"EOEntity", @"START Entity _attributesToSave entityname=%@", [self name]); if (!_attributesToSave) { NSArray *attributesToFetch = [self _attributesToFetch]; int i, count = [attributesToFetch count]; NSMutableArray *attributesToSave = [NSMutableArray arrayWithCapacity:count]; NSAssert3(!attributesToFetch || [attributesToFetch isKindOfClass: [NSArray class]], @"entity %@ attributesToFetch is not an NSArray but a %@\n%@", [self name], [_attributesToFetch class], _attributesToFetch); for (i = 0; i < count; i++) { EOAttribute *attribute = [attributesToFetch objectAtIndex: i]; BOOL isFlattened = [attribute isFlattened]; if (!isFlattened) [attributesToSave addObject: attribute]; } ASSIGN(_attributesToSave, attributesToSave); } EOFLOGObjectLevelArgs(@"EOEntity", @"STOP Entity _attributesToSave entityname=%@ attrs:%@", [self name], _attributesToSave); return _attributesToSave; } //sorted by name attributes - (NSArray*) _attributesToFetch { //Seems OK EOFLOGObjectLevelArgs(@"EOEntity", @"START Entity _attributesToFetch entityname=%@", [self name]); EOFLOGObjectLevelArgs(@"EOEntity", @"AttributesToFetch:%p", _attributesToFetch); EOFLOGObjectLevelArgs(@"EOEntity", @"AttributesToFetch:%@", _attributesToFetch); NSAssert2(!_attributesToFetch || [_attributesToFetch isKindOfClass: [NSArray class]], @"entity %@ attributesToFetch is not an NSArray but a %@", [self name], [_attributesToFetch class]); if (!_attributesToFetch) { NSMutableDictionary *attributesDict = [NSMutableDictionary dictionary]; NS_DURING { int iArray = 0; NSArray *arrays[] = { [self attributesUsedForLocking], [self primaryKeyAttributes], [self classProperties], [self relationships] }; _attributesToFetch = RETAIN([NSMutableArray array]); EOFLOGObjectLevelArgs(@"EOEntity", @"Entity %@ - _attributesToFetch %p [RC=%d]:%@", [self name], _attributesToFetch, [_attributesToFetch retainCount], _attributesToFetch); for (iArray = 0; iArray < 4; iArray++) { int i, count = 0; EOFLOGObjectLevelArgs(@"EOEntity", @"Entity %@ - arrays[iArray]:%@", [self name], arrays[iArray]); count = [arrays[iArray] count]; for (i = 0; i < count; i++) { id property = [arrays[iArray] objectAtIndex: i]; NSString *propertyName = [(EOAttribute*)property name]; //VERIFY EOFLOGObjectLevelArgs(@"EOEntity", @"propertyName=%@ - property=%@", propertyName, property); if ([property isKindOfClass: GDL2_EOAttributeClass]) { EOAttribute *attribute = property; if ([attribute isFlattened]) { attribute = [[attribute _definitionArray] objectAtIndex: 0]; propertyName = [attribute name]; } } if ([property isKindOfClass: [EORelationship class]]) { [self _addAttributesToFetchForRelationshipPath: [(EORelationship*)property relationshipPath] atts: attributesDict]; } else if ([property isKindOfClass: GDL2_EOAttributeClass]) { [attributesDict setObject: property forKey: propertyName]; } else { NSEmitTODO(); //TODO } } } } NS_HANDLER { NSDebugMLog(@"Exception: %@",localException); [localException raise]; } NS_ENDHANDLER; NS_DURING { NSDebugMLog(@"Attributes to fetch classes %@", [_attributesToFetch resultsOfPerformingSelector: @selector(class)]); [_attributesToFetch addObjectsFromArray: [attributesDict allValues]]; NSDebugMLog(@"Attributes to fetch classes %@", [_attributesToFetch resultsOfPerformingSelector: @selector(class)]); [_attributesToFetch sortUsingSelector: @selector(eoCompareOnName:)]; //Very important to have always the same order. } NS_HANDLER { NSDebugMLog(@"Exception: %@",localException); [localException raise]; } NS_ENDHANDLER; } NSAssert3(!_attributesToFetch || [_attributesToFetch isKindOfClass: [NSArray class]], @"Entity %@: _attributesToFetch is not an NSArray but a %@\n%@", [self name], [_attributesToFetch class], _attributesToFetch); EOFLOGObjectLevelArgs(@"EOEntity", @"Stop Entity %@ - _attributesToFetch %p [RC=%d]:%@", [self name], _attributesToFetch, [_attributesToFetch retainCount], _attributesToFetch); return _attributesToFetch; } - (EOMKKDInitializer*) _adaptorDictionaryInitializer { //OK EOFLOGObjectLevelArgs(@"EOEntity", @"Start _adaptorDictionaryInitializer=%@", _adaptorDictionaryInitializer); if (!_adaptorDictionaryInitializer) { NSArray *attributesToFetch = [self _attributesToFetch]; NSArray *attributeToFetchNames = [attributesToFetch resultsOfPerformingSelector: @selector(name)]; EOFLOGObjectLevelArgs(@"EOEntity", @"attributeToFetchNames=%@", attributeToFetchNames); NSAssert3(!attributesToFetch || [attributesToFetch isKindOfClass: [NSArray class]], @"entity %@ attributesToFetch is not an NSArray but a %@\n%@", [self name], [attributesToFetch class], attributesToFetch); NSAssert1([attributesToFetch count] > 0, @"No Attributes to fetch in entity %@", [self name]); NSAssert1([attributeToFetchNames count] > 0, @"No Attribute names to fetch in entity %@", [self name]); EOFLOGObjectLevelArgs(@"EOEntity", @"entity named %@: attributeToFetchNames=%@", [self name], attributeToFetchNames); ASSIGN(_adaptorDictionaryInitializer, [EOMutableKnownKeyDictionary initializerFromKeyArray: attributeToFetchNames]); EOFLOGObjectLevelArgs(@"EOEntity", @"entity named %@ _adaptorDictionaryInitializer=%@", [self name], _adaptorDictionaryInitializer); } EOFLOGObjectLevelArgs(@"EOEntity", @"Stop _adaptorDictionaryInitializer=%p", _adaptorDictionaryInitializer); EOFLOGObjectLevelArgs(@"EOEntity", @"Stop _adaptorDictionaryInitializer=%@", _adaptorDictionaryInitializer); return _adaptorDictionaryInitializer; } - (EOMKKDInitializer*) _snapshotDictionaryInitializer { if (!_snapshotDictionaryInitializer) { NSArray *dbSnapshotKeys = [self dbSnapshotKeys]; ASSIGN(_snapshotDictionaryInitializer, [EOMutableKnownKeyDictionary initializerFromKeyArray: dbSnapshotKeys]); } return _snapshotDictionaryInitializer; } - (EOMKKDInitializer*) _primaryKeyDictionaryInitializer { //OK if (!_primaryKeyDictionaryInitializer) { NSArray *primaryKeyAttributeNames = [self primaryKeyAttributeNames]; NSAssert1([primaryKeyAttributeNames count] > 0, @"No primaryKeyAttributeNames in entity %@", [self name]); EOFLOGObjectLevelArgs(@"EOEntity", @"entity named %@: primaryKeyAttributeNames=%@", [self name], primaryKeyAttributeNames); _primaryKeyDictionaryInitializer = [EOMKKDInitializer newWithKeyArray: primaryKeyAttributeNames]; EOFLOGObjectLevelArgs(@"EOEntity", @"entity named %@: _primaryKeyDictionaryInitializer=%@", [self name], _primaryKeyDictionaryInitializer); } return _primaryKeyDictionaryInitializer; } - (EOMKKDInitializer*) _propertyDictionaryInitializer { //OK // If not already built, built it if (!_propertyDictionaryInitializer) { // Get class properties (EOAttributes + EORelationships) NSArray *classProperties = [self classProperties]; NSArray *classPropertyNames = [classProperties resultsOfPerformingSelector: @selector(name)]; EOFLOGObjectLevelArgs(@"EOEntity", @"entity %@ classPropertyNames=%@", [self name], classPropertyNames); NSAssert1([classProperties count] > 0, @"No classProperties in entity %@", [self name]); NSAssert1([classPropertyNames count] > 0, @"No classPropertyNames in entity %@", [self name]); //Build the multiple known key initializer _propertyDictionaryInitializer = [EOMKKDInitializer newWithKeyArray: classPropertyNames]; } return _propertyDictionaryInitializer; } - (EOMKKDInitializer*) _instanceDictionaryInitializer { //OK // If not already built, built it if (!_instanceDictionaryInitializer) { // Get class properties (EOAttributes + EORelationships) NSArray *classProperties = [self classProperties]; NSArray* excludedPropertyNames=nil; NSArray *classPropertyNames = nil; Class classForInstances = [self _classForInstances]; classPropertyNames = [classProperties resultsOfPerformingSelector: @selector(name)]; EOFLOGObjectLevelArgs(@"EOEntity", @"entity %@ classPropertyNames=%@", [self name], classPropertyNames); excludedPropertyNames = [classForInstances _instanceDictionaryInitializerExcludedPropertyNames]; EOFLOGObjectLevelArgs(@"EOEntity", @"entity %@ excludedPropertyNames=%@", [self name], excludedPropertyNames); if ([excludedPropertyNames count]>0) { NSMutableArray* mutableClassPropertyNames=[classPropertyNames mutableCopy]; [mutableClassPropertyNames removeObjectsInArray:excludedPropertyNames]; classPropertyNames=AUTORELEASE(mutableClassPropertyNames); } EOFLOGObjectLevelArgs(@"EOEntity", @"entity %@ classPropertyNames=%@", [self name], classPropertyNames); NSAssert1([classProperties count] > 0, @"No classProperties in entity %@", [self name]); NSAssert1([classPropertyNames count] > 0, @"No classPropertyNames in entity %@", [self name]); //Build the multiple known key initializer _instanceDictionaryInitializer = [EOMKKDInitializer newWithKeyArray: classPropertyNames]; EOFLOGObjectLevelArgs(@"EOEntity", @"_instanceDictionaryInitializer=%@", _instanceDictionaryInitializer); } return _instanceDictionaryInitializer; } @end @implementation EOEntity (EOEntityRelationshipPrivate) - (EORelationship*) _inverseRelationshipPathForPath: (NSString*)path { //TODO return [self notImplemented: _cmd]; } - (NSDictionary*) _keyMapForIdenticalKeyRelationshipPath: (NSString*)path { NSDictionary *keyMap = nil; EORelationship *rel; NSMutableArray *sourceAttributeNames = [NSMutableArray array]; NSMutableArray *destinationAttributeNames = [NSMutableArray array]; NSArray *joins; int count = 0; //use path,not only one element ? rel = [self relationshipNamed: path]; joins = [rel joins]; count = [joins count]; if (count>0) { int i=0; IMP joinsOAI=NULL; IMP sanAO=NULL; IMP danAO=NULL; for (i = 0; i < count; i++) { EOJoin *join = GDL2_ObjectAtIndexWithImpPtr(joins,&joinsOAI,i); EOAttribute *sourceAttribute = [join sourceAttribute]; EOAttribute *destinationAttribute = [self _mapAttribute:sourceAttribute toDestinationAttributeInLastComponentOfRelationshipPath: path]; GDL2_AddObjectWithImpPtr(sourceAttributeNames,&sanAO, [sourceAttribute name]); GDL2_AddObjectWithImpPtr(destinationAttributeNames,&danAO, [destinationAttribute name]); } }; keyMap = [NSDictionary dictionaryWithObjectsAndKeys: sourceAttributeNames, @"sourceKeys", destinationAttributeNames, @"destinationKeys", nil, nil]; //return something like {destinationKeys = (code); sourceKeys = (languageCode); } return keyMap; } - (EOAttribute *)_mapAttribute: (EOAttribute *)attribute toDestinationAttributeInLastComponentOfRelationshipPath: (NSString *)path { NSArray *components = nil; EORelationship *rel = nil; NSArray *sourceAttributes = nil; NSArray *destinationAttributes = nil; EOEntity *destinationEntity = nil; NSAssert(attribute, @"No attribute"); NSAssert(path, @"No path"); NSAssert([path length] > 0, @"Empty path"); components = [path componentsSeparatedByString: @"."]; NSAssert([components count] > 0, @"Empty components array"); rel = [self relationshipNamed: [components lastObject]]; sourceAttributes = [rel sourceAttributes]; destinationAttributes = [rel destinationAttributes]; destinationEntity = [rel destinationEntity]; NSEmitTODO(); //TODO return [self notImplemented: _cmd]; } - (BOOL) _relationshipPathIsToMany: (NSString*)relPath { //Seems OK BOOL isToMany = NO; NSArray *parts = [relPath componentsSeparatedByString: @"."]; EOEntity *entity = self; int i, count = [parts count]; for (i = 0 ; !isToMany && i < count; i++) //VERIFY Stop when finding the 1st isToMany ? { EORelationship *rel = [entity relationshipNamed: [parts objectAtIndex: i]]; isToMany = [rel isToMany]; if (!isToMany) entity = [rel destinationEntity]; } return isToMany; } - (BOOL) _relationshipPathHasIdenticalKeys: (id)param0 { [self notImplemented: _cmd]; return NO; } - (NSDictionary *)_keyMapForRelationshipPath: (NSString *)path { //Ayers: Review // what if relationshipPathIsMultiHop (foo.bar.joe)? //NearOK NSMutableArray *sourceKeys = [NSMutableArray array]; NSMutableArray *destinationKeys = [NSMutableArray array]; //NSArray *attributesToFetch = [self _attributesToFetch]; //Use It !! EORelationship *relationship = [self anyRelationshipNamed: path]; //?? iterate on path ? //TODO NSEmitTODO(); //TODO if (relationship) { NSArray *joins = [relationship joins]; int count = [joins count]; if (count>0) { int i=0; IMP joinsOAI=NULL; IMP skAO=NULL; IMP dkAO=NULL; for(i = 0; i < count; i++) { EOJoin *join = GDL2_ObjectAtIndexWithImpPtr(joins,&joinsOAI,i); EOAttribute *sourceAttribute = [join sourceAttribute]; EOAttribute *destinationAttribute = [join destinationAttribute]; GDL2_AddObjectWithImpPtr(sourceKeys,&skAO,[sourceAttribute name]); GDL2_AddObjectWithImpPtr(destinationKeys,&dkAO,[destinationAttribute name]); } }; } return [NSDictionary dictionaryWithObjectsAndKeys: sourceKeys, @"sourceKeys", destinationKeys, @"destinationKeys", nil]; //{destinationKeys = (code); sourceKeys = (countryCode); } } @end @implementation EOEntity (EOEntitySQLExpression) - (NSString*) valueForSQLExpression: (EOSQLExpression*)sqlExpression { return [self notImplemented: _cmd]; //TODO } + (NSString*) valueForSQLExpression: (EOSQLExpression*)sqlExpression { return [self notImplemented: _cmd]; //TODO } @end @implementation EOEntity (MethodSet11) - (NSException *)validateObjectForDelete: (id)object { //OK ?? NSArray *relationships = nil; NSEnumerator *relEnum = nil; EORelationship *relationship = nil; NSMutableArray *expArray = nil; relationships = [self relationships]; relEnum = [relationships objectEnumerator]; while ((relationship = [relEnum nextObject])) { //classproperties //rien pour nullify if ([relationship deleteRule] == EODeleteRuleDeny) { if (!expArray) expArray = [NSMutableArray arrayWithCapacity:5]; [expArray addObject: [NSException validationExceptionWithFormat: @"delete operation for relationship key %@ refused", [relationship name]]]; } } if (expArray) return [NSException aggregateExceptionWithExceptions:expArray]; else return nil; } /** Retain an array of name of all EOAttributes **/ - (NSArray*) classPropertyAttributeNames { //Should be OK if (!_classPropertyAttributeNames) { int i=0; NSArray *classProperties = [self classProperties]; int count = [classProperties count]; _classPropertyAttributeNames = [NSMutableArray new]; for (i = 0; i < count; i++) { EOAttribute *property = [classProperties objectAtIndex: i]; if ([property isKindOfClass: GDL2_EOAttributeClass]) [(NSMutableArray*)_classPropertyAttributeNames addObject: [property name]]; }; EOFLOGObjectLevelArgs(@"EOEntity", @"_classPropertyAttributeNames=%@", _classPropertyAttributeNames); } return _classPropertyAttributeNames; } - (NSArray*) classPropertyToManyRelationshipNames { //Should be OK if (!_classPropertyToManyRelationshipNames) { NSArray *classProperties = [self classProperties]; int i, count = [classProperties count]; Class relClass = [EORelationship class]; _classPropertyToManyRelationshipNames = [NSMutableArray new]; for (i = 0; i < count; i++) { EORelationship *property = [classProperties objectAtIndex: i]; if ([property isKindOfClass: relClass] && [property isToMany]) [(NSMutableArray*)_classPropertyToManyRelationshipNames addObject: [property name]]; } } return _classPropertyToManyRelationshipNames; } - (NSArray*) classPropertyToOneRelationshipNames { //Should be OK if (!_classPropertyToOneRelationshipNames) { NSArray *classProperties = [self classProperties]; int i, count = [classProperties count]; Class relClass = [EORelationship class]; _classPropertyToOneRelationshipNames = [NSMutableArray new]; //or GC ? for (i = 0; i 0, @"Empty relationship path"); //Verify when multi part path and not _relationshipPathIsToMany:path parts = [relPath componentsSeparatedByString: @"."]; rel = [self relationshipNamed: [parts objectAtIndex: 0]]; if (!rel) { NSEmitTODO(); //TODO //TODO } else { NSArray *joins = [rel joins]; int count = [joins count]; if (count>0) { int i=0; IMP joinsOAI=NULL; IMP attributesSOFK=NULL; for (i = 0; i < count; i++) { EOJoin *join = GDL2_ObjectAtIndexWithImpPtr(joins,&joinsOAI,i); EOAttribute *attribute = [join sourceAttribute]; GDL2_SetObjectForKeyWithImpPtr(attributes,&attributesSOFK, attribute,[attribute name]); } }; } } - (NSArray*) dbSnapshotKeys { //OK if (!_dbSnapshotKeys) { NSArray *attributesToFetch = [self _attributesToFetch]; EOFLOGObjectLevelArgs(@"EOEntity", @"attributesToFetch=%@", attributesToFetch); NSAssert3(!attributesToFetch || [attributesToFetch isKindOfClass: [NSArray class]], @"entity %@ attributesToFetch is not an NSArray but a %@\n%@", [self name], [attributesToFetch class], attributesToFetch); ASSIGN(_dbSnapshotKeys, [NSArray arrayWithArray: [attributesToFetch resultsOfPerformingSelector: @selector(name)]]); } return _dbSnapshotKeys; } - (NSArray*) flattenedAttributes { //OK NSArray *flattenedAttributes = nil; NSArray *attributesToFetch = [self _attributesToFetch]; int count = [attributesToFetch count]; NSAssert3(!attributesToFetch || [attributesToFetch isKindOfClass: [NSArray class]], @"entity %@ attributesToFetch is not an NSArray but a %@\n%@", [self name], [attributesToFetch class], attributesToFetch); if (count>0) { int i=0; IMP atfOAI=NULL; IMP tAO=NULL; NSMutableArray* tmpArray = AUTORELEASE([GDL2_alloc(NSMutableArray) initWithCapacity: count]); for (i = 0; i < count; i++) { EOAttribute *attribute = GDL2_ObjectAtIndexWithImpPtr(attributesToFetch,&atfOAI,i); if ([attribute isFlattened]) GDL2_AddObjectWithImpPtr(tmpArray,&tAO,attribute); }; flattenedAttributes=tmpArray; } else flattenedAttributes=GDL2_NSArray; return flattenedAttributes; } @end @implementation EOEntity (EOEntityPrivateXX) - (EOExpressionArray*) _parseDescription: (NSString*)description isFormat: (BOOL)isFormat arguments: (char*)param2 { // definition = "(((text(code) || ' ') || upper(abbreviation)) || ' ')"; EOExpressionArray *expressionArray = nil; const char *s = NULL; const char *start = NULL; id objectToken = nil; id pool = nil; EOFLOGObjectLevelArgs(@"EOEntity", @"expression=%@", description); expressionArray = AUTORELEASE([EOExpressionArray new]); s = [description cString]; if (s) { IMP eaAO=NULL; pool = [NSAutoreleasePool new]; NS_DURING { /* Divide the expression string in alternating substrings that obey the following simple grammar: I = [a-zA-Z0-9@_#]([a-zA-Z0-9@_.#$])+ O = \'.*\' | \".*\" | [^a-zA-Z0-9@_#]+ S -> I S | O S | nothing */ while (s && *s) { /* Determines an I token. */ if (isalnum(*s) || *s == '@' || *s == '_' || *s == '#') { EOExpressionArray *expr = nil; start = s; for (++s; *s; s++) if (!isalnum(*s) && *s != '@' && *s != '_' && *s != '.' && *s != '#' && *s != '$') break; objectToken = GDL2_StringWithCStringAndLength(start, (unsigned)(s - start)); EOFLOGObjectLevelArgs(@"EOEntity", @"objectToken: '%@'", objectToken); expr = [self _parsePropertyName: objectToken]; EOFLOGObjectLevelArgs(@"EOEntity", @"expr: '%@'", expr); if (expr) objectToken = expr; EOFLOGObjectLevelArgs(@"EOEntity", @"addObject I Token: '%@'", objectToken); GDL2_AddObjectWithImpPtr(expressionArray,&eaAO,objectToken); } /* Determines an O token. */ start = s; for (; *s && !isalnum(*s) && *s != '@' && *s != '_' && *s != '#'; s++) { if (*s == '\'' || *s == '"') { char quote = *s; for (++s; *s; s++) if (*s == quote) break; else if (*s == '\\') s++; /* Skip the escaped characters */ if (!*s) [NSException raise: NSInvalidArgumentException format: @"%@ -- %@ 0x%x: unterminated character string", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self]; } } if (s != start) { objectToken = GDL2_StringWithCStringAndLength(start, (unsigned)(s - start)); EOFLOGObjectLevelArgs(@"EOEntity", @"addObject O Token: '%@'", objectToken); GDL2_AddObjectWithImpPtr(expressionArray,&eaAO,objectToken); } } } NS_HANDLER { RETAIN(localException); NSLog(@"exception in EOEntity _parseDescription:isFormat:arguments:"); NSLog(@"exception=%@", localException); [pool release];//Release the pool ! AUTORELEASE(localException); [localException raise]; } NS_ENDHANDLER; [pool release]; } // return nil if expressionArray is empty if ([expressionArray count] == 0) expressionArray = nil; // if expressionArray contains only one element and this element is a expressionArray, use it (otherwise, isFlatten will not be accurate) else if ([expressionArray count] == 1) { id expr = [expressionArray lastObject]; if ([expr isKindOfClass: [EOExpressionArray class]]) expressionArray = expr; } EOFLOGObjectLevelArgs(@"EOEntity", @"expressionArray=%@\nexpressionArray count=%d isFlattened=%s\n", expressionArray, [expressionArray count], ([expressionArray isFlattened] ? "YES" : "NO")); return expressionArray; } - (EOExpressionArray*) _parseRelationshipPath: (NSString*)path { EOEntity *entity = self; EOExpressionArray *expressionArray = nil; NSArray *components = nil; int i, count = 0; EOFLOGObjectLevelArgs(@"EOEntity",@"self=%p (name=%@) path=%@", self,[self name],path); NSAssert1([path length] > 0, @"Path is empty (%p)", path); expressionArray = [EOExpressionArray expressionArrayWithPrefix: nil infix: @"." suffix: nil]; EOFLOGObjectLevelArgs(@"EOEntity", @"self=%p expressionArray=%@", self, expressionArray); components = [path componentsSeparatedByString: @"."]; count = [components count]; for (i = 0; i < count; i++) { NSString *part = [components objectAtIndex: i]; EORelationship *relationship; NSAssert1([part length] > 0, @"part is empty (path=%@)", path); relationship = [entity anyRelationshipNamed: part]; EOFLOGObjectLevelArgs(@"EOEntity", @"part=%@ relationship=%@", part, relationship); if (relationship) { NSAssert2([relationship isKindOfClass: [EORelationship class]], @"relationship is not a EORelationship but a %@. relationship:\n%@", [relationship class], relationship); if ([relationship isFlattened]) { EOExpressionArray *definitionArray=[relationship _definitionArray]; NSDebugMLog(@"entityName=%@ path=%@",[self name],path); NSDebugMLog(@"relationship=%@",relationship); NSDebugMLog(@"relationship definitionArray=%@",definitionArray); // For flattened relationships, we add relationship definition array [expressionArray addObjectsFromArray:definitionArray]; // Use last relationship to find destinationEntity,... relationship=[expressionArray lastObject]; } else { [expressionArray addObject: relationship]; } entity = [relationship destinationEntity]; } else { NSDebugMLog(@"self %p name=%@: relationship \"%@\" used in \"%@\" doesn't exist in entity \"%@\"", self, [self name], part, path, [entity name]); //EOF don't throw exception. But we do ! [NSException raise: NSInvalidArgumentException format: @"%@ -- %@ 0x%x: entity name=%@: relationship \"%@\" used in \"%@\" doesn't exist in entity \"%@\"", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self, [self name], part, path, [entity name]]; } } EOFLOGObjectLevelArgs(@"EOEntity", @"self=%p expressionArray=%@", self, expressionArray); // return nil if expressionArray is empty if ([expressionArray count] == 0) expressionArray = nil; // if expressionArray contains only one element and this element is a expressionArray, use it (otherwise, isFlatten will not be accurate) else if ([expressionArray count] == 1) { id expr = [expressionArray lastObject]; if ([expr isKindOfClass: [EOExpressionArray class]]) expressionArray = expr; } EOFLOGObjectLevelArgs(@"EOEntity", @"self=%p expressionArray=%@", self, expressionArray); return expressionArray; } - (id) _parsePropertyName: (NSString*)propertyName { EOEntity *entity = self; EOExpressionArray *expressionArray = nil; NSArray *components = nil; int i, count = 0; EOFLOGObjectLevelArgs(@"EOEntity", @"self=%p self name=%@ propertyName=%@", self, [self name], propertyName); expressionArray = [EOExpressionArray expressionArrayWithPrefix: nil infix: @"." suffix: nil]; EOFLOGObjectLevelArgs(@"EOEntity", @"self=%p expressionArray=%@", self, expressionArray); components = [propertyName componentsSeparatedByString: @"."]; count = [components count]; for (i = 0; i < count; i++) { NSString *part = [components objectAtIndex: i]; EORelationship *relationship = [entity anyRelationshipNamed: part]; EOFLOGObjectLevelArgs(@"EOEntity", @"self=%p entity name=%@ part=%@ relationship=%@ relationship name=%@", self, [entity name], part, relationship, [relationship name]); if (relationship) { NSAssert2([relationship isKindOfClass: [EORelationship class]], @"relationship is not a EORelationship but a %@. relationship:\n%@", [relationship class], relationship); if ([relationship isFlattened]) { NSEmitTODO(); //TODO [self notImplemented: _cmd];//TODO } else { EOFLOGObjectLevelArgs(@"EOEntity",@"self=%p expressionArray addObject=%@ (name=%@)", self, relationship, [relationship name]); [expressionArray addObject: relationship]; } entity = [relationship destinationEntity]; } else { EOAttribute *attribute = [entity anyAttributeNamed: part]; EOFLOGObjectLevelArgs(@"EOEntity", @"self=%p entity name=%@ part=%@ attribute=%@ attribute name=%@", self, [entity name], part, attribute, [attribute name]); if (attribute) [expressionArray addObject: attribute]; else if (i < (count - 1)) { //EOF don't throw exception ? But we do ! [NSException raise: NSInvalidArgumentException format: @"%@ -- %@ 0x%x: attribute \"%@\" used in \"%@\" doesn't exist in entity %@", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self, propertyName, part, entity]; } } } EOFLOGObjectLevelArgs(@"EOEntity", @"self=%p expressionArray=%@", self, expressionArray); // return nil if expression is empty if ([expressionArray count] == 0) expressionArray = nil; else if ([expressionArray count] == 1) expressionArray = [expressionArray objectAtIndex: 0]; EOFLOGObjectLevelArgs(@"EOEntity", @"self=%p expressionArray=\"%@\"", self, expressionArray); return expressionArray; } @end @implementation EOEntity (Deprecated) + (EOEntity *)entity { return AUTORELEASE([[self alloc] init]); } + (EOEntity *)entityWithPropertyList: (NSDictionary *)propertyList owner: (id)owner { return AUTORELEASE([[self alloc] initWithPropertyList: propertyList owner: owner]); } @end @implementation EOEntityClassDescription - (id)initWithEntity: (EOEntity *)entity { if ((self = [super init])) { ASSIGN(_entity, entity); } return self; } - (void) dealloc { //OK EOFLOGObjectLevelArgs(@"EOEntity", @"Deallocate EOEntityClassDescription %p", self); fflush(stdout); fflush(stderr); DESTROY(_entity); [super dealloc]; } - (NSString *) description { return [NSString stringWithFormat: @"<%s %p - Entity: %@>", object_getClassName(self), self, [self entityName]]; } - (EOEntity *)entity { return _entity; } - (EOFetchSpecification *)fetchSpecificationNamed: (NSString *)name { NSEmitTODO(); //TODO [self notImplemented: _cmd]; return nil; } - (NSString *)entityName { return [_entity name]; } - (NSArray *)attributeKeys { //OK return [_entity classPropertyAttributeNames]; } - (void)awakeObject: (id)object fromFetchInEditingContext: (EOEditingContext *)context { //OK [super awakeObject: object fromFetchInEditingContext: context]; //nothing to do } /** * Overrides [EOClassDescription-awakeObject:fromInsertionInEditingContext:] * to initialize the class property relationships. The toMany relationships * properties are initialized with a mutable array, while toOne relationships * which propagate the primary key of the object get instantiated with a * freshly initialzed instance. Whether a relationship is manditory or not * is irrelevant at this point. */ - (void)awakeObject: (id)object fromInsertionInEditingContext: (EOEditingContext *)context { NSArray *relationships; NSArray *classProperties; EORelationship *relationship; int i, count; IMP relOAI=NULL; IMP objectSVFK=NULL; IMP objectTSVFK=NULL; IMP objectVFK=NULL; [super awakeObject: object fromInsertionInEditingContext: context]; relationships = [_entity relationships]; classProperties = [_entity classProperties]; count = [relationships count]; for (i = 0; i < count; i++) { relationship = GDL2_ObjectAtIndexWithImpPtr(relationships,&relOAI,i); if ([classProperties containsObject: relationship]) { if ([relationship isToMany]) { NSString *name = [relationship name]; id relationshipValue = GDL2_StoredValueForKeyWithImpPtr(object,&objectSVFK,name); /* We put a value only if there's not already one */ if (relationshipValue == nil) { /* [Ref: Assigns empty arrays to to-many relationship properties of newly inserted enterprise objects] */ GDL2_TakeStoredValueForKeyWithImpPtr(object,&objectTSVFK, [EOCheapCopyMutableArray array], name); } } else { if ([relationship propagatesPrimaryKey]) { NSString *name = [relationship name]; id relationshipValue = GDL2_ValueForKeyWithImpPtr(object,&objectVFK,name); if (relationshipValue == nil) { EOEntity *destinationEntity = [relationship destinationEntity]; EOClassDescription *classDescription = [destinationEntity classDescriptionForInstances]; relationshipValue = [classDescription createInstanceWithEditingContext: context globalID: nil zone: NULL]; [object addObject: relationshipValue toBothSidesOfRelationshipWithKey: name]; [context insertObject: relationshipValue]; } } } } } } - (EOClassDescription *)classDescriptionForDestinationKey: (NSString *)detailKey { EOClassDescription *cd = nil; EOEntity *destEntity = nil; EORelationship *rel = nil; EOFLOGObjectLevelArgs(@"EOEntity", @"detailKey=%@", detailKey); EOFLOGObjectLevelArgs(@"EOEntity", @"_entity name=%@", [_entity name]); rel = [_entity relationshipNamed: detailKey]; EOFLOGObjectLevelArgs(@"EOEntity", @"rel=%@", rel); destEntity = [rel destinationEntity]; EOFLOGObjectLevelArgs(@"EOEntity", @"destEntity name=%@", [destEntity name]); cd = [destEntity classDescriptionForInstances]; EOFLOGObjectLevelArgs(@"EOEntity", @"cd=%@", cd); return cd; } - (id)createInstanceWithEditingContext: (EOEditingContext *)editingContext globalID: (EOGlobalID *)globalID zone: (NSZone *)zone { id obj = nil; Class objectClass; NSAssert1(_entity, @"No _entity in %@", self); objectClass = [_entity classForObjectWithGlobalID: (EOKeyGlobalID*)globalID]; NSAssert2(objectClass, @"No objectClass for globalID=%@. EntityName=%@", globalID, [_entity name]); if (objectClass) { obj = AUTORELEASE([[objectClass allocWithZone:zone] initWithEditingContext: editingContext classDescription: self globalID: globalID]); } return obj; } - (NSFormatter *)defaultFormatterForKey: (NSString *)key { [self notImplemented: _cmd]; return nil; } - (NSFormatter *)defaultFormatterForKeyPath: (NSString *)keyPath { [self notImplemented: _cmd]; return nil; //TODO } - (EODeleteRule)deleteRuleForRelationshipKey: (NSString *)relationshipKey { EORelationship *rel = nil; EODeleteRule deleteRule = 0; rel = [_entity relationshipNamed: relationshipKey]; EOFLOGObjectLevelArgs(@"EOEntity", @"relationship %p=%@", rel, rel); deleteRule = [rel deleteRule]; EOFLOGObjectLevelArgs(@"EOEntity", @"deleteRule=%d", (int)deleteRule); return deleteRule; } - (NSString *)inverseForRelationshipKey: (NSString *)relationshipKey { //Ayers: Review //Near OK NSString *inverseName = nil; EORelationship *relationship = [_entity relationshipNamed: relationshipKey]; //EOEntity *parentEntity = [_entity parentEntity]; //TODO what if parentEntity EORelationship *inverseRelationship = [relationship inverseRelationship]; if (inverseRelationship) { EOEntity *inverseEntity = [inverseRelationship entity]; NSArray *classPropertieNames = [inverseEntity classPropertyNames]; inverseName = [inverseRelationship name]; if (![classPropertieNames containsObject: inverseName]) inverseName = nil; } return inverseName; } - (BOOL)ownsDestinationObjectsForRelationshipKey: (NSString*)relationshipKey { //OK return [[_entity relationshipNamed: relationshipKey] ownsDestination]; } - (NSArray *)toManyRelationshipKeys { //OK return [_entity classPropertyToManyRelationshipNames]; } - (NSArray *)toOneRelationshipKeys { //OK return [_entity classPropertyToOneRelationshipNames]; } - (EORelationship *)relationshipNamed: (NSString *)relationshipName { //OK return [_entity relationshipNamed:relationshipName]; } - (EORelationship *)anyRelationshipNamed: (NSString *)relationshipName { return [_entity anyRelationshipNamed:relationshipName]; } - (NSException *) validateObjectForDelete: (id)object { return [_entity validateObjectForDelete:object]; } - (NSException *)validateObjectForSave: (id)object { return nil; //Does Nothing ? works is done in record } - (NSException *)validateValue: (id *)valueP forKey: (NSString *)key { NSException *exception = nil; EOAttribute *attr; EORelationship *relationship; NSAssert(valueP, @"No value pointer"); attr = [_entity attributeNamed: key]; if (attr) { exception = [attr validateValue: valueP]; } else { relationship = [_entity relationshipNamed: key]; if (relationship) { exception = [relationship validateValue: valueP]; } else { NSEmitTODO(); //TODO } } return exception; } @end @implementation EOEntityClassDescription (GDL2Extenstions) /** returns a new autoreleased mutable dictionary to store properties returns nil if there's no key in the instanceDictionaryInitializer **/ - (EOMutableKnownKeyDictionary*) dictionaryForInstanceProperties { EOMutableKnownKeyDictionary* dict = nil; NSAssert(_entity,@"No entity"); dict = [_entity _dictionaryForInstanceProperties]; return dict; } @end @implementation EOEntityClassDescription (Deprecated) + (EOEntityClassDescription*)entityClassDescriptionWithEntity: (EOEntity *)entity { return AUTORELEASE([[self alloc] initWithEntity: entity]); } @end @implementation NSString (EODatabaseNameConversion) + (NSString *)nameForExternalName: (NSString *)externalName separatorString: (NSString *)separatorString initialCaps: (BOOL)initialCaps { NSEmitTODO(); //TODO [self notImplemented: _cmd]; return nil; } + (NSString *)externalNameForInternalName: (NSString *)internalName separatorString: (NSString *)separatorString useAllCaps: (BOOL)allCaps { NSEmitTODO(); //TODO [self notImplemented: _cmd]; return nil; } @end @implementation NSObject (EOEntity) /** should returns a set of property names to exclude from entity instanceDictionaryInitializer **/ + (NSArray *)_instanceDictionaryInitializerExcludedPropertyNames { // default implementation returns nil return nil; } @end