/* 
   EOEntity.m

   Copyright (C) 1996 Free Software Foundation, Inc.

   Author: Ovidiu Predescu <ovidiu@bx.logicnet.ro>
   Date: August 1996

   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 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; see the file COPYING.LIB.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <ctype.h>

#include <Foundation/NSValue.h>
#include <Foundation/NSUtilities.h>

#include <extensions/NSException.h>
#include <extensions/exceptions/GeneralExceptions.h>

#include <eoaccess/common.h>
#include <eoaccess/EOModel.h>
#include <eoaccess/EOEntity.h>
#include <eoaccess/EOAttribute.h>
#include <eoaccess/EORelationship.h>
#include <eoaccess/EOQualifier.h>
#include <eoaccess/EOPrimaryKeyDictionary.h>
#include <eoaccess/exceptions/EOFExceptions.h>

static int _compareByName(id obj1, id obj2, void * context);

@implementation EOEntity

- init
{
    [super init];
    attributes = [GCArray new];
    attributesByName = [GCMutableDictionary new];
    relationships = [GCArray new];
    relationshipsByName = [GCMutableDictionary new];
    classProperties = [GCArray new];
    return self;
}

- (void)dealloc
{
    /* Release only the non garbage collectable objects */
    [name release];
    [className release];
    [externalName release];
    [externalQuery release];
    [userDictionary release];
	[docComment release];
    [primaryKeyAttributeNames release];
    [attributesNamesUsedForInsert release];
    [classPropertyNames release];
    [super dealloc];
}

- (void)gcDecrementRefCountOfContainedObjects
{
    [model gcDecrementRefCount];
    [qualifier gcDecrementRefCount];
    [(id)attributes gcDecrementRefCount];
    [(id)attributesByName gcDecrementRefCount];
    [(id)relationships gcDecrementRefCount];
    [(id)relationshipsByName gcDecrementRefCount];
    [(id)primaryKeyAttributes gcDecrementRefCount];
    [(id)classProperties gcDecrementRefCount];
    [(id)attributesUsedForLocking gcDecrementRefCount];
    [(id)attributesUsedForInsert gcDecrementRefCount];
    [(id)attributesUsedForFetch gcDecrementRefCount];
    [(id)relationsUsedForFetch gcDecrementRefCount];
}

- (BOOL)gcIncrementRefCountOfContainedObjects
{
    if(![super gcIncrementRefCountOfContainedObjects])
	return NO;

    [model gcIncrementRefCount];
    [qualifier gcIncrementRefCount];
    [(id)attributes gcIncrementRefCount];
    [(id)attributesByName gcIncrementRefCount];
    [(id)relationships gcIncrementRefCount];
    [(id)relationshipsByName gcIncrementRefCount];
    [(id)primaryKeyAttributes gcIncrementRefCount];
    [(id)classProperties gcIncrementRefCount];
    [(id)attributesUsedForLocking gcIncrementRefCount];
    [(id)attributesUsedForInsert gcIncrementRefCount];
    [(id)attributesUsedForFetch gcIncrementRefCount];
    [(id)relationsUsedForFetch gcIncrementRefCount];

    [model gcIncrementRefCountOfContainedObjects];
    [qualifier gcIncrementRefCountOfContainedObjects];
    [(id)attributes gcIncrementRefCountOfContainedObjects];
    [(id)attributesByName gcIncrementRefCountOfContainedObjects];
    [(id)relationships gcIncrementRefCountOfContainedObjects];
    [(id)relationshipsByName gcIncrementRefCountOfContainedObjects];
    [(id)primaryKeyAttributes gcIncrementRefCountOfContainedObjects];
    [(id)classProperties gcIncrementRefCountOfContainedObjects];
    [(id)attributesUsedForLocking gcIncrementRefCountOfContainedObjects];
    [(id)attributesUsedForInsert gcIncrementRefCountOfContainedObjects];
    [(id)attributesUsedForFetch gcIncrementRefCountOfContainedObjects];
    [(id)relationsUsedForFetch gcIncrementRefCountOfContainedObjects];
    return YES;
}

