libs-gdl2/EOControl/EOGenericRecord.m
David Ayers 9238fc1a32 * *.m: Include GNUstep.h if GNUSTEP is not defined for OS X.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gdl2/trunk@17191 72102866-910b-0410-8b05-ffd578937521
2003-07-11 19:04:05 +00:00

1452 lines
38 KiB
Objective-C

/**
EOGenericRecord.m <title>EOGenericRecord</title>
Copyright (C) 2000-2002 Free Software Foundation, Inc.
Author: Mirko Viviani <mirko.viviani@rccr.cremona.it>
Date: June 2000
Author: Manuel Guesdon <mguesdon@orange-concept.com>
Date: October 2000
$Revision$
$Date$
<abstract></abstract>
This file is part of the GNUstep Database Library.
<license>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 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.
</license>
**/
#include "config.h"
RCS_ID("$Id$")
#ifndef NeXT_Foundation_LIBRARY
#include <Foundation/NSArray.h>
#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSObjCRuntime.h>
#include <Foundation/NSLock.h>
#include <Foundation/NSValue.h>
#include <Foundation/NSHashTable.h>
#include <Foundation/NSDebug.h>
#else
#include <Foundation/Foundation.h>
#endif
#ifndef GNUSTEP
#include <gnustep/base/GNUstep.h>
#endif
#include <gnustep/base/GSObjCRuntime.h>
#include <EOControl/EOClassDescription.h>
#include <EOControl/EOGenericRecord.h>
#include <EOControl/EONull.h>
#include <EOControl/EOObserver.h>
#include <EOControl/EOFault.h>
#include <EOControl/EOMutableKnownKeyDictionary.h>
#include <EOControl/EODebug.h>
#include <EOControl/EOKeyValueCoding.h>
#ifndef GNU_RUNTIME
#include <objc/objc-class.h>
#endif
@interface NSObject (EOCalculateSize)
- (unsigned int)eoGetSize;
@end
@interface NSString (EOCalculateSize)
- (unsigned int)eoGetSize;
@end
@interface NSArray (EOCalculateSize)
- (unsigned int)eoCalculateSizeWith: (NSMutableDictionary *)dict;
@end
@interface NSDictionary (EOCalculateSize)
- (unsigned int)eoCalculateSizeWith: (NSMutableDictionary *)dict;
@end
@interface EOFault (EOCalculateSize)
+ (unsigned int)eoCalculateSizeWith: (NSMutableDictionary *)dict
forFault: (id)object;
@end
@interface EOGenericRecord(EOCalculateSize)
+ (unsigned int)eoCalculateSizeWith: (NSMutableDictionary *)dict
forArray: (NSArray *)array;
@end
static NSHashTable *allGenericRecords = NULL;
static NSRecursiveLock *allGenericRecordsLock = nil;
@implementation EOGenericRecord
+ (void) initialize
{
if (self == [EOGenericRecord class] && !allGenericRecords)
{
allGenericRecords = NSCreateHashTable(NSNonOwnedPointerHashCallBacks,
1000);
allGenericRecordsLock = [NSRecursiveLock new];
}
}
+ (void)addCreatedObject: (EOGenericRecord *)o
{
[allGenericRecordsLock lock];
NSHashInsertIfAbsent(allGenericRecords, o);
[allGenericRecordsLock unlock];
}
+ (void)removeDestroyedObject: (EOGenericRecord *)o
{
[allGenericRecordsLock lock];
NSHashRemove(allGenericRecords, o);
[allGenericRecordsLock unlock];
}
- (id) init
{
if ((self = [super init]))
{
[[self class] addCreatedObject: self];
}
return self;
}
- (id) initWithEditingContext: (EOEditingContext *)context
classDescription: (EOClassDescription *)classDesc
globalID: (EOGlobalID *)globalID;
{
if ((self = [self init]))
{
NSMutableArray *classPropertyNames = nil;
EOMutableKnownKeyDictionary *entityMKKD = nil;
if (!classDesc)
{
[NSException raise: NSInternalInconsistencyException
format: @"%@ -- %@ 0x%x: attempt to initialize object with nil classDescription",
NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
self];
[self autorelease];
return nil;
}
ASSIGN(classDescription, classDesc);
// Get class properties (attributes + relationships)
classPropertyNames = [[NSMutableArray alloc]
initWithArray: [classDesc attributeKeys]];
[classPropertyNames addObjectsFromArray:
[classDesc toOneRelationshipKeys]];
[classPropertyNames addObjectsFromArray:
[classDesc toManyRelationshipKeys]];
NSAssert1([classPropertyNames count] > 0,
@"No classPropertyNames in %@", classDesc);
entityMKKD = [EOMutableKnownKeyDictionary
dictionaryWithInitializer:
[[EOMKKDInitializer newWithKeyArray: classPropertyNames] autorelease]];
ASSIGN(dictionary,entityMKKD);
EOFLOGObjectLevelArgs(@"EOGenericRecord", @"Record %p: dictionary=%@",
self, dictionary);
[classPropertyNames release];
}
return self;
}
- (void)dealloc
{
EOFLOGObjectLevelArgs(@"EOGenericRecord",
@"Deallocate EOGenericRecord %p (dict=%p)",
self, dictionary);
[[self class] removeDestroyedObject: self];
DESTROY(classDescription);
DESTROY(dictionary);
[super dealloc];
}
- (EOClassDescription*)classDescription
{
return classDescription;
}
//MG #if !FOUNDATION_HAS_KVC
static const char _c_id[2] = { _C_ID, 0 };
//used to allow derived object implementation
- (BOOL)_infoForInstanceVariableNamed: (NSString*)name
retType: (const char**)type
retSize: (unsigned int*)size
retOffset: (int*)offset
{
BOOL ok;
EOFLOGObjectFnStartCond(@"EOGenericRecordKVC");
/* ok=[super _infoForInstanceVariableNamed:name
retType:type
retSize:size
retOffset:offset];
*/
ok = GSFindInstanceVariable(self, [name cString], type, size, offset);
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC",
@"Super InstanceVar named %@:%s",
name, (ok ? "YES" : "NO"));
if (!ok)
{
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC",
@"dictionary: %p eoMKKDInitializer: %p",
dictionary,
[dictionary eoMKKDInitializer]);
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC", @"dictionary allkeys= %@",
[dictionary allKeys]);
if ([dictionary hasKey: name])
{
if (type)
*type = _c_id;
if (size)
*size = sizeof(id);
if (offset)
*offset = UINT_MAX; //Special Marker
ok = YES;
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC",
@"Self InstanceVar named %@:%s",
name, (ok ? "YES" : "NO"));
}
}
EOFLOGObjectFnStopCond(@"EOGenericRecordKVC");
return ok;
}
//used to allow derived object implementation
- (id)_getValueForKey: (NSString*)aKey
selector: (SEL)sel
type: (const char*)type
size: (unsigned)size
offset: (unsigned)offset
{
id value = nil;
EOFLOGObjectFnStartCond(@"EOGenericRecordKVC");
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC",
@"Super InstanceVar named %@: offset=%u",
aKey, offset);
if (offset == UINT_MAX)
{
value = [dictionary objectForKey: aKey];
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC", @"value %p (class=%@)",
value, [value class]);
}
else
{
/* value=[super _getValueForKey:aKey
selector:sel
type:type
size:size
offset:offset];*/
value = GSGetValue(self, aKey, sel, type, size, offset);
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC", @"value %p (class=%@)",
value, [value class]);
}
EOFLOGObjectFnStopCond(@"EOGenericRecordKVC");
return value;
}
//used to allow derived object implementation
- (void)_setValueForKey: (NSString *)aKey
object: (id)anObject
selector: (SEL)sel
type: (const char*)type
size: (unsigned)size
offset: (unsigned)offset
{
EOFLOGObjectFnStartCond(@"EOGenericRecordKVC");
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC",
@"Super InstanceVar named %@: offset=%u",
aKey, offset);
[self willChange];
if (offset == UINT_MAX)
{
if (anObject)
[dictionary setObject: anObject
forKey: aKey];
else
[dictionary removeObjectForKey: aKey];
}
else
/* [super _setValueForKey:aKey
object:anObject
selector:sel
type:type
size:size
offset:offset];
*/
GSSetValue(self, aKey, anObject, sel, type, size, offset);
EOFLOGObjectFnStopCond(@"EOGenericRecordKVC");
}
//used to allow derived object implementation
- (id) _handleQueryWithUnboundKey: (NSString*)aKey
{
return nil;
}
//used to allow derived object implementation
- (void) _handleTakeValue: (id)anObject forUnboundKey: (NSString*)aKey
{
}
/*
- (void)takeStoredValue:(id)value
forKey:(NSString *)key
{
EOFLOGObjectFnStartOrCond(@"EOGenericRecord");
EOFLOGObjectLevelArgs(@"EOGenericRecord", @"key=%@", key);
[super takeStoredValue: value
forKey: key];
EOFLOGObjectFnStopOrCond(@"EOGenericRecord");
}
- (void)takeValue:(id)value
forKey:(NSString *)key
{
EOFLOGObjectFnStartOrCond(@"EOGenericRecord");
EOFLOGObjectLevelArgs(@"EOGenericRecord", @"key=%@", key);
[self willChange];
[super takeValue: value
forKey: key];
EOFLOGObjectFnStopOrCond(@"EOGenericRecord");
//
// if(value == nil || value == [EONull null])
// [dictionary removeObjectForKey:key];
// else
// {
// NSArray *attrKeys, *toManyKeys, *toOneKeys;
//
// attrKeys = [classDescription attributeKeys];
// toManyKeys = [classDescription toManyRelationshipKeys];
// toOneKeys = [classDescription toOneRelationshipKeys];
//
// if([attrKeys containsObject:key] == NO &&
// [toManyKeys containsObject:key] == NO &&
// [toOneKeys containsObject:key] == NO)
// return;
//
// [dictionary setObject:value forKey:key];
// }
//
// EOFLOGObjectFnStopOrCond(@"EOGenericRecord");
}
- (id)storedValueForKey:(NSString *)key
{
id value=nil;
EOFLOGObjectFnStartOrCond(@"EOGenericRecord");
EOFLOGObjectLevelArgs(@"EOGenericRecord",@"key=%@",key);
value=[super storedValueForKey: key];
EOFLOGObjectFnStopOrCond(@"EOGenericRecord");
return value;
}
- (id)valueForKey:(NSString *)key
{
id value=nil;
EOFLOGObjectFnStartOrCond(@"EOGenericRecord");
EOFLOGObjectLevelArgs(@"EOGenericRecord", @"key=%@", key);
value=[super valueForKey: key];
EOFLOGObjectFnStopOrCond(@"EOGenericRecord");
return value;
// id value=nil;
// EOFLOGObjectFnStartOrCond(@"EOGenericRecord");
// EOFLOGObjectLevelArgs(@"EOGenericRecord", @"key=%@", key);
// NSArray *attrKeys, *toManyKeys, *toOneKeys;
//
// attrKeys = [classDescription attributeKeys];
// toManyKeys = [classDescription toManyRelationshipKeys];
// toOneKeys = [classDescription toOneRelationshipKeys];
//
// if([attrKeys containsObject:key] == NO &&
// [toManyKeys containsObject:key] == NO &&
// [toOneKeys containsObject:key] == NO)
// return nil;
//
// value=[dictionary objectForKey:key];
// EOFLOGObjectFnStopOrCond(@"EOGenericRecord");
// return value;
}
*/
- (id) storedValueForKey: (NSString*)aKey
{
SEL sel = 0;
const char *type = NULL;
unsigned size = 0;
unsigned off = 0;
NSString *name = nil;
NSString *cap = nil;
id value = nil;
EOFLOGObjectFnStartCond(@"EOGenericRecordKVC");
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC", @"aKey=%@", aKey);
if ([[self class] useStoredAccessor] == NO)
{
value = [self valueForKey: aKey];
}
else
{
size = [aKey length];
if (size < 1)
{
[NSException raise: NSInvalidArgumentException
format: @"storedValueForKey: ... empty key"];
}
cap = [[aKey substringToIndex: 1] uppercaseString];
if (size > 1)
{
cap = [cap stringByAppendingString: [aKey substringFromIndex: 1]];
}
name = [NSString stringWithFormat: @"_get%@", cap];
sel = NSSelectorFromString(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
name = [NSString stringWithFormat: @"_%@", aKey];
sel = NSSelectorFromString(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
sel = 0;
}
}
if (sel == 0)
{
if ([[self class] accessInstanceVariablesDirectly] == YES)
{
name = [NSString stringWithFormat: @"_%@", aKey];
if ([self _infoForInstanceVariableNamed:name
retType: &type
retSize: &size
retOffset: &off]==NO)
{
name = aKey;
[self _infoForInstanceVariableNamed:name
retType: &type
retSize: &size
retOffset: &off];
}
}
if (type == NULL)
{
name = [NSString stringWithFormat: @"get%@", cap];
sel = NSSelectorFromString(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
name = aKey;
sel = NSSelectorFromString(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
sel = 0;
}
}
}
}
value = [self _getValueForKey: aKey
selector: sel
type: type
size: size
offset: off];
}
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC", @"value=%@", value);
EOFLOGObjectFnStopCond(@"EOGenericRecordKVC");
return value;
}
- (void) takeStoredValue: (id)anObject
forKey: (NSString*)aKey
{
SEL sel = NULL;
const char *type = NULL;
unsigned size = 0;
unsigned off = 0;
NSString *cap = nil;
NSString *name = nil;
EOFLOGObjectFnStartCond(@"EOGenericRecordKVC");
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC", @"anObject=%@", anObject);
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC", @"aKey=%@", aKey);
if ([[self class] useStoredAccessor] == NO)
{
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC", @"aKey=%@", aKey);
[self takeValue: anObject
forKey: aKey];
}
else
{
size = [aKey length];
if (size < 1)
{
[NSException raise: NSInvalidArgumentException
format: @"takeStoredValue:forKey: ... empty key"];
}
cap = [[aKey substringToIndex: 1] uppercaseString];
if (size > 1)
{
cap = [cap stringByAppendingString: [aKey substringFromIndex: 1]];
}
name = [NSString stringWithFormat: @"_set%@:", cap];
type = NULL;
sel = NSSelectorFromString(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
sel = 0;
if ([[self class] accessInstanceVariablesDirectly] == YES)
{
name = [NSString stringWithFormat: @"_%@", aKey];
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC", @"aKey=%@ name=%@",
aKey, name);
if ([self _infoForInstanceVariableNamed: name
retType: &type
retSize: &size
retOffset: &off]==NO)
{
name = aKey;
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC",
@"aKey=%@ name=%@", aKey, name);
[self _infoForInstanceVariableNamed: name
retType: &type
retSize: &size
retOffset: &off];
}
}
if (type == NULL)
{
name = [NSString stringWithFormat: @"set%@:", cap];
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC", @"aKey=%@ name=%@",
aKey, name);
sel = NSSelectorFromString(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
sel = 0;
}
}
}
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC",
@"class=%@ aKey=%@ sel=%p offset=%u",
[self class], aKey, sel, off);
[self _setValueForKey: aKey
object: anObject
selector: sel
type: type
size: size
offset: off];
}
EOFLOGObjectFnStopCond(@"EOGenericRecordKVC");
}
//#endif /* !FOUNDATION_HAS_KVC */
/** if key is a bidirectional rel, use addObject:toBothSidesOfRelationship otherwise call takeValue:forKey: **/
- (void)smartTakeValue: (id)anObject
forKey: (NSString *)aKey
{
BOOL isToMany = [[classDescription toManyRelationshipKeys]
containsObject: aKey];
//NSDebugMLog(@"aKey=%@ rel=%@ anObject=%@", aKey, rel, anObject);
//NSDebugMLog(@"[rel isBidirectional]=%d", [rel isBidirectional]);
if ((isToMany
|| [[classDescription toOneRelationshipKeys] containsObject: aKey])
&& [classDescription inverseForRelationshipKey: aKey] != nil)
{
if (isNilOrEONull(anObject))
{
id oldObj = [self valueForKey: aKey];
if (isNilOrEONull(oldObj))
{
if (!isToMany)
[self takeValue: anObject
forKey: aKey];
}
else
[self removeObject: anObject
fromBothSidesOfRelationshipWithKey: aKey];
}
else
[self addObject: anObject
toBothSidesOfRelationshipWithKey: aKey];
}
else
[self takeValue: anObject
forKey: aKey];
}
//MG#if !FOUNDATION_HAS_KVC
- (void) takeValue: (id)anObject forKey: (NSString*)aKey
{
SEL sel;
const char *type;
unsigned size;
unsigned off=0;
NSString *cap;
NSString *name;
EOFLOGObjectFnStartCond(@"EOGenericRecordKVC");
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC", @"anObject=%@", anObject);
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC", @"aKey=%@", aKey);
size = [aKey length];
if (size < 1)
{
[NSException raise: NSInvalidArgumentException
format: @"takeValue:forKey: ... empty key"];
}
cap = [[aKey substringToIndex: 1] uppercaseString];
if (size > 1)
{
cap = [cap stringByAppendingString: [aKey substringFromIndex: 1]];
}
name = [NSString stringWithFormat: @"set%@:", cap];
type = NULL;
sel = NSSelectorFromString(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
name = [NSString stringWithFormat: @"_set%@:", cap];
sel = NSSelectorFromString(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
sel = 0;
if ([[self class] accessInstanceVariablesDirectly] == YES)
{
name = [NSString stringWithFormat: @"_%@", aKey];
if ([self _infoForInstanceVariableNamed: name
retType: &type
retSize: &size
retOffset: &off]==NO)
{
name = aKey;
[self _infoForInstanceVariableNamed: name
retType: &type
retSize: &size
retOffset: &off];
}
}
}
}
[self _setValueForKey: aKey
object: anObject
selector: sel
type: type
size: size
offset: off];
EOFLOGObjectFnStopCond(@"EOGenericRecordKVC");
}
- (id) valueForKey: (NSString*)aKey
{
SEL sel = 0;
NSString *cap;
NSString *name = nil;
const char *type = NULL;
unsigned size;
unsigned off = 0;
id value = nil;
EOFLOGObjectFnStartCond(@"EOGenericRecordKVC");
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC", @"aKey=%@", aKey);
size = [aKey length];
if (size < 1)
{
[NSException raise: NSInvalidArgumentException
format: @"valueForKey: ... empty key"];
}
cap = [[aKey substringToIndex: 1] uppercaseString];
if (size > 1)
{
cap = [cap stringByAppendingString: [aKey substringFromIndex: 1]];
}
name = [@"get" stringByAppendingString: cap];
sel = NSSelectorFromString(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
name = aKey;
sel = NSSelectorFromString(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
name = [@"_get" stringByAppendingString: cap];
sel = NSSelectorFromString(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
name = [NSString stringWithFormat: @"_%@", aKey];
sel = NSSelectorFromString(name);
if (sel == 0 || [self respondsToSelector: sel] == NO)
{
sel = 0;
}
}
}
}
if (sel == 0 && [[self class] accessInstanceVariablesDirectly] == YES)
{
name = [NSString stringWithFormat: @"_%@", aKey];
if ([self _infoForInstanceVariableNamed: name
retType: &type
retSize: &size
retOffset: &off]==NO)
{
name = aKey;
[self _infoForInstanceVariableNamed: name
retType: &type
retSize: &size
retOffset: &off];
}
}
value = [self _getValueForKey: aKey
selector: sel
type: type
size: size
offset: off];
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC", @"value: %p (class=%@)",
value, [value class]);
EOFLOGObjectFnStopCond(@"EOGenericRecordKVC");
return value;
}
//MG#else /* FOUNDATION_HAS_KVC */
/*
- (id) handleQueryWithUnboundKey: (NSString *)key
{
id value;
EOFLOGObjectFnStartCond(@"EOGenericRecordKVC");
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC",
@"Unbound key named %@",
key);
if (![dictionary hasKey: key])
return [super handleQueryWithUnboundKey: key];
value = [dictionary objectForKey: key];
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC", @"value %p (class=%@)",
value, [value class]);
EOFLOGObjectFnStopCond(@"EOGenericRecordKVC");
return value;
}
- (void) handleTakeValue: (id)value forUnboundKey: (NSString *)key
{
EOFLOGObjectFnStartCond(@"EOGenericRecordKVC");
EOFLOGObjectLevelArgs(@"EOGenericRecordKVC",
@"Unbound key named %@",
key);
[self willChange];
if (![dictionary hasKey:key])
[super handleTakeValue:value forUnboundKey: key];
if (value)
[dictionary setObject: value
forKey: key];
else
// [dictionary setObject: [EONull null]
// forKey: key];
[dictionary removeObjectForKey: key];
EOFLOGObjectFnStopCond(@"EOGenericRecordKVC");
}
*/
//MG#endif /* FOUNDATION_HAS_KVC */
/** used in -decription for self toOne or toMany objects to avoid
infinite loop in description **/
- (NSString *)_shortDescription
{
NSArray *toManyKeys = nil;
NSArray *toOneKeys = nil;
NSEnumerator *enumerator = [dictionary keyEnumerator];
NSMutableDictionary *dict;
NSString *key = nil;
id obj = nil;
toManyKeys = [classDescription toManyRelationshipKeys];
toOneKeys = [classDescription toOneRelationshipKeys];
dict = [NSMutableDictionary dictionaryWithCapacity: [dictionary count]];
while ((key = [enumerator nextObject]))
{
obj = [dictionary objectForKey: key];
if (!obj)
[dict setObject: @"(null)"
forKey: key];
else
{
// print out only simple values
if ([toManyKeys containsObject: key] == NO
&& [toOneKeys containsObject: key] == NO)
{
[dict setObject: obj
forKey: key];
}
}
}
return [NSString stringWithFormat: @"<%s %p : classDescription=%@\nvalues=%@>",
object_get_class_name(self),
(void*)self,
classDescription,
dict];
}
- (NSString *)description
{
NSArray *toManyKeys = nil;
NSArray *toOneKeys = nil;
NSEnumerator *enumerator = [dictionary keyEnumerator];
NSMutableDictionary *dict;
NSString *key = nil;
id obj = nil;
toManyKeys = [classDescription toManyRelationshipKeys];
toOneKeys = [classDescription toOneRelationshipKeys];
dict = [NSMutableDictionary dictionaryWithCapacity: [dictionary count]];
while ((key = [enumerator nextObject]))
{
obj = [dictionary objectForKey: key];
if (!obj)
[dict setObject: @"(null)"
forKey: key];
else
{
if ([toManyKeys containsObject: key] == NO
&& [toOneKeys containsObject: key] == NO)
{
[dict setObject: obj
forKey: key];
}
else
{
if ([EOFault isFault: obj] == YES)
{
[dict setObject: [obj description]
forKey: key];
}
else if ([toManyKeys containsObject: key] == YES)
{
NSEnumerator *toManyEnum;
NSMutableArray *array;
id rel;
array = [NSMutableArray arrayWithCapacity: 8];
toManyEnum = [obj objectEnumerator];
while ((rel = [toManyEnum nextObject]))
{
NSString* relDescr;
// Avoid infinit loop
if ([rel respondsToSelector: @selector(_shortDescription)])
relDescr=[rel _shortDescription];
else
relDescr=[rel description];
[array addObject:
[NSString
stringWithFormat: @"<%@ %p>",
relDescr, NSStringFromClass([rel class])]];
}
[dict setObject: [NSString stringWithFormat:
@"<%p %@ : %@>",
obj, [obj class], array]
forKey: key];
}
else
{
[dict setObject: [NSString
stringWithFormat: @"<%p %@: classDescription=%@>",
obj,
NSStringFromClass([obj class]),
[(EOGenericRecord *)obj classDescription]]
forKey: key];
}
}
}
}
return [NSString stringWithFormat: @"<%s %p : classDescription=%@\nvalues=%@>",
object_get_class_name(self),
(void*)self,
classDescription,
dict];
}
//debug only
- (NSString *)debugDictionaryDescription
{
return [dictionary debugDescription];
}
/*dictionary has following entries:
- NSMutableDictionary* processed: processed entries (key=object address, value=size)
- NSMutableDictionary* summaryNb: objects by class name (key=class name, value=number of objects)
- NSMutableDictionary* summarySize: objects by class name (key=class name, value=size)
- NSMutableArray* unknownClasses: not calculated objects classes
size are size of objects + size of elementary included objects
*/
+ (void)eoCalculateAllSizeWith: (NSMutableDictionary *)dict
{
EOGenericRecord *record = nil;
NSHashEnumerator hashEnum;
NSAutoreleasePool *arp;
EOFLOGClassFnStart();
//NSDebugMLog(@"CALCULATE START");
[allGenericRecordsLock lock];
NS_DURING
{
arp = [NSAutoreleasePool new];
hashEnum = NSEnumerateHashTable(allGenericRecords);
while ((record = (EOGenericRecord*)NSNextHashEnumeratorItem(&hashEnum)))
{
if ([EOFault isFault:record])
[EOFault eoCalculateSizeWith: dict
forFault: record];
else
[record eoCalculateSizeWith: dict];
}
NSEndHashTableEnumeration(&hashEnum);
}
NS_HANDLER
{
NSDebugMLog(@"%@ (%@)", localException, [localException reason]);
RETAIN(localException);
DESTROY(arp);
AUTORELEASE(localException);
[allGenericRecordsLock unlock];
NSDebugMLog(@"CALCULATE STOPEXC", "");
[localException raise];
}
NS_ENDHANDLER;
DESTROY(arp);
[allGenericRecordsLock unlock];
//NSDebugMLog(@"CALCULATE STOP");
EOFLOGClassFnStop();
}
- (unsigned int)eoCalculateSizeWith: (NSMutableDictionary *)dict
{
NSMutableDictionary *processed;
NSValue *selfP;
EOFLOGObjectFnStartOrCond(@"EOGenericRecord");
//NSDebugMLog(@"CALCULATE OBJ START %p", self);
processed = [dict objectForKey: @"processed"];
selfP = [NSValue valueWithPointer: self];
if (![processed objectForKey: selfP])
{
NSMutableDictionary *summaryNb = nil;
NSMutableDictionary *summarySize = nil;
NSMutableArray *unknownClasses = nil;
NSArray *props;
NSString *selfClassName = NSStringFromClass([self class]);
NSNumber *selfSummaryNb = nil;
NSNumber *selfSummarySize = nil;
unsigned int size = 0;
int i, propCount;
//NSDebugMLog(@"self=%@",self);
if (!processed)
{
processed = (NSMutableDictionary *)[NSMutableDictionary dictionary];
[dict setObject: processed
forKey: @"processed"];
}
[processed setObject: [NSNumber numberWithUnsignedInt: 0]
forKey: selfP];
//NSDebugMLog(@"classDescription=%@", classDescription);
// Get class properties (attributes + relationships)
props = [NSMutableArray arrayWithArray: [classDescription attributeKeys]];
[(NSMutableArray *)props addObjectsFromArray:
[classDescription toOneRelationshipKeys]];
[(NSMutableArray *)props addObjectsFromArray:
[classDescription toManyRelationshipKeys]];
size += [self eoGetSize];
size += [dictionary eoGetSize];
//NSDebugMLog(@"props=%@",props);
propCount = [props count];
for (i = 0; i < propCount; i++)
{
NSString *propKey = [props objectAtIndex: i];
id value = [self valueForKey: propKey];
//NSDebugMLog(@"propKey=%@", propKey);
//NSDebugMLog(@"value isFault=%s", ([EOFault isFault:value] ? "YES" : "NO"));
//NSDebugMLog(@"value=%p class=%@", value, [value class]);
if (value)
{
if ([EOFault isFault:value])
size += [EOFault eoCalculateSizeWith: dict
forFault: value];
else if ([value respondsToSelector: @selector(eoCalculateSizeWith:)])
{
size += [value eoCalculateSizeWith: dict];
}
else if ([value respondsToSelector: @selector(eoGetSize)])
size += [value eoGetSize];
else
{
NSString *className = NSStringFromClass([value class]);
if (!unknownClasses)
{
unknownClasses = [dict objectForKey: @"unknownClasses"];
if (!unknownClasses)
{
unknownClasses = [NSMutableArray array];
[dict setObject: unknownClasses
forKey: @"unknownClasses"];
}
}
if (![unknownClasses containsObject: className])
[unknownClasses addObject: className];
}
}
}
if (size > 0)
[processed setObject: [NSNumber numberWithUnsignedInt: size]
forKey: selfP];
summaryNb = [dict objectForKey: @"summaryNb"];
if (!summaryNb)
{
summaryNb = (NSMutableDictionary *)[NSMutableDictionary dictionary];
[dict setObject: summaryNb
forKey: @"summaryNb"];
}
selfSummaryNb = [summaryNb objectForKey: selfClassName];
selfSummaryNb = [NSNumber numberWithUnsignedInt: [selfSummaryNb
unsignedIntValue] + 1];
[summaryNb setObject: selfSummaryNb
forKey: selfClassName];
summarySize = [dict objectForKey: @"summarySize"];
if (!summarySize)
{
summarySize = (NSMutableDictionary *)[NSMutableDictionary dictionary];
[dict setObject: summarySize
forKey: @"summarySize"];
}
selfSummarySize = [summarySize objectForKey: selfClassName];
selfSummarySize = [NSNumber numberWithUnsignedInt:
[selfSummarySize unsignedIntValue] + size];
[summarySize setObject: selfSummarySize
forKey: selfClassName];
}
//NSDebugMLog(@"CALCULATE OBJ STOP %p", self);
EOFLOGObjectFnStopOrCond(@"EOGenericRecord");
return 0;
}
+ (unsigned int)eoCalculateSizeWith: (NSMutableDictionary *)dict
forArray: (NSArray *)array
{
NSMutableDictionary *processed;
NSValue *selfP;
EOFLOGObjectFnStartOrCond(@"EOGenericRecord");
//NSDebugMLog(@"CALCULATE ARRAY START %p", self);
processed = [dict objectForKey: @"processed"];
selfP = [NSValue valueWithPointer: array];
if (![processed objectForKey: selfP])
{
int i, count;
if (!processed)
{
processed = (NSMutableDictionary *)[NSMutableDictionary dictionary];
[dict setObject: processed
forKey: @"processed"];
}
[processed setObject: [NSNumber numberWithUnsignedInt: 0]
forKey: selfP];
count = [array count];
for (i = 0; i < count; i++)
{
id value = [array objectAtIndex: i];
if (value)
{
if ([value respondsToSelector: @selector(eoCalculateSizeWith:)])
[value eoCalculateSizeWith: dict];
}
}
}
//NSDebugMLog(@"CALCULATE ARRAY START %p", self);
EOFLOGClassFnStop();
return [array eoGetSize]; //return the base size
}
+ (NSString *)eoFormatSizeDictionary: (NSDictionary *)dict
{
NSMutableString *dscr = [NSMutableString string];
NSMutableDictionary *processed;
NSMutableDictionary *summaryNb;
NSMutableDictionary *summarySize;
NSString *key;
unsigned totalSize = 0;
unsigned totalNb = 0;
NSEnumerator *enumK;
EOFLOGClassFnStart();
processed = [dict objectForKey: @"processed"];
summaryNb = [dict objectForKey: @"summaryNb"];
summarySize = [dict objectForKey: @"summarySize"];
enumK = [[[summaryNb allKeys] sortedArrayUsingSelector:
@selector(compare:)] objectEnumerator];
while ((key = [enumK nextObject]))
{
NSNumber *size = [summarySize objectForKey: key];
NSNumber *number = [summaryNb objectForKey: key];
[dscr appendFormat: @"%@: totalSize=%@ bytes (%d Kb)/ objectsNb=%@ / meanSize=%d bytes (%d Kb)\n",
key,
size,
[size unsignedIntValue]/1024,
number,
(int)([size unsignedIntValue] / [number unsignedIntValue]),
(int)([size unsignedIntValue] / [number unsignedIntValue] / 1024)];
totalSize += [size unsignedIntValue];
totalNb += [number unsignedIntValue];
}
[dscr appendFormat: @"-------------\ntotalSize=%u bytes (%d Kb) / objectsNb=%u / meanSize=%d bytes (%d Kb)\n",
totalSize,
totalSize / 1024,
totalNb,
(int)(totalNb!=0 ? (totalSize / totalNb) : 0),
(int)(totalNb!=0 ? (totalSize / totalNb / 1024) : 0)];
EOFLOGClassFnStop();
return dscr;
}
@end /* EOGenericRecord */
@implementation NSObject (EOCalculateSize)
- (unsigned int)eoGetSize
{
unsigned int size = 0;
Class selfClass = Nil;
// EOFLOGObjectFnStartOrCond(@"EOGenericRecord");
selfClass = [self class];
size = selfClass->instance_size;
// EOFLOGObjectFnStopOrCond(@"EOGenericRecord");
return size;
}
@end
@implementation NSString (EOCalculateSize)
- (unsigned int)eoGetSize
{
unsigned int size;
//consider 2bytes string
//EOFLOGObjectFnStartOrCond(@"EOGenericRecord");
size = [super eoGetSize] + [self length] * 2;
//EOFLOGObjectFnStopOrCond(@"EOGenericRecord");
return size;
}
@end
@implementation NSArray (EOCalculateSize)
- (unsigned int)eoCalculateSizeWith: (NSMutableDictionary *)dict
{
unsigned int size;
//NSDebugMLog(@"CALCULATE ARRAY START %p", self);
size = [EOGenericRecord eoCalculateSizeWith: dict
forArray: self];
//NSDebugMLog(@"CALCULATE ARRAY STOP %p", self);
return size;
}
@end
@implementation NSDictionary (EOCalculateSize)
- (unsigned int)eoCalculateSizeWith: (NSMutableDictionary *)dict
{
//EOFLOGObjectFnStartOrCond(@"EOGenericRecord");
return [EOGenericRecord eoCalculateSizeWith: dict
forArray: [self allValues]];
//EOFLOGObjectFnStopOrCond(@"EOGenericRecord");
}
@end
@implementation EOFault (EOCalculateSize)
+ (unsigned int)eoCalculateSizeWith: (NSMutableDictionary *)dict
forFault: (id)object
{
NSMutableDictionary *processed;
unsigned int baseSize = 0;
NSValue *objectP;
EOFLOGClassFnStart();
//NSDebugFLog(@"CALCULATE FAULT START %p",object);
processed = [dict objectForKey: @"processed"];
objectP = [NSValue valueWithPointer: object];
if (![processed objectForKey: objectP])
{
NSString *objectClassName = [NSString stringWithFormat: @"%@ (Fault)",
NSStringFromClass([object class])];
NSNumber *objectSummaryNb = 0;
NSNumber *objectSummarySize = 0;
Class objectClass = [object class];
unsigned int size = 0;
//NSDebugMLog(@"object=%p", object);
if (!processed)
{
processed = (NSMutableDictionary *)[NSMutableDictionary dictionary];
[dict setObject: processed
forKey: @"processed"];
}
[processed setObject: [NSNumber numberWithUnsignedInt: 0]
forKey: objectP];
size += objectClass->instance_size;
if ([object isKindOfClass: [NSArray class]])
baseSize += size;
else
{
NSMutableDictionary *summaryNb = nil;
NSMutableDictionary *summarySize = nil;
if (size>0)
[processed setObject: [NSNumber numberWithUnsignedInt: size]
forKey: objectP];
summaryNb = [dict objectForKey: @"summaryNb"];
if (!summaryNb)
{
summaryNb = (NSMutableDictionary *)[NSMutableDictionary
dictionary];
[dict setObject: summaryNb
forKey: @"summaryNb"];
}
objectSummaryNb = [summaryNb objectForKey: objectClassName];
objectSummaryNb = [NSNumber numberWithUnsignedInt:
[objectSummaryNb unsignedIntValue] + 1];
[summaryNb setObject: objectSummaryNb
forKey: objectClassName];
summarySize = [dict objectForKey: @"summarySize"];
if (!summarySize)
{
summarySize = (NSMutableDictionary *)[NSMutableDictionary
dictionary];
[dict setObject: summarySize
forKey: @"summarySize"];
}
objectSummarySize = [summarySize objectForKey: objectClassName];
objectSummarySize = [NSNumber numberWithUnsignedInt:
[objectSummarySize unsignedIntValue]
+ size];
[summarySize setObject: objectSummarySize
forKey: objectClassName];
}
}
//NSDebugMLog(@"CALCULATE FAULT STOP %p", object);
EOFLOGClassFnStop();
return baseSize;
}
@end