]> err.no Git - sope/blob - sope-gdl1/GDLAccess/EOEntity.m
added missing inline pathes
[sope] / sope-gdl1 / GDLAccess / EOEntity.m
1 /* 
2    EOEntity.m
3
4    Copyright (C) 1996 Free Software Foundation, Inc.
5
6    Author: Ovidiu Predescu <ovidiu@bx.logicnet.ro>
7    Date: August 1996
8
9    Author: Helge Hess <helge.hess@mdlink.de>
10    Date: November 1999
11    
12    This file is part of the GNUstep Database Library.
13
14    This library is free software; you can redistribute it and/or
15    modify it under the terms of the GNU Library General Public
16    License as published by the Free Software Foundation; either
17    version 2 of the License, or (at your option) any later version.
18
19    This library is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22    Library General Public License for more details.
23
24    You should have received a copy of the GNU Library General Public
25    License along with this library; see the file COPYING.LIB.
26    If not, write to the Free Software Foundation,
27    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 */
29
30 #import "common.h"
31 #import "EOEntity.h"
32 #import "EOAttribute.h"
33 #import "EOFExceptions.h"
34 #import "EOModel.h"
35 #import "EOPrimaryKeyDictionary.h"
36 #import "EOSQLQualifier.h"
37 #import "EORelationship.h"
38 #import <EOControl/EOKeyValueCoding.h>
39 #import <EOControl/EOKeyGlobalID.h>
40
41 static int _compareByName(id obj1, id obj2, void * context);
42
43 @interface NSObject(MappedArrayProtocol)
44 - (NSArray *)mappedArrayUsingSelector:(SEL)_selector;
45 @end
46
47 @interface NSString(EntityBeautify)
48 - (NSString *)_beautifyEntityName;
49 @end
50
51 @implementation EOEntity
52
53 - (id)init {
54   if ((self = [super init])) {
55     self->attributes          = [[NSArray alloc] init];
56     self->attributesByName    = [[NSMutableDictionary alloc] init];
57     self->relationships       = [[NSArray alloc] init];
58     self->relationshipsByName = [[NSMutableDictionary alloc] init];
59     self->classProperties     = [[NSArray alloc] init];
60     self->model               = nil;
61   }
62   return self;
63 }
64
65 - (void)resetAttributes {
66   [self->attributes makeObjectsPerformSelector:@selector(resetEntity)];
67 }
68 - (void)resetRelationships {
69   [self->relationships makeObjectsPerformSelector:@selector(resetEntities)];
70 }
71
72 - (void)dealloc {
73   self->model = nil;
74   RELEASE(self->qualifier);
75   [self resetAttributes];
76   RELEASE(self->attributes);
77   RELEASE(self->attributesByName);
78   [self resetRelationships];
79   RELEASE(self->relationships);
80   RELEASE(self->relationshipsByName);
81   RELEASE(self->primaryKeyAttributes);
82   RELEASE(self->classProperties);
83   RELEASE(self->attributesUsedForLocking);
84   RELEASE(self->attributesUsedForInsert);
85   RELEASE(self->attributesUsedForFetch);
86   RELEASE(self->relationsUsedForFetch);
87   RELEASE(self->name);                     self->name = nil;
88   RELEASE(self->className);                self->className = nil;
89   RELEASE(self->externalName);             self->externalName = nil;
90   RELEASE(self->externalQuery);            self->externalQuery = nil;
91   RELEASE(self->userDictionary);           self->userDictionary = nil;
92   RELEASE(self->primaryKeyAttributeNames);
93   self->primaryKeyAttributeNames = nil;
94   RELEASE(self->attributesNamesUsedForInsert);
95   self->attributesNamesUsedForInsert = nil;
96   RELEASE(self->classPropertyNames); self->classPropertyNames = nil;
97   [super dealloc];
98 }
99
100 // These methods should be here to let the library work with NeXT foundation
101 - (id)copy {
102   return RETAIN(self);
103 }
104 - (id)copyWithZone:(NSZone *)_zone {
105   return RETAIN(self);
106 }
107
108 // Is equal only if same name; used to make aliasing ordering stable
109 - (unsigned)hash {
110   return [name hash];
111 }
112
113 - (id)initWithName:(NSString *)_name {
114   [self init];
115   ASSIGN(name, _name);
116   return self;
117 }
118
119 - (BOOL)setName:(NSString *)_name {
120   if([model entityNamed:name]) return NO;
121   ASSIGN(name, _name);
122   return YES;
123 }
124
125 + (BOOL)isValidName:(NSString *)attributeName {
126   unsigned   len = [attributeName cStringLength];
127   char       buf[len + 1];
128   const char *s;
129   
130   s = buf;
131   [attributeName getCString:buf];
132   
133   if(!isalnum((int)*s) && *s != '@' && *s != '_' && *s != '#')
134     return NO;
135
136   for(++s; *s; s++)
137     if(!isalnum((int)*s) && *s != '@' && *s != '_' && *s != '#' && *s != '$')
138       return NO;
139
140   return YES;
141 }
142
143 - (BOOL)addAttribute:(EOAttribute *)attribute {
144   NSString* attributeName = [attribute name];
145
146   if([self->attributesByName objectForKey:attributeName])
147     return NO;
148
149   if([self->relationshipsByName objectForKey:attributeName])
150     return NO;
151
152   if([self createsMutableObjects])
153     [(NSMutableArray*)self->attributes addObject:attribute];
154   else {
155     id newAttributes = [self->attributes arrayByAddingObject:attribute];
156     ASSIGN(self->attributes, newAttributes);
157   }
158
159   [self->attributesByName setObject:attribute forKey:attributeName];
160   [attribute setEntity:self];
161   [self invalidatePropertiesCache];
162   return YES;
163 }
164
165 - (void)removeAttributeNamed:(NSString *)attributeName {
166   id attribute = [self->attributesByName objectForKey:attributeName];
167
168   if(attribute) {
169     [attribute resetEntity];
170     if([self createsMutableObjects])
171       [(NSMutableArray*)attributes removeObject:attribute];
172     else {
173       self->attributes = [AUTORELEASE(self->attributes) mutableCopy];
174       [(NSMutableArray*)self->attributes removeObject:attribute];
175       self->attributes = [AUTORELEASE(self->attributes) copy];
176     }
177     [self->attributesByName removeObjectForKey:attributeName];
178     [self invalidatePropertiesCache];
179   }
180 }
181
182 - (EOAttribute *)attributeNamed:(NSString *)attributeName {
183   return [self->attributesByName objectForKey:attributeName];
184 }
185
186 - (BOOL)addRelationship:(EORelationship *)relationship {
187   NSString* relationshipName = [relationship name];
188
189   if([self->attributesByName objectForKey:relationshipName])
190     return NO;
191
192   if([self->relationshipsByName objectForKey:relationshipName])
193     return NO;
194
195   if([self createsMutableObjects])
196     [(NSMutableArray*)relationships addObject:relationship];
197   else {
198     id newRelationships = [self->relationships arrayByAddingObject:relationship];
199     ASSIGN(self->relationships, newRelationships);
200   }
201
202   [self->relationshipsByName setObject:relationship forKey:relationshipName];
203   [relationship setEntity:self];
204   [self invalidatePropertiesCache];
205   return YES;
206 }
207
208 - (void)removeRelationshipNamed:(NSString*)relationshipName {
209   id relationship = [relationshipsByName objectForKey:relationshipName];
210
211   if(relationship) {
212     [relationship setEntity:nil];
213     if([self createsMutableObjects])
214       [(NSMutableArray*)self->relationships removeObject:relationship];
215     else {
216       self->relationships = [AUTORELEASE(self->relationships) mutableCopy];
217       [(NSMutableArray*)self->relationships removeObject:relationship];
218       self->relationships = [AUTORELEASE(relationships) copy];
219     }
220     [self->relationshipsByName removeObjectForKey:relationship];
221     [self invalidatePropertiesCache];
222   }
223 }
224
225 - (EORelationship*)relationshipNamed:(NSString *)relationshipName {
226   if([relationshipName isNameOfARelationshipPath]) {
227     NSArray        *defArray;
228     int            i, count;
229     EOEntity       *currentEntity = self;
230     NSString       *relName       = nil;
231     EORelationship *relationship  = nil;
232
233     defArray = [relationshipName componentsSeparatedByString:@"."];
234     relName  = [defArray objectAtIndex:0];
235
236     for(i = 0, count = [defArray count]; i < count; i++) {
237       if(![EOEntity isValidName:relName])
238         return nil;
239       relationship = [currentEntity->relationshipsByName objectForKey:relName];
240       if(relationship == nil)
241         return nil;
242       currentEntity = [relationship destinationEntity];
243     }
244     return relationship;
245   }
246   else
247     return [self->relationshipsByName objectForKey:relationshipName];
248 }
249
250 - (BOOL)setPrimaryKeyAttributes:(NSArray *)keys {
251   int i, count = [keys count];
252
253   for(i = 0; i < count; i++)
254     if(![self isValidPrimaryKeyAttribute:[keys objectAtIndex:i]])
255       return NO;
256
257   RELEASE(self->primaryKeyAttributes);
258   RELEASE(self->primaryKeyAttributeNames);
259
260   if([keys isKindOfClass:[NSArray class]]
261      || [keys isKindOfClass:[NSMutableArray class]])
262     self->primaryKeyAttributes = [keys copy];
263   else
264     self->primaryKeyAttributes = [[NSArray alloc] initWithArray:keys];
265
266   self->primaryKeyAttributeNames = [NSMutableArray arrayWithCapacity:count];
267   for(i = 0; i < count; i++) {
268     id key = [keys objectAtIndex:i];
269     
270     [(NSMutableArray*)self->primaryKeyAttributeNames
271                       addObject:[(EOAttribute*)key name]];
272   }
273   self->primaryKeyAttributeNames
274     = RETAIN([self->primaryKeyAttributeNames
275                   sortedArrayUsingSelector:@selector(compare:)]);
276     
277   [self invalidatePropertiesCache];
278     
279   return YES;
280 }
281
282 - (BOOL)isValidPrimaryKeyAttribute:(EOAttribute*)anAttribute {
283   if(![anAttribute isKindOfClass:[EOAttribute class]])
284     return NO;
285
286   if([self->attributesByName objectForKey:[anAttribute name]])
287     return YES;
288
289   return NO;
290 }
291
292 - (NSDictionary *)primaryKeyForRow:(NSDictionary *)_row {
293   return [EOPrimaryKeyDictionary dictionaryWithKeys:
294                                    self->primaryKeyAttributeNames
295                                  fromDictionary:_row];
296 }
297
298 - (NSDictionary *)snapshotForRow:(NSDictionary *)aRow {
299   NSArray             *array;
300   int                 i, n;
301   NSMutableDictionary *dict;
302
303   array = [self attributesUsedForLocking];
304   n     = [array count];
305   dict  = [NSMutableDictionary dictionaryWithCapacity:n];
306   
307   for (i = 0; i < n; i++) {
308     EOAttribute *attribute;
309     NSString *columnName;
310     NSString *attributeName;
311     id value;
312
313     attribute     = [array objectAtIndex:i];
314     columnName    = [attribute columnName];
315     attributeName = [attribute name];
316
317     value = [aRow objectForKey:attributeName];
318
319 #if DEBUG
320     NSAssert1(columnName, @"missing column name in attribute %@ !", attribute);
321     NSAssert3(value, @"missing value for column '%@' (attr '%@') in row %@",
322               columnName, attribute, aRow);
323 #endif
324     
325     [dict setObject:value forKey:attributeName];
326   }
327   return dict;
328 }
329
330 /* Getting attributes used for database oprations */
331
332 - (NSArray*)attributesUsedForInsert
333 {
334   if (!flags.isPropertiesCacheValid)
335     [self validatePropertiesCache];
336   return self->attributesUsedForInsert;
337 }
338
339 - (NSArray *)attributesUsedForFetch {
340   if (!flags.isPropertiesCacheValid)
341     [self validatePropertiesCache];
342   return self->attributesUsedForFetch;
343 }
344
345 - (NSArray *)relationsUsedForFetch {
346   if (!flags.isPropertiesCacheValid)
347     [self validatePropertiesCache];
348   return self->relationsUsedForFetch;
349 }
350
351 - (NSArray *)attributesNamesUsedForInsert {
352   if (!flags.isPropertiesCacheValid)
353     [self validatePropertiesCache];
354   return self->attributesNamesUsedForInsert;
355 }
356
357 - (BOOL)setClassProperties:(NSArray *)properties {
358   int i, count = [properties count];
359
360   for(i = 0; i < count; i++)
361     if(![self isValidClassProperty:[properties objectAtIndex:i]])
362       return NO;
363
364   RELEASE(self->classProperties);    self->classProperties    = nil;
365   RELEASE(self->classPropertyNames); self->classPropertyNames = nil;
366
367   if([properties isKindOfClass:[NSArray class]]
368      || [properties isKindOfClass:[NSMutableArray class]]) {
369     self->classProperties = [properties copyWithZone:[self zone]];
370   }
371   else {
372     self->classProperties = [[NSArray allocWithZone:[self zone]]
373                                       initWithArray:properties];
374   }
375
376   self->classPropertyNames = [NSMutableArray arrayWithCapacity:count];
377   for(i = 0; i < count; i++) {
378     id property = [properties objectAtIndex:i];
379     [(NSMutableArray*)classPropertyNames addObject:[(EOAttribute*)property name]];
380   }
381   self->classPropertyNames = [self->classPropertyNames copyWithZone:[self zone]];
382   [self invalidatePropertiesCache];
383     
384   return YES;
385 }
386
387 - (BOOL)isValidClassProperty:(id)aProperty {
388   id thePropertyName = nil;
389
390   if(!([aProperty isKindOfClass:[EOAttribute class]]
391        || [aProperty isKindOfClass:[EORelationship class]]))
392     return NO;
393
394   thePropertyName = [(EOAttribute*)aProperty name];
395   if([self->attributesByName objectForKey:thePropertyName]
396      || [self->relationshipsByName objectForKey:thePropertyName])
397     return YES;
398
399   return NO;
400 }
401
402 - (NSArray *)relationshipsNamed:(NSString *)_relationshipPath {    
403   if([_relationshipPath isNameOfARelationshipPath]) {
404     NSMutableArray *myRelationships = [[NSMutableArray alloc] init];
405     NSArray  *defArray = [_relationshipPath componentsSeparatedByString:@"."];
406     int            i, count  = [defArray count] - 1;
407     EOEntity       *currentEntity = self;
408     NSString       *relName       = nil;
409     id             relation       = nil;
410
411     for(i = 0; i < count; i++) {
412       relName = [defArray objectAtIndex:i];
413
414       if([EOEntity isValidName:relName]) {
415         relation = [currentEntity relationshipNamed:relName];
416         if(relation) {
417           [myRelationships addObject:relation];
418           currentEntity = [relation destinationEntity];
419         }
420       }
421     }
422     return AUTORELEASE(myRelationships);
423   }
424   return nil;
425 }
426
427 - (id)propertyNamed:(NSString *)_name {
428   if([_name isNameOfARelationshipPath]) {
429     NSArray  *defArray      = [_name componentsSeparatedByString:@"."];
430     EOEntity *currentEntity = self;
431     NSString *propertyName;
432     int      i = 0, count = [defArray count];
433     id       property;
434
435     for(; i < count - 1; i++) {
436       propertyName = [defArray objectAtIndex:i];
437       if(![EOEntity isValidName:propertyName])
438         return nil;
439       property = [currentEntity propertyNamed:propertyName];
440       if(!property)
441         return nil;
442             
443       currentEntity = [property destinationEntity];
444     }
445     propertyName = [defArray lastObject];
446     property = [currentEntity attributeNamed:propertyName];
447     return property;
448   }
449   else {
450     id attribute    = nil;
451     id relationship = nil;
452
453     attribute = [self->attributesByName objectForKey:_name];
454     if(attribute)
455       return attribute;
456
457     relationship = [self->relationshipsByName objectForKey:_name];
458     if(relationship)
459       return relationship;
460   }
461
462   return nil;
463 }
464
465 - (BOOL)setAttributesUsedForLocking:(NSArray *)_attributes {
466   int i, count = [_attributes count];
467
468   for(i = 0; i < count; i++)
469     if(![self isValidAttributeUsedForLocking:
470               [_attributes objectAtIndex:i]])
471       return NO;
472
473   RELEASE(self->attributesUsedForLocking);
474
475   if([_attributes isKindOfClass:[NSArray class]]
476      || [_attributes isKindOfClass:[NSMutableArray class]])
477     self->attributesUsedForLocking = [_attributes copy];
478   else
479     self->attributesUsedForLocking = [[NSArray alloc] initWithArray:_attributes];
480   [self invalidatePropertiesCache];
481     
482   return YES;
483 }
484
485 - (BOOL)isValidAttributeUsedForLocking:(EOAttribute*)anAttribute {
486   if(!([anAttribute isKindOfClass:[EOAttribute class]]
487        && [self->attributesByName objectForKey:[anAttribute name]]))
488     return NO;
489   
490   return YES;
491 }
492
493 - (void)setModel:(EOModel *)aModel {
494   self->model = aModel;  /* non-retained */
495 }
496 - (void)resetModel {
497   self->model = nil;
498 }
499 - (BOOL)hasModel {
500   return (self->model != nil) ? YES : NO;
501 }
502
503 - (void)setClassName:(NSString *)_name {
504   if(!_name) _name = @"EOGenericRecord";
505   ASSIGN(self->className, _name);
506 }
507
508 - (void)setReadOnly:(BOOL)flag
509 {
510   flags.isReadOnly = flag;
511 }
512
513 - (BOOL)referencesProperty:(id)property {
514   id propertyName = [(EOAttribute*)property name];
515
516   if([self->attributesByName objectForKey:propertyName]
517      || [self->relationshipsByName objectForKey:propertyName])
518     return YES;
519   return NO;
520 }
521
522 - (EOSQLQualifier*)qualifier {
523   if (self->qualifier == nil) {
524     self->qualifier = [[EOSQLQualifier allocWithZone:[self zone]]
525                                        initWithEntity:self
526                                        qualifierFormat:nil];
527   }
528   return self->qualifier;
529 }
530
531 // accessors
532
533 - (void)setExternalName:(NSString*)_name {
534   ASSIGN(externalName, _name);
535 }
536 - (NSString *)externalName {
537   return self->externalName;
538 }
539
540 - (void)setExternalQuery:(NSString*)query {
541   ASSIGN(externalQuery, query);
542 }
543 - (NSString *)externalQuery {
544   return self->externalQuery;
545 }
546
547 - (void)setUserDictionary:(NSDictionary*)dict {
548   ASSIGN(userDictionary, dict);
549 }
550 - (NSDictionary *)userDictionary {
551   return self->userDictionary;
552 }
553
554 - (NSString *)name {
555   return self->name;
556 }
557 - (BOOL)isReadOnly {
558   return self->flags.isReadOnly;
559 }
560 - (NSString *)className {
561   return self->className;
562 }
563 - (NSArray *)attributesUsedForLocking {
564   return self->attributesUsedForLocking;
565 }
566 - (NSArray *)classPropertyNames {
567   return self->classPropertyNames;
568 }
569 - (NSArray *)classProperties {
570   return self->classProperties;
571 }
572 - (NSArray *)primaryKeyAttributes {
573   return self->primaryKeyAttributes;
574 }
575 - (NSArray *)primaryKeyAttributeNames {
576   return self->primaryKeyAttributeNames;
577 }
578 - (NSArray *)relationships {
579   return self->relationships;
580 }
581 - (EOModel *)model {
582   return self->model;
583 }
584 - (NSArray *)attributes {
585   return self->attributes;
586 }
587
588 /* EOEntityCreation */
589
590 + (EOEntity *)entityFromPropertyList:(id)propertyList model:(EOModel *)_model {
591   NSDictionary *plist = propertyList;
592   EOEntity     *entity;
593   NSArray      *array;
594   NSEnumerator *enumerator;
595   id attributePList;
596   id relationshipPList;
597
598   entity = [[[EOEntity alloc] init] autorelease];
599   [entity setCreateMutableObjects:YES];
600
601   entity->name           = RETAIN([plist objectForKey:@"name"]);
602   entity->className      = RETAIN([plist objectForKey:@"className"]);
603   entity->externalName   = RETAIN([plist objectForKey:@"externalName"]);
604   entity->externalQuery  = RETAIN([plist objectForKey:@"externalQuery"]);
605   entity->userDictionary = RETAIN([plist objectForKey:@"userDictionary"]);
606
607   array      = [plist objectForKey:@"attributes"];
608   enumerator = [array objectEnumerator];
609   
610   while ((attributePList = [enumerator nextObject])) {
611     EOAttribute *attribute;
612     
613     attribute = [EOAttribute attributeFromPropertyList:attributePList];
614     
615     if (![entity addAttribute:attribute]) {
616       NSLog(@"duplicate name for attribute '%@' in entity '%@'",
617             [attribute name], [entity name]);
618       [_model errorInReading];
619     }
620   }
621
622   entity->attributesUsedForLocking
623     = RETAIN([plist objectForKey:@"attributesUsedForLocking"]);
624   entity->classPropertyNames
625     = RETAIN([plist objectForKey:@"classProperties"]);
626
627   if ((attributePList = [plist objectForKey:@"primaryKeyAttributes"])) {
628     entity->primaryKeyAttributeNames
629       = RETAIN([attributePList sortedArrayUsingSelector:@selector(compare:)]);
630   }
631
632   else
633     if ((attributePList = [plist objectForKey:@"primaryKeyAttribute"]))
634       entity->primaryKeyAttributeNames
635         = RETAIN([NSArray arrayWithObject:attributePList]);
636
637   array = [plist objectForKey:@"relationships"];
638   enumerator = [array objectEnumerator];
639   while((relationshipPList = [enumerator nextObject])) {
640     EORelationship *relationship
641       = [EORelationship relationshipFromPropertyList:relationshipPList
642                         model:_model];
643     
644     if(![entity addRelationship:relationship]) {
645       NSLog(@"duplicate name for relationship '%@' in entity '%@'",
646             [relationship name], [entity name]);
647       [_model errorInReading];
648     }
649   }
650
651   [entity setCreateMutableObjects:NO];
652
653   return entity;
654 }
655
656 - (void)replaceStringsWithObjects {
657   NSEnumerator   *enumerator    = nil;
658   EOAttribute    *attribute     = nil;
659   NSString       *attributeName = nil;
660   NSString       *propertyName  = nil;
661   NSMutableArray *array         = nil;
662   int            i, count;
663
664   enumerator           = [self->primaryKeyAttributeNames objectEnumerator];
665   RELEASE(self->primaryKeyAttributes);
666   self->primaryKeyAttributes = AUTORELEASE([NSMutableArray new]);
667   
668   while ((attributeName = [enumerator nextObject])) {
669     attribute = [self attributeNamed:attributeName];
670     
671     if((attribute == nil) || ![self isValidPrimaryKeyAttribute:attribute]) {
672       NSLog(@"invalid attribute name specified as primary key attribute "
673             @"'%s' in entity '%s'", 
674             [attributeName cString], [name cString]);
675       [self->model errorInReading];
676     }
677     else
678       [(NSMutableArray*)self->primaryKeyAttributes addObject:attribute];
679   }
680   self->primaryKeyAttributes = [self->primaryKeyAttributes copy];
681
682   enumerator = [self->classPropertyNames objectEnumerator];
683   RELEASE(self->classProperties);
684   self->classProperties = AUTORELEASE([NSMutableArray new]);
685   while((propertyName = [enumerator nextObject])) {
686     id property;
687
688     property = [self propertyNamed:propertyName];
689     if(!property || ![self isValidClassProperty:property]) {
690       NSLog(@"invalid property '%s' specified as class property in "
691             @"entity '%s'", 
692             [propertyName cString], [name cString]);
693       [self->model errorInReading];
694     }
695     else
696       [(NSMutableArray*)self->classProperties addObject:property];
697   }
698   self->classProperties = [self->classProperties copy];
699
700   array = AUTORELEASE([NSMutableArray new]);
701   [array setArray:self->attributesUsedForLocking];
702   RELEASE(self->attributesUsedForLocking);
703   count = [array count];
704   for(i = 0; i < count; i++) {
705     attributeName = [array objectAtIndex:i];
706     attribute = [self attributeNamed:attributeName];
707     if(!attribute || ![self isValidAttributeUsedForLocking:attribute]) {
708       NSLog(@"invalid attribute specified as attribute used for "
709             @"locking '%@' in entity '%@'", attributeName, name);
710       [self->model errorInReading];
711     }
712     else
713       [array replaceObjectAtIndex:i withObject:attribute];
714   }
715   self->attributesUsedForLocking = [array copy];
716 }
717
718 - (id)propertyList
719 {
720   id propertyList;
721   
722   propertyList = [NSMutableDictionary dictionary];
723   [self encodeIntoPropertyList:propertyList];
724   return propertyList;
725 }
726
727 - (void)setCreateMutableObjects:(BOOL)flag {
728   if(self->flags.createsMutableObjects == flag)
729     return;
730
731   self->flags.createsMutableObjects = flag;
732
733   if(self->flags.createsMutableObjects) {
734     self->attributes    = [AUTORELEASE(self->attributes) mutableCopy];
735     self->relationships = [AUTORELEASE(self->relationships) mutableCopy];
736   }
737   else {
738     self->attributes    = [AUTORELEASE(self->attributes)    copy];
739     self->relationships = [AUTORELEASE(self->relationships) copy];
740   }
741 }
742
743 - (BOOL)createsMutableObjects {
744   return self->flags.createsMutableObjects;
745 }
746
747 #if 0
748 static inline void _printIds(NSArray *a, const char *pfx, const char *indent) {
749   int i;
750   if (pfx    == NULL) pfx    = "";
751   if (indent == NULL) indent = "  ";
752   printf("%s", pfx);
753   for (i = 0; i < [a count]; i++)
754     printf("%s0x%08X\n", indent, (unsigned)[a objectAtIndex:i]);
755 }
756 #endif
757
758 static inline BOOL _containsObject(NSArray *a, id obj) {
759   id (*objAtIdx)(NSArray*, SEL, int idx);
760   register int i;
761   
762   objAtIdx = (void *)[a methodForSelector:@selector(objectAtIndex:)];
763   for (i = [a count] - 1; i >= 0; i--) {
764     register id o;
765
766     o = objAtIdx(a, @selector(objectAtIndex:), i);
767     if (o == obj) return YES;
768   }
769   return NO;
770 }
771
772 - (void)validatePropertiesCache
773 {
774   NSMutableArray *updAttr = [NSMutableArray new];
775   NSMutableArray *updName = [NSMutableArray new];
776   NSMutableArray *fetAttr = [NSMutableArray new];
777   NSMutableArray *fetRels = [NSMutableArray new];
778     
779   int i;
780     
781   [self invalidatePropertiesCache];
782
783 #ifdef DEBUG
784   NSAssert((updAttr != nil) && (updName != nil) &&
785            (fetAttr != nil) && (fetRels != nil),
786            @"allocation of array failed !");
787   
788   NSAssert(self->primaryKeyAttributes,     @"no pkey attributes are set !");
789   NSAssert(self->attributesUsedForLocking, @"no locking attrs are set !");
790   NSAssert(self->classProperties,          @"no class properties are set !");
791 #endif
792
793   //_printIds(self->attributes,               "attrs:\n", "  ");
794   //_printIds(self->attributesUsedForLocking, "lock:\n", "  ");
795
796   for (i = ([self->attributes count] - 1); i >= 0; i--) {
797     EOAttribute *attr = [self->attributes objectAtIndex:i];
798     BOOL pk, lk, cp, sa;
799
800     pk = _containsObject(self->primaryKeyAttributes,     attr);
801     lk = _containsObject(self->attributesUsedForLocking, attr);
802     cp = _containsObject(self->classProperties,          attr);
803     sa = YES;
804     
805 #if 0
806     NSLog(@"attribute %@ pk=%i lk=%i cp=%i sa=%i", 
807           [attr name], pk, lk, cp, sa);
808 #endif
809     
810     if ((pk || lk || cp) && (!_containsObject(fetAttr, attr)))
811       [fetAttr addObject:attr];
812     
813     if ((pk || lk || cp) && (sa) && (!_containsObject(updAttr, attr))) {
814       [updAttr addObject:attr];
815       [updName addObject:[attr name]];
816     }
817   }
818     
819   for (i = [relationships count]-1; i >= 0; i--) {
820     id rel = [relationships objectAtIndex:i];
821
822     if (_containsObject(classProperties, rel))
823       [fetRels addObject:rel];
824   }
825
826   RELEASE(self->attributesUsedForInsert);
827   self->attributesUsedForInsert = [[NSArray alloc] initWithArray:updAttr];
828   RELEASE(self->relationsUsedForFetch);
829   self->relationsUsedForFetch   = [[NSArray alloc] initWithArray:fetRels];
830   RELEASE(self->attributesUsedForFetch);
831   self->attributesUsedForFetch  =
832     [[NSArray alloc] initWithArray:
833         [fetAttr sortedArrayUsingFunction:_compareByName context:nil]];
834   RELEASE(self->attributesNamesUsedForInsert);
835   attributesNamesUsedForInsert = [updName copy];
836
837   if ([self->attributesUsedForFetch count] == 0) {
838     NSLog(@"WARNING: entity %@ has no fetch attributes: "
839           @"attributes=%@ !",
840           self,
841           [[(id)self->attributes mappedArrayUsingSelector:@selector(name)]
842                                  componentsJoinedByString:@","]);
843   }
844     
845   RELEASE(updAttr); updAttr = nil;
846   RELEASE(fetAttr); fetAttr = nil;
847   RELEASE(fetRels); fetRels = nil;
848   RELEASE(updName); updName = nil;
849
850   self->flags.isPropertiesCacheValid = YES;
851 }
852
853 - (void)invalidatePropertiesCache {
854   if (flags.isPropertiesCacheValid) {
855     RELEASE(self->attributesUsedForInsert);
856     RELEASE(self->attributesUsedForFetch);
857     RELEASE(self->relationsUsedForFetch);
858     RELEASE(self->attributesNamesUsedForInsert);
859         
860     self->attributesUsedForInsert = nil;
861     self->attributesUsedForFetch = nil;
862     self->relationsUsedForFetch = nil;
863     self->attributesNamesUsedForInsert = nil;
864         
865     flags.isPropertiesCacheValid = NO;
866   }
867 }
868
869 // description
870
871 - (NSString *)description {
872   return [NSString stringWithFormat:
873                      @"<%@[0x%08X]: name=%@ className=%@ tableName=%@ "
874                      @"readOnly=%s>",
875                      NSStringFromClass([self class]), self,
876                      [self name], [self className], [self externalName],
877                      [self isReadOnly] ? "YES" : "NO"
878                    ];
879 }
880
881 @end /* EOEntity (EOEntityCreation) */
882
883
884 @implementation EOEntity(ValuesConversion)
885
886 - (NSDictionary *)convertValuesToModel:(NSDictionary *)aRow {
887   NSMutableDictionary *dict = [NSMutableDictionary dictionary];
888   NSEnumerator        *enumerator = [aRow keyEnumerator];
889   NSString            *key;
890     
891   while ((key = [enumerator nextObject])) {
892     id old = [aRow objectForKey:key];
893     id new = [[self attributeNamed:key] convertValueToModel:old];
894         
895     if (new) [dict setObject:new forKey:key];
896   }
897     
898   return [dict count] ? dict : nil;
899 }
900
901 static int _compareByName(id obj1, id obj2, void * context) {
902   return [[(EOAttribute*)obj1 name] compare:[(EOAttribute*)obj2 name]];
903 }
904
905 @end /* EOAttribute (ValuesConversion) */
906
907 @implementation EOEntity(EOF2Additions)
908
909 - (BOOL)isAbstractEntity {
910   return NO;
911 }
912
913 /* ids */
914
915 - (EOGlobalID *)globalIDForRow:(NSDictionary *)_row {
916   static Class EOKeyGlobalIDClass = Nil;
917   unsigned int keyCount = [self->primaryKeyAttributeNames count];
918   id           values[keyCount];
919   unsigned int i;
920   
921   for (i = 0; i < keyCount; i++) {
922     NSString *attrName;
923
924     attrName  = [self->primaryKeyAttributeNames objectAtIndex:i];
925     values[i] = [_row objectForKey:attrName];
926
927     if (values[i] == nil)
928       return nil;
929   }
930   if (EOKeyGlobalIDClass == Nil) EOKeyGlobalIDClass = [EOKeyGlobalID class];
931
932   return [EOKeyGlobalIDClass globalIDWithEntityName:self->name
933                              keys:&(values[0])
934                              keyCount:keyCount
935                              zone:[self zone]];
936 }
937
938 - (BOOL)isPrimaryKeyValidInObject:(id)_object {
939   unsigned int keyCount = [self->primaryKeyAttributeNames count];
940   unsigned int i;
941   
942   if (_object == nil) return NO;
943   
944   for (i = 0; i < keyCount; i++) {
945     if ([_object valueForKey:[self->primaryKeyAttributeNames objectAtIndex:i]]
946         == nil)
947       return NO;
948   }
949   return YES;
950 }
951
952 /* refs to other models */
953
954 - (NSArray *)externalModelsReferenced {
955   NSEnumerator   *e;
956   EORelationship *relship;
957   NSMutableArray *result;
958   EOModel        *thisModel;
959   
960   thisModel = [self model];
961   result    = nil;
962   
963   e = [self->relationships objectEnumerator];
964   while ((relship = [e nextObject])) {
965     EOEntity *targetEntity;
966     EOModel  *extModel;
967
968     targetEntity = [relship destinationEntity];
969     extModel = [targetEntity model];
970
971     if (extModel != thisModel) {
972       if (result == nil) result = [NSMutableArray array];
973       [result addObject:extModel];
974     }
975   }
976   return result ? result : [NSArray array];
977 }
978
979 /* fetch specs */
980
981 - (EOFetchSpecification *)fetchSpecificationNamed:(NSString *)_name {
982   return nil;
983 }
984 - (NSArray *)fetchSpecificationNames {
985   return nil;
986 }
987
988 /* names */
989
990 - (void)beautifyName {
991   [self setName:[[self name] _beautifyEntityName]];
992 }
993
994 @end /* EOEntity(EOF2Additions) */
995
996 @implementation EOEntity(PropertyListCoding)
997
998 static inline void _addToPropList(NSMutableDictionary *propertyList,
999                                   id _value, NSString *key) {
1000   if (_value) [propertyList setObject:_value forKey:key];
1001 }
1002
1003 - (void)encodeIntoPropertyList:(NSMutableDictionary *)_plist {
1004   int i, count;
1005
1006   _addToPropList(_plist, self->name,           @"name");
1007   _addToPropList(_plist, self->className,      @"className");
1008   _addToPropList(_plist, self->externalName,   @"externalName");
1009   _addToPropList(_plist, self->externalQuery,  @"externalQuery");
1010   _addToPropList(_plist, self->userDictionary, @"userDictionary");
1011   
1012   if ((count = [self->attributes count])) {
1013     id attributesPList;
1014
1015     attributesPList = [NSMutableArray array];
1016     for (i = 0; i < count; i++) {
1017       NSMutableDictionary *attributePList;
1018
1019       attributePList = [[NSMutableDictionary alloc] init];
1020       [[self->attributes objectAtIndex:i]
1021                          encodeIntoPropertyList:attributePList];
1022       [attributesPList addObject:attributePList];
1023       RELEASE(attributePList);
1024     }
1025     
1026     _addToPropList(_plist, attributesPList, @"attributes");
1027   }
1028
1029   if ((count = [self->attributesUsedForLocking count])) {
1030     id attributesUsedForLockingPList;
1031
1032     attributesUsedForLockingPList = [NSMutableArray array];
1033     for (i = 0; i < count; i++) {
1034       id attributePList;
1035
1036       attributePList =
1037         [(EOAttribute*)[self->attributesUsedForLocking objectAtIndex:i] name];
1038       [attributesUsedForLockingPList addObject:attributePList];
1039     }
1040     _addToPropList(_plist, attributesUsedForLockingPList,
1041                    @"attributesUsedForLocking");
1042   }
1043
1044   if ((count = [self->classProperties count])) {
1045     id classPropertiesPList = nil;
1046
1047     classPropertiesPList = [NSMutableArray array];
1048     for (i = 0; i < count; i++) {
1049       id classPropertyPList;
1050
1051       classPropertyPList = 
1052         [(EOAttribute*)[self->classProperties objectAtIndex:i] name];
1053       [classPropertiesPList addObject:classPropertyPList];
1054     }
1055     _addToPropList(_plist, classPropertiesPList, @"classProperties");
1056   }
1057
1058   if ((count = [self->primaryKeyAttributes count])) {
1059     id primaryKeyAttributesPList;
1060
1061     primaryKeyAttributesPList = [NSMutableArray array];
1062     for (i = 0; i < count; i++) {
1063       id attributePList;
1064       attributePList =
1065         [(EOAttribute*)[self->primaryKeyAttributes objectAtIndex:i] name];
1066       [primaryKeyAttributesPList addObject:attributePList];
1067     }
1068     _addToPropList(_plist, primaryKeyAttributesPList, @"primaryKeyAttributes");
1069   }
1070
1071   if ((count = [self->relationships count])) {
1072     id relationshipsPList;
1073     
1074     relationshipsPList = [NSMutableArray array];
1075     for (i = 0; i < count; i++) {
1076       NSMutableDictionary *relationshipPList;
1077
1078       relationshipPList = [NSMutableDictionary dictionary];
1079       
1080       [[self->relationships objectAtIndex:i]
1081                             encodeIntoPropertyList:relationshipPList];
1082       [relationshipsPList addObject:relationshipPList];
1083     }
1084     _addToPropList(_plist, relationshipsPList, @"relationships");
1085   }
1086 }
1087
1088 @end /* EOEntity(PropertyListCoding) */
1089
1090 @implementation NSString(EntityBeautify)
1091
1092 - (NSString *)_beautifyEntityName {
1093   if ([self length] == 0)
1094     return @"";
1095   else {
1096     unsigned clen = 0;
1097     char     *s   = NULL;
1098     unsigned cnt, cnt2;
1099
1100     clen = [self cStringLength];
1101 #if GNU_RUNTIME
1102     s = objc_atomic_malloc(clen + 4);
1103 #else
1104     s = malloc(clen + 4);
1105 #endif
1106
1107     [self getCString:s maxLength:clen];
1108     
1109     for (cnt = cnt2 = 0; cnt < clen; cnt++, cnt2++) {
1110       if ((s[cnt] == '_') && (s[cnt + 1] != '\0')) {
1111         s[cnt2] = toupper(s[cnt + 1]);
1112         cnt++;
1113       }
1114       else if ((s[cnt] == '2') && (s[cnt + 1] != '\0')) {
1115         s[cnt2] = s[cnt];
1116         cnt++;
1117         cnt2++;
1118         s[cnt2] = toupper(s[cnt]);
1119       }
1120       else
1121         s[cnt2] = tolower(s[cnt]);
1122     }
1123     s[cnt2] = '\0';
1124
1125     s[0] = toupper(s[0]);
1126
1127 #if !LIB_FOUNDATION_LIBRARY
1128     {
1129       NSString *os;
1130
1131       os = [NSString stringWithCString:s];
1132       free(s);
1133       return os;
1134     }
1135 #else
1136     return [NSString stringWithCStringNoCopy:s freeWhenDone:YES];
1137 #endif
1138   }
1139 }
1140
1141 @end