// This method should be here to let the library work with NeXT foundation
- (id)copy
{
    return [self retain];
}

// This method should be here to let the library work with NeXT foundation
- (id)copyWithZone:(NSZone*)zone_
{
  return [self retain];
}

// Is equal only if same name; used to make aliasing ordering stable
- (unsigned)hash
{
    return [name hash];
}

- initWithName:(NSString*)_name
{
    [self init];
    ASSIGN(name, _name);
    return self;
}

-(NSString*)description
{
  NSString* dscr=nil;
  dscr=[NSString stringWithFormat:@"<%s %p - name=%@ className=%@ externalName=%@ externalQuery=%@",
				 object_get_class_name(self),
				 (void*)self,
				 name,
				 className,
				 externalName,
				 externalQuery];
  dscr=[dscr stringByAppendingFormat:@" userDictionary=%@",
			 userDictionary];
  dscr=[dscr stringByAppendingFormat:@" primaryKeyAttributeNames=%@ attributesNamesUsedForInsert=%@ classPropertyNames=%@",
			 primaryKeyAttributeNames,
			 attributesNamesUsedForInsert,
			 classPropertyNames];
  return dscr;
};

- (BOOL)setName:(NSString*)_name
{
    if([model entityNamed:name])
	return NO;

    ASSIGN(name, _name);
    return YES;
}

+ (BOOL)isValidName:(NSString*)attributeName
{
    const char* s = [attributeName cString];

    if(!isalnum(*s) && *s != '@' && *s != '_' && *s != '#')
	return NO;

    for(++s; *s; s++)
	if(!isalnum(*s) && *s != '@' && *s != '_' && *s != '#' && *s != '$')
	    return NO;

    return YES;
}

- (BOOL)addAttribute:(EOAttribute*)attribute
{
    NSString* attributeName = [attribute name];

    if([attributesByName objectForKey:attributeName])
	return NO;

    if([relationshipsByName objectForKey:attributeName])
	return NO;

    if([self createsMutableObjects])
	[(GCMutableArray*)attributes addObject:attribute];
    else
	attributes = [[[attributes autorelease]
			    arrayByAddingObject:attribute] retain];

    [attributesByName setObject:attribute forKey:attributeName];
    [attribute setEntity:self];
    [self invalidatePropertiesCache];
    return YES;
}

- (void)removeAttributeNamed:(NSString*)attributeName
{
    id attribute = [attributesByName objectForKey:attributeName];

    if(attribute) {
	[attribute setEntity:nil];
	if([self createsMutableObjects])
	    [(GCMutableArray*)attributes removeObject:attribute];
	else {
	    attributes = [[attributes autorelease] mutableCopy];
	    [(GCMutableArray*)attributes removeObject:attribute];
	    attributes = [[attributes autorelease] copy];
	}
	[attributesByName removeObjectForKey:attributeName];
	[self invalidatePropertiesCache];
    }
}

- (EOAttribute*)attributeNamed:(NSString*)attributeName
{
    return [attributesByName objectForKey:attributeName];
}

- (BOOL)addRelationship:(EORelationship*)relationship
{
    NSString* relationshipName = [relationship name];

    if([attributesByName objectForKey:relationshipName])
	return NO;

    if([relationshipsByName objectForKey:relationshipName])
	return NO;

    if([self createsMutableObjects])
	[(GCMutableArray*)relationships addObject:relationship];
    else
	relationships = [[[relationships autorelease]
			    arrayByAddingObject:relationship] retain];

    [relationshipsByName setObject:relationship forKey:relationshipName];
    [relationship setEntity:self];
    [self invalidatePropertiesCache];
    return YES;
}

- (void)removeRelationshipNamed:(NSString*)relationshipName
{
    id relationship = [relationshipsByName objectForKey:relationshipName];

    if(relationship) {
	[relationship setEntity:nil];
	if([self createsMutableObjects])
	    [(GCMutableArray*)relationships removeObject:relationship];
	else {
	    relationships = [[relationships autorelease] mutableCopy];
	    [(GCMutableArray*)relationships removeObject:relationship];
	    relationships = [[relationships autorelease] copy];
	}
	[relationshipsByName removeObjectForKey:relationship];
	[self invalidatePropertiesCache];
    }
}

