]> err.no Git - sope/blob - sope-gdl1/GDLAccess/EOAttribute.m
Xcode updates and updates for SOPE:X 2.x
[sope] / sope-gdl1 / GDLAccess / EOAttribute.m
1 /* 
2    EOAttributeOrdering.m
3
4    Copyright (C) 1996 Free Software Foundation, Inc.
5
6    Author: Ovidiu Predescu <ovidiu@bx.logicnet.ro>
7    Date: 1996
8
9    This file is part of the GNUstep Database Library.
10
11    This library is free software; you can redistribute it and/or
12    modify it under the terms of the GNU Library General Public
13    License as published by the Free Software Foundation; either
14    version 2 of the License, or (at your option) any later version.
15
16    This library is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19    Library General Public License for more details.
20
21    You should have received a copy of the GNU Library General Public
22    License along with this library; see the file COPYING.LIB.
23    If not, write to the Free Software Foundation,
24    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26
27 // $Id: EOAttribute.m 1 2004-08-20 10:38:46Z znek $
28
29 #import "common.h"
30 #import "EOAttribute.h"
31 #import "EOModel.h"
32 #import "EOEntity.h"
33 #import "EORelationship.h"
34 #import "EOExpressionArray.h"
35 #import "EOCustomValues.h"
36 #import <EOControl/EONull.h>
37 #import "EOFExceptions.h"
38
39 @interface NSString(BeautifyAttributeName)
40
41 - (NSString *)_beautifyAttributeName;
42
43 @end
44
45 @implementation EOAttribute
46
47 static NSString *defaultCalendarFormat = @"%b %d %Y %H:%M";
48 static EONull   *null = nil;
49
50 + (void)initialize {
51   if (null == nil)
52     null = [[EONull null] retain];
53 }
54
55 - (id)initWithName:(NSString*)_name {
56   if ((self = [super init])) {
57     ASSIGN(self->name,_name);
58     self->entity = nil;
59   }
60   return self;
61 }
62 - (id)init {
63   return [self initWithName:nil];
64 }
65
66 - (void)dealloc {
67   RELEASE(self->name);
68   RELEASE(self->calendarFormat);
69   RELEASE(self->clientTimeZone);
70   RELEASE(self->serverTimeZone);
71   RELEASE(self->columnName);
72   RELEASE(self->definition);
73   RELEASE(self->externalType);
74   RELEASE(self->valueClassName);
75   RELEASE(self->valueType);
76   RELEASE(self->insertFormat);
77   RELEASE(self->selectFormat);
78   RELEASE(self->updateFormat);
79   RELEASE(self->userDictionary);
80   self->entity = nil; /* non-retained */
81   RELEASE(self->definitionArray);
82   RELEASE(self->realAttribute);
83   [super dealloc];
84 }
85
86 // These methods should be here to let the library work with NeXT foundation
87 - (id)copy {
88   return RETAIN(self);
89 }
90 - (id)copyWithZone:(NSZone *)_zone {
91   return RETAIN(self);
92 }
93
94 // Is equal only if same name; used to make aliasing ordering stable
95 - (unsigned)hash {
96   return [self->name hash];
97 }
98
99 - (BOOL)setName:(NSString*)_name {
100   if([name isEqual:_name])
101     return YES;
102
103   if([entity attributeNamed:_name])
104     return NO;
105
106   ASSIGN(name, _name);
107   return YES;
108 }
109
110 + (BOOL)isValidName:(NSString*)_name {
111   return [EOEntity isValidName:_name];
112 }
113
114 - (BOOL)setReadOnly:(BOOL)flag {
115   if(!flag && ([self isDerived] || [self isFlattened]))
116     return NO;
117   flags.isReadOnly = flag;
118   return YES;
119 }
120
121 - (BOOL)referencesProperty:(id)property {
122   return (flags.isDerived)
123     ? [self->definitionArray indexOfObject:property] != NSNotFound
124     : NO;
125 }
126
127 - (void)setDefinition:(NSString *)def {
128   NSArray  *defArray;
129   int      i, count;
130   EOEntity *currentEntity;
131   id       realAttributeName;
132
133   if (def == nil) {
134     [NSException raise:NSInvalidArgumentException
135                  format:@"invalid (nil) definition argument!"];
136   }
137   
138   self->flags.isDerived = YES;
139   self->flags.isFlattened = NO;
140   
141   ASSIGN(self->definition, def);
142
143   if ([definition isNameOfARelationshipPath]) {
144     self->flags.isFlattened = YES;
145     defArray        = [definition componentsSeparatedByString:@"."];
146     count           = [defArray count];
147     
148     RELEASE(self->definitionArray);
149     self->definitionArray = [[NSMutableArray alloc] initWithCapacity:count];
150     
151     NS_DURING {
152       currentEntity = self->entity;
153       
154       for (i = 0; i < count - 1; i++) {
155         id relationshipName, relationship;
156
157         relationshipName = [defArray objectAtIndex:i];
158         
159         if(![EOEntity isValidName:relationshipName]) {
160           [[[InvalidNameException alloc]
161              initWithName:relationshipName] raise];
162         }
163     
164         relationship
165           = [currentEntity relationshipNamed:relationshipName];
166         
167         if(relationship == nil) {
168           [[[InvalidPropertyException alloc]
169              initWithName:relationshipName entity:currentEntity] raise];
170         }
171         if([relationship isToMany]) {
172           [[[RelationshipMustBeToOneException alloc]
173              initWithName:relationshipName entity:currentEntity] raise];
174         }
175         [self->definitionArray addObject:relationship];
176         currentEntity = [relationship destinationEntity];
177       }
178       realAttributeName = [defArray lastObject];
179
180       RELEASE(self->realAttribute);
181       self->realAttribute =
182         RETAIN([currentEntity attributeNamed:realAttributeName]);
183       if (self->realAttribute == nil) {
184         [[[InvalidPropertyException alloc]
185            initWithName:realAttributeName entity:currentEntity] raise];
186       }
187       [self->definitionArray addObject:self->realAttribute];
188     }
189     NS_HANDLER {
190       RELEASE(self->definitionArray);
191       self->definitionArray = nil;
192       [localException raise];
193     }
194     NS_ENDHANDLER;
195   }
196   else {
197     [self->definitionArray release];
198     self->definitionArray = nil;
199     self->definitionArray
200       = [[EOExpressionArray parseExpression:definition
201                                   entity:entity
202                                   replacePropertyReferences:YES]
203           retain];
204   }
205 }
206
207 - (NSString *)expressionValueForContext:(id<EOExpressionContext>)context {
208   return (context)
209     ? [context expressionValueForAttribute:self]
210     : columnName;
211 }
212
213 - (void)setEntity:(EOEntity*)_entity {
214   self->entity = _entity; /* non-retained */
215 }
216 - (EOEntity *)entity {
217   return self->entity;
218 }
219 - (void)resetEntity {
220   self->entity = nil;
221 }
222 - (BOOL)hasEntity {
223   return (self->entity != nil) ? YES : NO;
224 }
225
226 - (void)setCalendarFormat:(NSString*)format {
227   ASSIGN(self->calendarFormat, format);
228 }
229 - (NSString *)calendarFormat {
230   return self->calendarFormat;
231 }
232
233 - (void)setClientTimeZone:(NSTimeZone*)tz {
234   ASSIGN(self->clientTimeZone, tz);
235 }
236 - (NSTimeZone *)clientTimeZone {
237   return self->clientTimeZone;
238 }
239
240 - (void)setServerTimeZone:(NSTimeZone*)tz {
241   ASSIGN(self->serverTimeZone, tz);
242 }
243 - (NSTimeZone *)serverTimeZone {
244   return self->serverTimeZone;
245 }
246
247 - (void)setColumnName:(NSString*)_name {
248   ASSIGN(self->columnName, _name);
249 }
250 - (NSString *)columnName {
251   return self->columnName;
252 }
253
254 - (void)setExternalType:(NSString*)type {
255   ASSIGN(self->externalType, type);
256 }
257 - (NSString *)externalType {
258   return ((self->externalType == nil) && self->flags.isFlattened)
259        ? [self->realAttribute externalType]
260        : self->externalType;
261 }
262
263 - (void)setValueClassName:(NSString *)_name {
264   ASSIGN(self->valueClassName, _name);
265 }
266 - (NSString *)valueClassName {
267   return ((self->valueClassName == nil) && self->flags.isFlattened)
268     ? [self->realAttribute valueClassName]
269     : self->valueClassName;
270 }
271
272 - (void)setValueType:(NSString *)type {
273   ASSIGN(self->valueType, type);
274 }
275 - (NSString *)valueType {
276   return ((self->valueType == nil) && self->flags.isFlattened)
277     ? [self->realAttribute valueType]
278     : self->valueType;
279 }
280
281 - (void)setInsertFormat:(NSString *)string {
282   ASSIGN(self->insertFormat, string);
283 }
284 - (NSString *)insertFormat {
285   return self->insertFormat;
286 }
287
288 - (void)setSelectFormat:(NSString *)string {
289   ASSIGN(self->selectFormat, string);
290 }
291 - (NSString *)selectFormat {
292   return self->selectFormat;
293 }
294
295 - (void)setUpdateFormat:(NSString*)string {
296   ASSIGN(self->updateFormat, string);
297 }
298 - (NSString *)updateFormat {
299   return self->updateFormat;
300 }
301
302 - (void)setUserDictionary:(NSDictionary *)dict {
303   ASSIGN(self->userDictionary, dict);
304 }
305 - (NSDictionary *)userDictionary {
306   return self->userDictionary;
307 }
308
309 + (NSString *)defaultCalendarFormat {
310   return defaultCalendarFormat;
311 }
312 - (NSString *)name {
313   return self->name;
314 }
315 - (NSString *)definition {
316   return self->definition;
317 }
318 - (NSMutableArray *)definitionArray {
319   return self->definitionArray;
320 }
321 - (BOOL)isDerived {
322   return self->flags.isDerived;
323 }
324 - (BOOL)isFlattened {
325   return self->flags.isFlattened;
326 }
327
328 - (BOOL)isReadOnly {
329   return self->flags.isDerived ? YES : self->flags.isReadOnly;
330 }
331
332 /* description */
333
334 - (NSString *)description {
335   return [[self propertyList] description];
336 }
337
338 @end /* EOAttribute */
339
340
341 @implementation EOAttribute (EOAttributePrivate)
342
343 + (EOAttribute*)attributeFromPropertyList:(id)propertyList {
344   EOAttribute *attribute = nil;
345   NSString    *timeZoneName;
346   id          tmp;
347
348   attribute = [[EOAttribute alloc] init];
349   AUTORELEASE(attribute);
350
351   [attribute setName:[propertyList objectForKey:@"name"]];
352   [attribute setCalendarFormat:[propertyList objectForKey:@"calendarFormat"]];
353
354   timeZoneName = [propertyList objectForKey:@"clientTimeZone"];
355   if (timeZoneName)
356     [attribute setClientTimeZone:[NSTimeZone timeZoneWithName:timeZoneName]];
357
358   timeZoneName = [propertyList objectForKey:@"serverTimeZone"];
359   if (timeZoneName)
360     [attribute setServerTimeZone:[NSTimeZone timeZoneWithName:timeZoneName]];
361   
362   [attribute setColumnName:    [propertyList objectForKey:@"columnName"]];
363   [attribute setExternalType:  [propertyList objectForKey:@"externalType"]];
364   [attribute setValueClassName:[propertyList objectForKey:@"valueClassName"]];
365   [attribute setValueType:     [propertyList objectForKey:@"valueType"]];
366   [attribute setInsertFormat:  [propertyList objectForKey:@"insertFormat"]];
367   [attribute setSelectFormat:  [propertyList objectForKey:@"selectFormat"]];
368   [attribute setUpdateFormat:  [propertyList objectForKey:@"updateFormat"]];
369   [attribute setUserDictionary:[propertyList objectForKey:@"userDictionary"]];
370   
371   [attribute setReadOnly:
372                  [[propertyList objectForKey:@"isReadOnly"] isEqual:@"Y"]];
373
374   if ((tmp = [propertyList objectForKey:@"allowsNull"]))
375     [attribute setAllowsNull:[tmp isEqual:@"Y"]];
376   else
377     [attribute setAllowsNull:YES];
378   
379   [attribute setWidth:
380                [[propertyList objectForKey:@"width"] unsignedIntValue]];
381
382   /* Don't call setDefinition: now. The attributes array in
383      entity is not yet set. */
384   attribute->definition = RETAIN([propertyList objectForKey:@"definition"]);
385
386   return attribute;
387 }
388
389 /* WARNING: You should call this method from entity after the relationships
390    were constructed and after the `attributes' array contains the real
391    attributes. */
392 - (void)replaceStringsWithObjects {
393   if(self->definition) {
394     NS_DURING
395       [self setDefinition:self->definition];
396     NS_HANDLER {
397       //CATCH(PropertyDefinitionException)
398       NSLog([localException reason]);
399       [[self->entity model] errorInReading];
400     }
401     NS_ENDHANDLER;
402   }
403 }
404
405 - (id)propertyList {
406   NSMutableDictionary *propertyList;
407
408   propertyList = [NSMutableDictionary dictionaryWithCapacity:16];
409   [self encodeIntoPropertyList:propertyList];
410   return propertyList;
411 }
412
413 - (int)compareByName:(EOAttribute *)_other {
414   return [[(EOAttribute *)self name] compare:[_other name]];
415 }
416
417 @end /* EOAttribute (EOAttributePrivate) */
418
419 @implementation EOAttribute(ValuesConversion)
420
421 - (id)convertValue:(id)aValue toClass:(Class)aClass forType:(NSString*)_type {
422   // Check nil/EONull
423   if (aValue == nil)
424     return nil;
425   if (aValue == [EONull null])
426     return aValue;
427     
428     // Check if we need conversion; we use is kind of because 
429     // a string is not a NSString but some concrete class, so is NSData,
430     // NSNumber and may be other classes
431   if ([aValue isKindOfClass:aClass])
432     return aValue;
433     
434     // We have to convert the aValue
435     
436     // Try EOCustomValues
437   if ([aValue respondsToSelector:@selector(stringForType:)]) {
438     // Special case if aClass is NSNumber
439     if (aClass == [NSNumber class]) {
440       return [NSNumber
441                       numberWithString:[aValue stringForType:_type]
442                       type:_type];
443     }
444         
445     // Even more Special case if aClass is NSCalendar date
446     if (aClass == [NSCalendarDate class]) {
447       id format, date;
448             
449       format = [self calendarFormat];
450       if (format == nil)
451         format = [EOAttribute defaultCalendarFormat];
452       date = [NSCalendarDate 
453                              dateWithString:[aValue stringForType:_type]
454                              calendarFormat:format];
455       [date setCalendarFormat:format];
456       return date;
457     }
458         
459     // See if we can alloc a new aValue and initilize it
460     if ([aClass instancesRespondToSelector:
461                 @selector(initWithString:type:)]) {
462       return AUTORELEASE([[aClass alloc] 
463                                   initWithString:[aValue stringForType:_type]
464                                   type:_type]);
465     }
466   }
467     
468   // Try EODatabaseCustomValues
469   if ([aValue respondsToSelector:@selector(dataForType:)]) {
470     // See if we can alloc a new aValue and initilize it
471     if ([aClass instancesRespondToSelector:
472                 @selector(initWithData:type:)]) {
473       return AUTORELEASE([[aClass alloc] 
474                                   initWithData:[aValue dataForType:_type]
475                                   type:_type]);
476     }
477   }
478     
479   // Could not convert if got here
480   return nil;
481 }
482
483 - (id)convertValueToModel:(id)aValue {
484   id aValueClassName;
485   Class aValueClass;
486     
487   // Check value class from attribute
488   aValueClassName = [self valueClassName];
489   aValueClass     = NSClassFromString(aValueClassName);
490   if (aValueClass == Nil)
491     return aValue;
492     
493   return [self convertValue:aValue 
494                toClass:aValueClass forType:[self valueType]];
495 }
496
497 @end
498
499 @implementation NSString (EOAttributeTypeCheck)
500
501 - (BOOL)isNameOfARelationshipPath {
502   BOOL result = NO;
503   char buf[[self cStringLength] + 1];
504   const char *s;
505   
506   s = buf;
507   [self getCString:buf];
508   
509   if(!isalnum((int)*s) && *s != '@' && *s != '_' && *s != '#')
510     return NO;
511
512   for(++s; *s; s++) {
513     if(!isalnum((int)*s) && *s != '@' && *s != '_' && *s != '#' && *s != '$'
514        && *s != '.')
515       return NO;
516     if(*s == '.')
517       result = YES;
518   }
519
520   return result;
521 }
522
523 @end
524
525 @implementation EOAttribute(PropertyListCoding)
526
527 static inline void _addToPropList(NSMutableDictionary *propertyList,
528                                   id _value, NSString *key) {
529   if (_value) [propertyList setObject:_value forKey:key];
530 }
531
532 - (void)encodeIntoPropertyList:(NSMutableDictionary *)_plist {
533   _addToPropList(_plist, self->name,           @"name");
534   _addToPropList(_plist, self->calendarFormat, @"calendarFormat");
535   _addToPropList(_plist, self->columnName,     @"columnName");
536   _addToPropList(_plist, self->definition,     @"definition");
537   _addToPropList(_plist, self->externalType,   @"externalType");
538   _addToPropList(_plist, self->valueClassName, @"valueClassName");
539   _addToPropList(_plist, self->valueType,      @"valueType");
540   _addToPropList(_plist, self->insertFormat,   @"insertFormat");
541   _addToPropList(_plist, self->selectFormat,   @"selectFormat");
542   _addToPropList(_plist, self->updateFormat,   @"updateFormat");
543   _addToPropList(_plist, self->userDictionary, @"userDictionary");
544   
545   if (self->clientTimeZone) {
546 #if !LIB_FOUNDATION_LIBRARY
547     [_plist setObject:[self->clientTimeZone name]
548             forKey:@"clientTimeZone"];
549 #else
550     [_plist setObject:[self->clientTimeZone timeZoneName]
551             forKey:@"clientTimeZone"];
552 #endif
553   }
554   if (self->serverTimeZone) {
555 #if !LIB_FOUNDATION_LIBRARY
556     [_plist setObject:[self->serverTimeZone name]
557             forKey:@"serverTimeZone"];
558 #else
559     [_plist setObject:[self->serverTimeZone timeZoneName]
560             forKey:@"serverTimeZone"];
561 #endif
562   }
563
564   if (self->width != 0) {
565     [_plist setObject:[NSNumber numberWithUnsignedInt:self->width]
566             forKey:@"width"];
567   }
568   
569   if (self->flags.isReadOnly) {
570     [_plist setObject:[NSString stringWithCString:"Y"]
571             forKey:@"isReadOnly"];
572   }
573   if (self->flags.allowsNull) {
574     [_plist setObject:[NSString stringWithCString:"Y"]
575             forKey:@"allowsNull"];
576   }
577 }
578
579 @end /* EOAttribute(PropertyListCoding) */
580
581 @implementation EOAttribute(EOF2Additions)
582
583 - (void)beautifyName {
584   [self setName:[[self name] _beautifyAttributeName]];
585 }
586
587 /* constraints */
588
589 - (void)setAllowsNull:(BOOL)_flag {
590   self->flags.allowsNull = _flag ? 1 : 0;
591 }
592 - (BOOL)allowsNull {
593   return self->flags.allowsNull ? YES : NO;
594 }
595
596 - (void)setWidth:(unsigned)_width {
597   self->width = _width;
598 }
599 - (unsigned)width {
600   return self->width;
601 }
602
603 - (NSException *)validateValue:(id *)_value {
604   if (_value == NULL) return nil;
605   
606   /* check NULL constraint */
607   if (!self->flags.allowsNull) {
608     if ((*_value == nil) || (*_value == null)) {
609       NSException *e;
610       NSDictionary *ui;
611       
612       ui = [NSDictionary dictionaryWithObjectsAndKeys:
613                            *_value ? *_value : null, @"value",
614                            self,                     @"attribute",
615                            nil];
616       
617       e = [NSException exceptionWithName:@"EOValidationException"
618                        reason:@"violated not-null constraint"
619                        userInfo:ui];
620       return e;
621     }
622   }
623   
624   /* check width constraint */
625   
626   if (self->width != 0) {
627     static Class NSDataClass   = Nil;
628     static Class NSStringClass = Nil;
629
630     if (NSDataClass   == nil) NSDataClass   = [NSData   class];
631     if (NSStringClass == nil) NSStringClass = [NSString class];
632     
633     if ([[*_value class] isKindOfClass:NSDataClass]) {
634       unsigned len;
635       
636       len = [*_value length];
637       if (len > self->width) {
638         NSException *e;
639         NSDictionary *ui;
640         
641         ui = [NSDictionary dictionaryWithObjectsAndKeys:
642                              [NSNumber numberWithUnsignedInt:self->width],
643                              @"maxWidth",
644                              [NSNumber numberWithUnsignedInt:len], @"width",
645                              *_value ? *_value : null,             @"value",
646                              self,                                 @"attribute",
647                              nil];
648         
649         e = [NSException exceptionWithName:@"EOValidationException"
650                          reason:@"data value exceeds allowed attribute width"
651                          userInfo:ui];
652         return e;
653       }
654     }
655     else if ([[*_value class] isKindOfClass:NSStringClass]) {
656       unsigned len;
657       
658       len = [*_value cStringLength];
659       if (len > self->width) {
660         NSException *e;
661         NSDictionary *ui;
662         
663         ui = [NSDictionary dictionaryWithObjectsAndKeys:
664                              [NSNumber numberWithUnsignedInt:self->width],
665                              @"maxWidth",
666                              [NSNumber numberWithUnsignedInt:len], @"width",
667                              *_value ? *_value : null,             @"value",
668                              self,                                 @"attribute",
669                              nil];
670         
671         e = [NSException exceptionWithName:@"EOValidationException"
672                          reason:@"string value exceeds allowed attribute width"
673                          userInfo:ui];
674         return e;
675       }
676     }
677   }
678   
679   return nil;
680 }
681
682 - (NSString *)readFormat {
683   return nil;
684 }
685 - (NSString *)writeFormat {
686   return nil;
687 }
688
689 @end /* EOAttribute(EOF2Additions) */
690
691 @implementation NSString(BeautifyAttributeName)
692
693 - (NSString *)_beautifyAttributeName {
694   unsigned clen = 0;
695   char     *s   = NULL;
696   unsigned cnt, cnt2;
697
698   if ([self length] == 0)
699     return @"";
700
701   clen = [self cStringLength];
702 #if GNU_RUNTIME
703   s = objc_atomic_malloc(clen + 4);
704 #else
705   s = malloc(clen + 4);
706 #endif
707
708   [self getCString:s maxLength:clen];
709     
710   for (cnt = cnt2 = 0; cnt < clen; cnt++, cnt2++) {
711       if ((s[cnt] == '_') && (s[cnt + 1] != '\0')) {
712         s[cnt2] = toupper(s[cnt + 1]);
713         cnt++;
714       }
715       else if ((s[cnt] == '2') && (s[cnt + 1] != '\0')) {
716         s[cnt2] = s[cnt];
717         cnt++;
718         cnt2++;
719         s[cnt2] = toupper(s[cnt]);
720       }
721       else
722         s[cnt2] = tolower(s[cnt]);
723   }
724   s[cnt2] = '\0';
725
726 #if !LIB_FOUNDATION_LIBRARY
727   {
728       NSString *os;
729
730       os = [NSString stringWithCString:s];
731       free(s);
732       return os;
733   }
734 #else
735   return [NSString stringWithCStringNoCopy:s freeWhenDone:YES];
736 #endif
737 }
738
739 @end /* NSString(BeautifyAttributeName) */