]> err.no Git - sope/blob - sope-gdl1/GDLAccess/EOEntity.m
removed subminor
[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   if([anAttribute isDerived])
491     return NO;
492
493   return YES;
494 }
495
496 - (void)setModel:(EOModel *)aModel {
497   self->model = aModel;  /* non-retained */
498 }
499 - (void)resetModel {
500   self->model = nil;
501 }
502 - (BOOL)hasModel {
503   return (self->model != nil) ? YES : NO;
504 }
505
506 - (void)setClassName:(NSString *)_name {
507   if(!_name) _name = @"EOGenericRecord";
508   ASSIGN(self->className, _name);
509 }
510
511 - (void)setReadOnly:(BOOL)flag
512 {
513   flags.isReadOnly = flag;
514 }
515
516 - (BOOL)referencesProperty:(id)property {
517   id propertyName = [(EOAttribute*)property name];
518
519   if([self->attributesByName objectForKey:propertyName]
520      || [self->relationshipsByName objectForKey:propertyName])
521     return YES;
522   return NO;
523 }
524
525 - (EOSQLQualifier*)qualifier {
526   if (self->qualifier == nil) {
527     self->qualifier = [[EOSQLQualifier allocWithZone:[self zone]]
528                                        initWithEntity:self
529                                        qualifierFormat:nil];
530   }
531   return self->qualifier;
532 }
533
534 // accessors
535
536 - (void)setExternalName:(NSString*)_name {
537   ASSIGN(externalName, _name);
538 }
539 - (NSString *)externalName {
540   return self->externalName;
541 }
542
543 - (void)setExternalQuery:(NSString*)query {
544   ASSIGN(externalQuery, query);
545 }
546 - (NSString *)externalQuery {
547   return self->externalQuery;
548 }
549
550 - (void)setUserDictionary:(NSDictionary*)dict {
551   ASSIGN(userDictionary, dict);
552 }
553 - (NSDictionary *)userDictionary {
554   return self->userDictionary;
555 }
556
557 - (NSString *)name {
558   return self->name;
559 }
560 - (BOOL)isReadOnly {
561   return self->flags.isReadOnly;
562 }
563 - (NSString *)className {
564   return self->className;
565 }
566 - (NSArray *)attributesUsedForLocking {
567   return self->attributesUsedForLocking;
568 }
569 - (NSArray *)classPropertyNames {
570   return self->classPropertyNames;
571 }
572 - (NSArray *)classProperties {
573   return self->classProperties;
574 }
575 - (NSArray *)primaryKeyAttributes {
576   return self->primaryKeyAttributes;
577 }
578 - (NSArray *)primaryKeyAttributeNames {
579   return self->primaryKeyAttributeNames;
580 }
581 - (NSArray *)relationships {
582   return self->relationships;
583 }
584 - (EOModel *)model {
585   return self->model;
586 }
587 - (NSArray *)attributes {
588   return self->attributes;
589 }
590
591 @end /* EOEntity */
592
593
594 @implementation EOEntity (EOEntityCreation)
595
596 + (EOEntity *)entityFromPropertyList:(id)propertyList model:(EOModel *)_model {
597   EOEntity     *entity;
598   NSArray      *array;
599   NSEnumerator *enumerator;
600   id attributePList;
601   id relationshipPList;
602
603   entity = AUTORELEASE([[EOEntity alloc] init]);
604   [entity setCreateMutableObjects:YES];
605
606   entity->name           = RETAIN([propertyList objectForKey:@"name"]);
607   entity->className      = RETAIN([propertyList objectForKey:@"className"]);
608   entity->externalName   = RETAIN([propertyList objectForKey:@"externalName"]);
609   entity->externalQuery  = RETAIN([propertyList objectForKey:@"externalQuery"]);
610   entity->userDictionary = RETAIN([propertyList objectForKey:@"userDictionary"]);
611
612   array      = [propertyList objectForKey:@"attributes"];
613   enumerator = [array objectEnumerator];
614   
615   while ((attributePList = [enumerator nextObject])) {
616     EOAttribute *attribute;
617     
618     attribute = [EOAttribute attributeFromPropertyList:attributePList];
619     
620     if (![entity addAttribute:attribute]) {
621       NSLog(@"duplicate name for attribute '%@' in entity '%@'",
622             [attribute name], [entity name]);
623       [_model errorInReading];
624     }
625   }
626
627   entity->attributesUsedForLocking
628     = RETAIN([propertyList objectForKey:@"attributesUsedForLocking"]);
629   entity->classPropertyNames
630     = RETAIN([propertyList objectForKey:@"classProperties"]);
631
632   if ((attributePList = [propertyList objectForKey:@"primaryKeyAttributes"])) {
633     entity->primaryKeyAttributeNames
634       = RETAIN([attributePList sortedArrayUsingSelector:@selector(compare:)]);
635   }
636
637   else
638     if ((attributePList = [propertyList objectForKey:@"primaryKeyAttribute"]))
639       entity->primaryKeyAttributeNames
640         = RETAIN([NSArray arrayWithObject:attributePList]);
641
642   array = [propertyList objectForKey:@"relationships"];
643   enumerator = [array objectEnumerator];
644   while((relationshipPList = [enumerator nextObject])) {
645     EORelationship *relationship
646       = [EORelationship relationshipFromPropertyList:relationshipPList
647                         model:_model];
648     
649     if(![entity addRelationship:relationship]) {
650       NSLog(@"duplicate name for relationship '%@' in entity '%@'",
651             [relationship name], [entity name]);
652       [_model errorInReading];
653     }
654   }
655
656   [entity setCreateMutableObjects:NO];
657
658   return entity;
659 }
660
661 - (void)replaceStringsWithObjects {
662   NSEnumerator   *enumerator    = nil;
663   EOAttribute    *attribute     = nil;
664   NSString       *attributeName = nil;
665   NSString       *propertyName  = nil;
666   NSMutableArray *array         = nil;
667   int            i, count;
668
669   enumerator           = [self->primaryKeyAttributeNames objectEnumerator];
670   RELEASE(self->primaryKeyAttributes);
671   self->primaryKeyAttributes = AUTORELEASE([NSMutableArray new]);
672   
673   while ((attributeName = [enumerator nextObject])) {
674     attribute = [self attributeNamed:attributeName];
675     
676     if((attribute == nil) || ![self isValidPrimaryKeyAttribute:attribute]) {
677       NSLog(@"invalid attribute name specified as primary key attribute "
678             @"'%s' in entity '%s'", 
679             [attributeName cString], [name cString]);
680       [self->model errorInReading];
681     }
682     else
683       [(NSMutableArray*)self->primaryKeyAttributes addObject:attribute];
684   }
685   self->primaryKeyAttributes = [self->primaryKeyAttributes copy];
686
687   enumerator = [self->classPropertyNames objectEnumerator];
688   RELEASE(self->classProperties);
689   self->classProperties = AUTORELEASE([NSMutableArray new]);
690   while((propertyName = [enumerator nextObject])) {
691     id property;
692
693     property = [self propertyNamed:propertyName];
694     if(!property || ![self isValidClassProperty:property]) {
695       NSLog(@"invalid property '%s' specified as class property in "
696             @"entity '%s'", 
697             [propertyName cString], [name cString]);
698       [self->model errorInReading];
699     }
700     else
701       [(NSMutableArray*)self->classProperties addObject:property];
702   }
703   self->classProperties = [self->classProperties copy];
704
705   array = AUTORELEASE([NSMutableArray new]);
706   [array setArray:self->attributesUsedForLocking];
707   RELEASE(self->attributesUsedForLocking);
708   count = [array count];
709   for(i = 0; i < count; i++) {
710     attributeName = [array objectAtIndex:i];
711     attribute = [self attributeNamed:attributeName];
712     if(!attribute || ![self isValidAttributeUsedForLocking:attribute]) {
713       NSLog(@"invalid attribute specified as attribute used for "
714             @"locking '%@' in entity '%@'", attributeName, name);
715       [self->model errorInReading];
716     }
717     else
718       [array replaceObjectAtIndex:i withObject:attribute];
719   }
720   self->attributesUsedForLocking = [array copy];
721 }
722
723 - (id)propertyList
724 {
725   id propertyList;
726   
727   propertyList = [NSMutableDictionary dictionary];
728   [self encodeIntoPropertyList:propertyList];
729   return propertyList;
730 }
731
732 - (void)setCreateMutableObjects:(BOOL)flag {
733   if(self->flags.createsMutableObjects == flag)
734     return;
735
736   self->flags.createsMutableObjects = flag;
737
738   if(self->flags.createsMutableObjects) {
739     self->attributes    = [AUTORELEASE(self->attributes) mutableCopy];
740     self->relationships = [AUTORELEASE(self->relationships) mutableCopy];
741   }
742   else {
743     self->attributes    = [AUTORELEASE(self->attributes)    copy];
744     self->relationships = [AUTORELEASE(self->relationships) copy];
745   }
746 }
747
748 - (BOOL)createsMutableObjects {
749   return self->flags.createsMutableObjects;
750 }
751
752 #if 0
753 static inline void _printIds(NSArray *a, const char *pfx, const char *indent) {
754   int i;
755   if (pfx    == NULL) pfx    = "";
756   if (indent == NULL) indent = "  ";
757   printf("%s", pfx);
758   for (i = 0; i < [a count]; i++)
759     printf("%s0x%08X\n", indent, (unsigned)[a objectAtIndex:i]);
760 }
761 #endif
762
763 static inline BOOL _containsObject(NSArray *a, id obj) {
764   id (*objAtIdx)(NSArray*, SEL, int idx);
765   register int i;
766   
767   objAtIdx = (void *)[a methodForSelector:@selector(objectAtIndex:)];
768   for (i = [a count] - 1; i >= 0; i--) {
769     register id o;
770
771     o = objAtIdx(a, @selector(objectAtIndex:), i);
772     if (o == obj) return YES;
773   }
774   return NO;
775 }
776
777 - (void)validatePropertiesCache
778 {
779   NSMutableArray *updAttr = [NSMutableArray new];
780   NSMutableArray *updName = [NSMutableArray new];
781   NSMutableArray *fetAttr = [NSMutableArray new];
782   NSMutableArray *fetRels = [NSMutableArray new];
783     
784   int i;
785     
786   [self invalidatePropertiesCache];
787
788 #ifdef DEBUG
789   NSAssert((updAttr != nil) && (updName != nil) &&
790            (fetAttr != nil) && (fetRels != nil),
791            @"allocation of array failed !");
792   
793   NSAssert(self->primaryKeyAttributes,     @"no pkey attributes are set !");
794   NSAssert(self->attributesUsedForLocking, @"no locking attrs are set !");
795   NSAssert(self->classProperties,          @"no class properties are set !");
796 #endif
797
798   //_printIds(self->attributes,               "attrs:\n", "  ");
799   //_printIds(self->attributesUsedForLocking, "lock:\n", "  ");
800
801   for (i = ([self->attributes count] - 1); i >= 0; i--) {
802     EOAttribute *attr = [self->attributes objectAtIndex:i];
803     BOOL pk, lk, cp, sa;
804
805     pk = _containsObject(self->primaryKeyAttributes, attr);
806     lk = _containsObject(self->attributesUsedForLocking, attr);
807     cp = _containsObject(self->classProperties, attr);
808     sa = (![attr isDerived] && ![attr isFlattened]);
809
810     //NSLog(@"attribute %@ pk=%i lk=%i cp=%i sa=%i", [attr name], pk, lk, cp, sa);
811     
812     if ((pk || lk || cp) && (!_containsObject(fetAttr, attr)))
813       [fetAttr addObject:attr];
814     
815     if ((pk || lk || cp) && (sa) && (!_containsObject(updAttr, attr))) {
816       [updAttr addObject:attr];
817       [updName addObject:[attr name]];
818     }
819   }
820     
821   for (i = [relationships count]-1; i >= 0; i--) {
822     id rel = [relationships objectAtIndex:i];
823
824     if (_containsObject(classProperties, rel))
825       [fetRels addObject:rel];
826   }
827
828   RELEASE(self->attributesUsedForInsert);
829   self->attributesUsedForInsert = [[NSArray alloc] initWithArray:updAttr];
830   RELEASE(self->relationsUsedForFetch);
831   self->relationsUsedForFetch   = [[NSArray alloc] initWithArray:fetRels];
832   RELEASE(self->attributesUsedForFetch);
833   self->attributesUsedForFetch  =
834     [[NSArray alloc] initWithArray:
835         [fetAttr sortedArrayUsingFunction:_compareByName context:nil]];
836   RELEASE(self->attributesNamesUsedForInsert);
837   attributesNamesUsedForInsert = [updName copy];
838
839   if ([self->attributesUsedForFetch count] == 0) {
840     NSLog(@"WARNING: entity %@ has no fetch attributes: "
841           @"attributes=%@ !",
842           self,
843           [[(id)self->attributes mappedArrayUsingSelector:@selector(name)]
844                                  componentsJoinedByString:@","]);
845   }
846     
847   RELEASE(updAttr); updAttr = nil;
848   RELEASE(fetAttr); fetAttr = nil;
849   RELEASE(fetRels); fetRels = nil;
850   RELEASE(updName); updName = nil;
851
852   self->flags.isPropertiesCacheValid = YES;
853 }
854
855 - (void)invalidatePropertiesCache {
856   if (flags.isPropertiesCacheValid) {
857     RELEASE(self->attributesUsedForInsert);
858     RELEASE(self->attributesUsedForFetch);
859     RELEASE(self->relationsUsedForFetch);
860     RELEASE(self->attributesNamesUsedForInsert);
861         
862     self->attributesUsedForInsert = nil;
863     self->attributesUsedForFetch = nil;
864     self->relationsUsedForFetch = nil;
865     self->attributesNamesUsedForInsert = nil;
866         
867     flags.isPropertiesCacheValid = NO;
868   }
869 }
870
871 // description
872
873 - (NSString *)description {
874   return [NSString stringWithFormat:
875                      @"<%@[0x%08X]: name=%@ className=%@ tableName=%@ "
876                      @"readOnly=%s>",
877                      NSStringFromClass([self class]), self,
878                      [self name], [self className], [self externalName],
879                      [self isReadOnly] ? "YES" : "NO"
880                    ];
881 }
882
883 @end /* EOEntity (EOEntityCreation) */
884
885
886 @implementation EOEntity(ValuesConversion)
887
888 - (NSDictionary *)convertValuesToModel:(NSDictionary *)aRow {
889   NSMutableDictionary *dict = [NSMutableDictionary dictionary];
890   NSEnumerator        *enumerator = [aRow keyEnumerator];
891   NSString            *key;
892     
893   while ((key = [enumerator nextObject])) {
894     id old = [aRow objectForKey:key];
895     id new = [[self attributeNamed:key] convertValueToModel:old];
896         
897     if (new) [dict setObject:new forKey:key];
898   }
899     
900   return [dict count] ? dict : nil;
901 }
902
903 static int _compareByName(id obj1, id obj2, void * context) {
904   return [[(EOAttribute*)obj1 name] compare:[(EOAttribute*)obj2 name]];
905 }
906
907 @end /* EOAttribute (ValuesConversion) */
908
909 @implementation EOEntity(EOF2Additions)
910
911 - (BOOL)isAbstractEntity {
912   return NO;
913 }
914
915 /* ids */
916
917 - (EOGlobalID *)globalIDForRow:(NSDictionary *)_row {
918   static Class EOKeyGlobalIDClass = Nil;
919   unsigned int keyCount = [self->primaryKeyAttributeNames count];
920   id           values[keyCount];
921   unsigned int i;
922   
923   for (i = 0; i < keyCount; i++) {
924     NSString *attrName;
925
926     attrName  = [self->primaryKeyAttributeNames objectAtIndex:i];
927     values[i] = [_row objectForKey:attrName];
928
929     if (values[i] == nil)
930       return nil;
931   }
932   if (EOKeyGlobalIDClass == Nil) EOKeyGlobalIDClass = [EOKeyGlobalID class];
933
934   return [EOKeyGlobalIDClass globalIDWithEntityName:self->name
935                              keys:&(values[0])
936                              keyCount:keyCount
937                              zone:[self zone]];
938 }
939
940 - (BOOL)isPrimaryKeyValidInObject:(id)_object {
941   unsigned int keyCount = [self->primaryKeyAttributeNames count];
942   unsigned int i;
943   
944   if (_object == nil) return NO;
945   
946   for (i = 0; i < keyCount; i++) {
947     if ([_object valueForKey:[self->primaryKeyAttributeNames objectAtIndex:i]]
948         == nil)
949       return NO;
950   }
951   return YES;
952 }
953
954 /* refs to other models */
955
956 - (NSArray *)externalModelsReferenced {
957   NSEnumerator   *e;
958   EORelationship *relship;
959   NSMutableArray *result;
960   EOModel        *thisModel;
961   
962   thisModel = [self model];
963   result    = nil;
964   
965   e = [self->relationships objectEnumerator];
966   while ((relship = [e nextObject])) {
967     EOEntity *targetEntity;
968     EOModel  *extModel;
969
970     targetEntity = [relship destinationEntity];
971     extModel = [targetEntity model];
972
973     if (extModel != thisModel) {
974       if (result == nil) result = [NSMutableArray array];
975       [result addObject:extModel];
976     }
977   }
978   return result ? result : [NSArray array];
979 }
980
981 /* fetch specs */
982
983 - (EOFetchSpecification *)fetchSpecificationNamed:(NSString *)_name {
984   return nil;
985 }
986 - (NSArray *)fetchSpecificationNames {
987   return nil;
988 }
989
990 /* names */
991
992 - (void)beautifyName {
993   [self setName:[[self name] _beautifyEntityName]];
994 }
995
996 @end /* EOEntity(EOF2Additions) */
997
998 @implementation EOEntity(PropertyListCoding)
999
1000 static inline void _addToPropList(NSMutableDictionary *propertyList,
1001                                   id _value, NSString *key) {
1002   if (_value) [propertyList setObject:_value forKey:key];
1003 }
1004
1005 - (void)encodeIntoPropertyList:(NSMutableDictionary *)_plist {
1006   int i, count;
1007
1008   _addToPropList(_plist, self->name,           @"name");
1009   _addToPropList(_plist, self->className,      @"className");
1010   _addToPropList(_plist, self->externalName,   @"externalName");
1011   _addToPropList(_plist, self->externalQuery,  @"externalQuery");
1012   _addToPropList(_plist, self->userDictionary, @"userDictionary");
1013   
1014   if ((count = [self->attributes count])) {
1015     id attributesPList;
1016
1017     attributesPList = [NSMutableArray array];
1018     for (i = 0; i < count; i++) {
1019       NSMutableDictionary *attributePList;
1020
1021       attributePList = [[NSMutableDictionary alloc] init];
1022       [[self->attributes objectAtIndex:i]
1023                          encodeIntoPropertyList:attributePList];
1024       [attributesPList addObject:attributePList];
1025       RELEASE(attributePList);
1026     }
1027     
1028     _addToPropList(_plist, attributesPList, @"attributes");
1029   }
1030
1031   if ((count = [self->attributesUsedForLocking count])) {
1032     id attributesUsedForLockingPList;
1033
1034     attributesUsedForLockingPList = [NSMutableArray array];
1035     for (i = 0; i < count; i++) {
1036       id attributePList;
1037
1038       attributePList =
1039         [(EOAttribute*)[self->attributesUsedForLocking objectAtIndex:i] name];
1040       [attributesUsedForLockingPList addObject:attributePList];
1041     }
1042     _addToPropList(_plist, attributesUsedForLockingPList,
1043                    @"attributesUsedForLocking");
1044   }
1045
1046   if ((count = [self->classProperties count])) {
1047     id classPropertiesPList = nil;
1048
1049     classPropertiesPList = [NSMutableArray array];
1050     for (i = 0; i < count; i++) {
1051       id classPropertyPList;
1052
1053       classPropertyPList = 
1054         [(EOAttribute*)[self->classProperties objectAtIndex:i] name];
1055       [classPropertiesPList addObject:classPropertyPList];
1056     }
1057     _addToPropList(_plist, classPropertiesPList, @"classProperties");
1058   }
1059
1060   if ((count = [self->primaryKeyAttributes count])) {
1061     id primaryKeyAttributesPList;
1062
1063     primaryKeyAttributesPList = [NSMutableArray array];
1064     for (i = 0; i < count; i++) {
1065       id attributePList;
1066       attributePList =
1067         [(EOAttribute*)[self->primaryKeyAttributes objectAtIndex:i] name];
1068       [primaryKeyAttributesPList addObject:attributePList];
1069     }
1070     _addToPropList(_plist, primaryKeyAttributesPList, @"primaryKeyAttributes");
1071   }
1072
1073   if ((count = [self->relationships count])) {
1074     id relationshipsPList;
1075     
1076     relationshipsPList = [NSMutableArray array];
1077     for (i = 0; i < count; i++) {
1078       NSMutableDictionary *relationshipPList;
1079
1080       relationshipPList = [NSMutableDictionary dictionary];
1081       
1082       [[self->relationships objectAtIndex:i]
1083                             encodeIntoPropertyList:relationshipPList];
1084       [relationshipsPList addObject:relationshipPList];
1085     }
1086     _addToPropList(_plist, relationshipsPList, @"relationships");
1087   }
1088 }
1089
1090 @end /* EOEntity(PropertyListCoding) */
1091
1092 @implementation NSString(EntityBeautify)
1093
1094 - (NSString *)_beautifyEntityName {
1095   if ([self length] == 0)
1096     return @"";
1097   else {
1098     unsigned clen = 0;
1099     char     *s   = NULL;
1100     unsigned cnt, cnt2;
1101
1102     clen = [self cStringLength];
1103 #if GNU_RUNTIME
1104     s = objc_atomic_malloc(clen + 4);
1105 #else
1106     s = malloc(clen + 4);
1107 #endif
1108
1109     [self getCString:s maxLength:clen];
1110     
1111     for (cnt = cnt2 = 0; cnt < clen; cnt++, cnt2++) {
1112       if ((s[cnt] == '_') && (s[cnt + 1] != '\0')) {
1113         s[cnt2] = toupper(s[cnt + 1]);
1114         cnt++;
1115       }
1116       else if ((s[cnt] == '2') && (s[cnt + 1] != '\0')) {
1117         s[cnt2] = s[cnt];
1118         cnt++;
1119         cnt2++;
1120         s[cnt2] = toupper(s[cnt]);
1121       }
1122       else
1123         s[cnt2] = tolower(s[cnt]);
1124     }
1125     s[cnt2] = '\0';
1126
1127     s[0] = toupper(s[0]);
1128
1129 #if !LIB_FOUNDATION_LIBRARY
1130     {
1131       NSString *os;
1132
1133       os = [NSString stringWithCString:s];
1134       free(s);
1135       return os;
1136     }
1137 #else
1138     return [NSString stringWithCStringNoCopy:s freeWhenDone:YES];
1139 #endif
1140   }
1141 }
1142
1143 @end