- (EORelationship*)relationshipNamed:(NSString*)relationshipName
{
    if([relationshipName isNameOfARelationshipPath]) {
	NSArray* defArray
		= [relationshipName componentsSeparatedByString:@"."];
	int i = 0, count = [defArray count];
	EOEntity* currentEntity = self;
	NSString* relName = [defArray objectAtIndex:i];
	EORelationship* relationship = nil;

	for(; i < count; i++) {
	    if(![EOEntity isValidName:relName])
		return nil;
	    relationship
		= [currentEntity->relationshipsByName objectForKey:relName];
	    if(!relationship)
		return nil;
	    currentEntity = [relationship destinationEntity];
	}
	return relationship;
    }
    else
	return [relationshipsByName objectForKey:relationshipName];
}

- (BOOL)setPrimaryKeyAttributes:(NSArray*)keys
{
    int i, count = [keys count];

    for(i = 0; i < count; i++)
	if(![self isValidPrimaryKeyAttribute:[keys objectAtIndex:i]])
	    return NO;

    [primaryKeyAttributes release];
    [primaryKeyAttributeNames release];

    if([keys isKindOfClass:[GCArray class]]
	    || [keys isKindOfClass:[GCMutableArray class]])
	primaryKeyAttributes = [keys copy];
    else
	primaryKeyAttributes = [[GCArray alloc] initWithArray:keys];

    primaryKeyAttributeNames = [NSMutableArray arrayWithCapacity:count];
    for(i = 0; i < count; i++) {
	id key = [keys objectAtIndex:i];
	[(NSMutableArray*)primaryKeyAttributeNames
		addObject:[(EOAttribute*)key name]];
    }
    primaryKeyAttributeNames = [[primaryKeyAttributeNames
		    sortedArrayUsingSelector:@selector(compare:)]
		    retain];
    [self invalidatePropertiesCache];
    
    return YES;
}

- (BOOL)isValidPrimaryKeyAttribute:(EOAttribute*)anAttribute
{
    if(![anAttribute isKindOfClass:[EOAttribute class]])
	return NO;

    if([attributesByName objectForKey:[anAttribute name]])
	return YES;

    return NO;
}

- (NSDictionary*)primaryKeyForRow:(NSDictionary*)aRow
{
    return [EOPrimaryKeyDictionary 
	dictionaryWithKeys:primaryKeyAttributeNames
	fromDictionary:aRow];
}

- (NSDictionary*)snapshotForRow:(NSDictionary*)aRow
{
    NSArray* array = [self attributesUsedForLocking];
    int i, n = [array count];
    NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithCapacity:n];
    
    for (i=0; i<n; i++) {
	id key = [(EOAttribute*)[array objectAtIndex:i] name];
	[dict setObject:[aRow objectForKey:key] forKey:key];
    }
    return dict;
}

/* Getting attributes used for database oprations */

- (NSArray*)attributesUsedForInsert
{
    if (!flags.isPropertiesCacheValid)
	[self validatePropertiesCache];
    return attributesUsedForInsert;
}

- (NSArray*)attributesUsedForFetch
{
    if (!flags.isPropertiesCacheValid)
	[self validatePropertiesCache];
    return attributesUsedForFetch;
}

- (NSArray*)relationsUsedForFetch
{
    if (!flags.isPropertiesCacheValid)
	[self validatePropertiesCache];
    return relationsUsedForFetch;
}

- (NSArray*)attributesNamesUsedForInsert
{
    if (!flags.isPropertiesCacheValid)
	[self validatePropertiesCache];
    return attributesNamesUsedForInsert;
}

- (BOOL)setClassProperties:(NSArray*)properties
{
    int i, count = [properties count];

    for(i = 0; i < count; i++)
	if(![self isValidClassProperty:[properties objectAtIndex:i]])
	    return NO;

    [classProperties release];
    [classPropertyNames release];

    if([properties isKindOfClass:[GCArray class]]
	    || [properties isKindOfClass:[GCMutableArray class]])
	classProperties = [properties copy];
    else
	classProperties = [[GCArray alloc] initWithArray:properties];

    classPropertyNames = [NSMutableArray arrayWithCapacity:count];
    for(i = 0; i < count; i++) {
	id property = [properties objectAtIndex:i];
	[(NSMutableArray*)classPropertyNames
		addObject:[(EOAttribute*)property name]];
    }
    classPropertyNames = [classPropertyNames copy];
    [self invalidatePropertiesCache];
    
    return YES;
}

- (BOOL)isValidClassProperty:aProperty
{
    id thePropertyName;

    if(!([aProperty isKindOfClass:[EOAttribute class]]
	    || [aProperty isKindOfClass:[EORelationship class]]))
	return NO;

    thePropertyName = [(EOAttribute*)aProperty name];
    if([attributesByName objectForKey:thePropertyName]
	    || [relationshipsByName objectForKey:thePropertyName])
	return YES;

    return NO;
}

- (id)propertyNamed:(NSString*)_name
{
    if([_name isNameOfARelationshipPath]) {
	NSArray* defArray = [_name componentsSeparatedByString:@"."];
	int i = 0, count = [defArray count];
	EOEntity* currentEntity = self;
	NSString* propertyName;
	id property;

	for(; i < count - 1; i++) {
	    propertyName = [defArray objectAtIndex:i];
	    if(![EOEntity isValidName:propertyName])
		return nil;
	    property = [currentEntity propertyNamed:propertyName];
	    if(!property)
		return nil;
	    currentEntity = [property destinationEntity];
	}
	propertyName = [defArray lastObject];
	property = [currentEntity attributeNamed:propertyName];
	return property;
    }
    else {
	id attribute;
	id relationship;

	attribute = [attributesByName objectForKey:_name];
	if(attribute)
	    return attribute;

	relationship = [relationshipsByName objectForKey:_name];
	if(relationship)
	    return relationship;
    }

    return nil;
}

- (BOOL)setAttributesUsedForLocking:(NSArray*)_attributes
{
    int i, count = [_attributes count];

    for(i = 0; i < count; i++)
	if(![self isValidAttributeUsedForLocking:
		    [_attributes objectAtIndex:i]])
	    return NO;

    [attributesUsedForLocking release];

    if([_attributes isKindOfClass:[GCArray class]]
	    || [_attributes isKindOfClass:[GCMutableArray class]])
	attributesUsedForLocking = [_attributes copy];
    else
	attributesUsedForLocking = [[GCArray alloc] initWithArray:_attributes];
    [self invalidatePropertiesCache];
    
    return YES;
}

- (BOOL)isValidAttributeUsedForLocking:(EOAttribute*)anAttribute
{
    if(!([anAttribute isKindOfClass:[EOAttribute class]]
	    && [attributesByName objectForKey:[anAttribute name]]))
	return NO;

    if([anAttribute isDerived])
	return NO;

    return YES;
}

- (void)setModel:(EOModel*)aModel	{ ASSIGN(model, aModel); }

- (void)setClassName:(NSString*)_name
{
    if(!_name)
	_name = @"EOGenericRecord";
    ASSIGN(className, _name);
}

- (void)setReadOnly:(BOOL)flag
{
    flags.isReadOnly = flag;
}

- (BOOL)referencesProperty:property
{
    id propertyName = [(EOAttribute*)property name];

    if([attributesByName objectForKey:propertyName]
	    || [relationshipsByName objectForKey:propertyName])
	return YES;
    return NO;
}

- (EOQualifier*)qualifier
{
    if (!qualifier)
	qualifier = [[EOQualifier alloc]
			initWithEntity:self qualifierFormat:nil];
    return qualifier;
}

- (void)setExternalName:(NSString*)_name { ASSIGN(externalName, _name); }
- (void)setExternalQuery:(NSString*)query { ASSIGN(externalQuery, query); }
- (void)setUserDictionary:(NSDictionary*)dict { ASSIGN(userDictionary, dict); }
- (void)setDocComment:(NSString*)comment
{
    ASSIGN(docComment,comment);
}


- (NSString*)name		{ return name; }
- (NSDictionary*)userDictionary	{ return userDictionary; }
- (NSString*)docComment { return docComment; }
- (BOOL)isReadOnly		{ return flags.isReadOnly; }
- (NSString*)externalQuery	{ return externalQuery; }
- (NSString*)externalName	{ return externalName; }
- (NSString*)className		{ return className; } 
- (NSArray*)attributesUsedForLocking { return attributesUsedForLocking; }
- (NSArray*)classPropertyNames	{ return classPropertyNames; }
- (NSArray*)classProperties	{ return classProperties; }
- (NSArray*)primaryKeyAttributes { return primaryKeyAttributes; }
- (NSArray*)primaryKeyAttributeNames { return primaryKeyAttributeNames; }
- (NSArray*)relationships	{ return relationships; }
- (EOModel*)model		{ return model; }
- (NSArray*)attributes		{ return attributes; }

@end /* EOEntity */


@implementation EOEntity (EOEntityCreation)

+ (EOEntity*)entityFromPropertyList:(id)propertyList model:(EOModel*)_model
{
    EOEntity* entity = [[EOEntity new] autorelease];
    NSArray* array;
    NSEnumerator* enumerator;
    id attributePList;
    id relationshipPList;

    [entity setCreateMutableObjects:YES];

    entity->name = [[propertyList objectForKey:@"name"] retain];
    entity->className = [[propertyList objectForKey:@"className"] retain];
    entity->externalName = [[propertyList objectForKey:@"externalName"]
				retain];
    entity->externalQuery = [[propertyList objectForKey:@"externalQuery"]
				retain];
    entity->userDictionary = [[propertyList objectForKey:@"userDictionary"]
				retain];
	entity->docComment = [[propertyList objectForKey:@"docComment"] 
							retain];

    array = [propertyList objectForKey:@"attributes"];
    enumerator = [array objectEnumerator];
    while((attributePList = [enumerator nextObject])) {
	EOAttribute* attribute
		= [EOAttribute attributeFromPropertyList:attributePList];
	if(![entity addAttribute:attribute]) {
	  NSString* _error=[NSString stringWithFormat:@"duplicate name for attribute '%@' in entity '%@'",
								 [attribute name], [entity name]];
	  [_model errorInReadingWithError:_error];
	}
    }

    entity->attributesUsedForLocking
	    = [[propertyList objectForKey:@"attributesUsedForLocking"] retain];
    entity->primaryKeyAttributeNames
	    = [[[propertyList objectForKey:@"primaryKeyAttributes"] 
		sortedArrayUsingSelector:@selector(compare:)] retain];
    entity->classPropertyNames
	    = [[propertyList objectForKey:@"classProperties"] retain];

    array = [propertyList objectForKey:@"relationships"];
    enumerator = [array objectEnumerator];
    while((relationshipPList = [enumerator nextObject])) {
	EORelationship* relationship
	    = [EORelationship relationshipFromPropertyList:relationshipPList
				model:_model];
	if(![entity addRelationship:relationship]) {
	  NSString* _error=[NSString stringWithFormat:@"duplicate name for relationship '%@' in entity '%@'",
								 [relationship name], [entity name]];
	  [_model errorInReadingWithError:_error];
	}
    }

    [entity setCreateMutableObjects:NO];

    return entity;
}

- (void)replaceStringsWithObjects
{
    NSEnumerator* enumerator;
    EOAttribute* attribute;
    NSString* attributeName;
    NSString* propertyName;
    int i, count;
    GCMutableArray* array;

    enumerator = [primaryKeyAttributeNames objectEnumerator];
    primaryKeyAttributes = [[GCMutableArray new] autorelease];
    while((attributeName = [enumerator nextObject])) {
	attribute = [self attributeNamed:attributeName];
	if(!attribute || ![self isValidPrimaryKeyAttribute:attribute]) {
	  NSString* _error=[NSString stringWithFormat:@"invalid attribute name specified as primary key attribute "
								 @"'%s' in entity '%s'", 
								 [attributeName cString], [name cString]];
	  NSAssert(model,@"No Model");
	  [model errorInReadingWithError:_error];
	}
	else
	    [(GCMutableArray*)primaryKeyAttributes addObject:attribute];
    }
    primaryKeyAttributes = [primaryKeyAttributes copy];

    enumerator = [classPropertyNames objectEnumerator];
    classProperties = [[GCMutableArray new] autorelease];
    while((propertyName = [enumerator nextObject])) {
	id property;

	property = [self propertyNamed:propertyName];
	if(!property || ![self isValidClassProperty:property]) {
	  NSString* _error=[NSString stringWithFormat:@"invalid property '%s' specified as class property in "
								 @"entity '%s'", 
								 [propertyName cString], [name cString]];
	  NSAssert(model,@"No Model");
	  [model errorInReadingWithError:_error];
	}
	else
	    [(GCMutableArray*)classProperties addObject:property];
    }
    classProperties = [classProperties copy];

    array = [[GCMutableArray new] autorelease];
    [array setArray:attributesUsedForLocking];
    [attributesUsedForLocking release];
    count = [array count];
    for(i = 0; i < count; i++) {
	attributeName = [array objectAtIndex:i];
	attribute = [self attributeNamed:attributeName];
	if(!attribute || ![self isValidAttributeUsedForLocking:attribute]) {
	  NSString* _error=[NSString stringWithFormat:@"invalid attribute specified as attribute used for "
								 @"locking '%@' in entity '%@'",
								 attributeName, name];
	  NSAssert(model,@"No Model");
	  [model errorInReadingWithError:_error];
	}
	else
	    [array replaceObjectAtIndex:i withObject:attribute];
    }
    attributesUsedForLocking = [array copy];
}

- (id)propertyList
{
    id propertyList = [[NSMutableDictionary new] autorelease];
    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(userDictionary)
	[propertyList setObject:userDictionary forKey:@"userDictionary"];
    if(docComment)
	[propertyList setObject:docComment forKey:@"docComment"];

    if((count = [attributes count])) {
	id attributesPList = [[NSMutableArray new] autorelease];
	for(i = 0; i < count; i++) {
	    id attributePList = [[attributes objectAtIndex:i] propertyList];
	    [attributesPList addObject:attributePList];
	}
	[propertyList setObject:attributesPList forKey:@"attributes"];
    }

    if((count = [attributesUsedForLocking count])) {
	id attributesUsedForLockingPList = [[NSMutableArray new] autorelease];
	for(i = 0; i < count; i++) {
	    id attributePList
		= [(EOAttribute*)[attributesUsedForLocking objectAtIndex:i]
				    name];
	    [attributesUsedForLockingPList addObject:attributePList];
	}
	[propertyList setObject:attributesUsedForLockingPList
		      forKey:@"attributesUsedForLocking"];
    }

    if((count = [classProperties count])) {
	id classPropertiesPList = [[NSMutableArray new] autorelease];
	for(i = 0; i < count; i++) {
	    id classPropertyPList
		= [(EOAttribute*)[classProperties objectAtIndex:i]
				    name];
	    [classPropertiesPList addObject:classPropertyPList];
	}
	[propertyList setObject:classPropertiesPList
		      forKey:@"classProperties"];
    }

    if((count = [primaryKeyAttributes count])) {
	id primaryKeyAttributesPList = [[NSMutableArray new] autorelease];
	for(i = 0; i < count; i++) {
	    id attributePList
		= [(EOAttribute*)[primaryKeyAttributes objectAtIndex:i]
				    name];
	    [primaryKeyAttributesPList addObject:attributePList];
	}
	[propertyList setObject:primaryKeyAttributesPList
		      forKey:@"primaryKeyAttributes"];
    }

    if((count = [relationships count])) {
	id relationshipsPList = [[NSMutableArray new] autorelease];
	for(i = 0; i < count; i++) {
	    id relationshipPList = [[relationships objectAtIndex:i]
					propertyList];
	    [relationshipsPList addObject:relationshipPList];
	}
	[propertyList setObject:relationshipsPList forKey:@"relationships"];
    }

    return propertyList;
}

- (void)setCreateMutableObjects:(BOOL)flag
{
    if(flags.createsMutableObjects == flag)
	return;

    flags.createsMutableObjects = flag;

    if(flags.createsMutableObjects) {
	attributes = [[attributes autorelease] mutableCopy];
	relationships = [[relationships autorelease] mutableCopy];
    }
    else {
	attributes = [[attributes autorelease] copy];
	relationships = [[relationships autorelease] copy];
    }
}

- (BOOL)createsMutableObjects	{ return flags.createsMutableObjects; }

- (void)validatePropertiesCache
{
    NSMutableArray *updAttr = [NSMutableArray new];
    NSMutableArray *updName = [NSMutableArray new];
    NSMutableArray *fetAttr = [NSMutableArray new];
    NSMutableArray *fetRels = [NSMutableArray new];
    
    int i;
    
    [self invalidatePropertiesCache];

    for (i = [attributes count]-1; i >= 0; i--) {
	EOAttribute* attr = [attributes objectAtIndex:i];
	BOOL pk = ([primaryKeyAttributes indexOfObjectIdenticalTo:attr] != 
		    NSNotFound);
	BOOL lk = ([attributesUsedForLocking indexOfObjectIdenticalTo:attr] != 
		    NSNotFound);
	BOOL cp = ([classProperties indexOfObjectIdenticalTo:attr] != 
		    NSNotFound);
	BOOL sa = (![attr isDerived] && ![attr isFlattened]);
	
	if ((pk || lk || cp) && 
	    [fetAttr indexOfObjectIdenticalTo:attr] == NSNotFound)
		[fetAttr addObject:attr];
	if ((pk || lk || cp) && (sa) &&
	    [updAttr indexOfObjectIdenticalTo:attr] == NSNotFound) {
		[updAttr addObject:attr];
		[updName addObject:[attr name]];
	    }
    }
    
    for (i = [relationships count]-1; i >= 0; i--) {
	id rel = [relationships objectAtIndex:i];
	
	if ([classProperties indexOfObjectIdenticalTo:rel] != NSNotFound)
	    [fetRels addObject:rel];
    }
     
    attributesUsedForInsert = [[GCArray alloc] initWithArray:updAttr];
    relationsUsedForFetch   = [[GCArray alloc] initWithArray:fetRels];
    attributesUsedForFetch  = [[GCArray alloc] initWithArray:
	    [fetAttr sortedArrayUsingFunction:_compareByName context:nil]];
    attributesNamesUsedForInsert = [updName copy];
    
    [updAttr release];
    [fetAttr release];
    [fetRels release];
    [updName release];
    
    flags.isPropertiesCacheValid = YES;
}

- (void)invalidatePropertiesCache
{
    if (flags.isPropertiesCacheValid) {
	[attributesUsedForInsert release];
	[attributesUsedForFetch release];
	[relationsUsedForFetch release];
	[attributesNamesUsedForInsert release];
	
	attributesUsedForInsert = nil;
	attributesUsedForFetch = nil;
	relationsUsedForFetch = nil;
	attributesNamesUsedForInsert = nil;
	
	flags.isPropertiesCacheValid = NO;
    }
}

@end /* EOEntity (EOEntityCreation) */


@implementation EOEntity(ValuesConversion)

- (NSDictionary*)convertValuesToModel:(NSDictionary*)aRow
{
    NSMutableDictionary* dict = [NSMutableDictionary dictionary];
    NSEnumerator* enumerator = [aRow keyEnumerator];
    NSString* key;
    
    while ((key = [enumerator nextObject])) {
	id old = [aRow objectForKey:key];
	id new = [[self attributeNamed:key] convertValueToModel:old];
	
	if (new)
	    [dict setObject:new forKey:key];
	else if (old)
	  {
	    NSLog(@"WARNING: value '%@' of class %@ for attribute '%@' in entity '%@' has not been converted !",
			  old,
			  [old class],
			  [[self attributeNamed:key] name],
			  [self name]);
	  };
    }
    
    return [dict count] ? dict : nil;
}

static int _compareByName(id obj1, id obj2, void * context)
{
    return [[(EOAttribute*)obj1 name] compare:[(EOAttribute*)obj2 name]];
}

@end /* EOAttribute (ValuesConversion) */
