4 Copyright (C) 1996 Free Software Foundation, Inc.
6 Author: Ovidiu Predescu <ovidiu@bx.logicnet.ro>
9 This file is part of the GNUstep Database Library.
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.
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.
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.
27 // $Id: EOAttribute.m 1 2004-08-20 10:38:46Z znek $
30 #import "EOAttribute.h"
33 #import "EORelationship.h"
34 #import "EOExpressionArray.h"
35 #import "EOCustomValues.h"
36 #import <EOControl/EONull.h>
37 #import "EOFExceptions.h"
39 @interface NSString(BeautifyAttributeName)
41 - (NSString *)_beautifyAttributeName;
45 @implementation EOAttribute
47 static NSString *defaultCalendarFormat = @"%b %d %Y %H:%M";
48 static EONull *null = nil;
52 null = [[EONull null] retain];
55 - (id)initWithName:(NSString*)_name {
56 if ((self = [super init])) {
57 ASSIGN(self->name,_name);
63 return [self initWithName:nil];
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);
86 // These methods should be here to let the library work with NeXT foundation
90 - (id)copyWithZone:(NSZone *)_zone {
94 // Is equal only if same name; used to make aliasing ordering stable
96 return [self->name hash];
99 - (BOOL)setName:(NSString*)_name {
100 if([name isEqual:_name])
103 if([entity attributeNamed:_name])
110 + (BOOL)isValidName:(NSString*)_name {
111 return [EOEntity isValidName:_name];
114 - (BOOL)setReadOnly:(BOOL)flag {
115 if(!flag && ([self isDerived] || [self isFlattened]))
117 flags.isReadOnly = flag;
121 - (BOOL)referencesProperty:(id)property {
122 return (flags.isDerived)
123 ? [self->definitionArray indexOfObject:property] != NSNotFound
127 - (void)setDefinition:(NSString *)def {
130 EOEntity *currentEntity;
131 id realAttributeName;
134 [NSException raise:NSInvalidArgumentException
135 format:@"invalid (nil) definition argument!"];
138 self->flags.isDerived = YES;
139 self->flags.isFlattened = NO;
141 ASSIGN(self->definition, def);
143 if ([definition isNameOfARelationshipPath]) {
144 self->flags.isFlattened = YES;
145 defArray = [definition componentsSeparatedByString:@"."];
146 count = [defArray count];
148 RELEASE(self->definitionArray);
149 self->definitionArray = [[NSMutableArray alloc] initWithCapacity:count];
152 currentEntity = self->entity;
154 for (i = 0; i < count - 1; i++) {
155 id relationshipName, relationship;
157 relationshipName = [defArray objectAtIndex:i];
159 if(![EOEntity isValidName:relationshipName]) {
160 [[[InvalidNameException alloc]
161 initWithName:relationshipName] raise];
165 = [currentEntity relationshipNamed:relationshipName];
167 if(relationship == nil) {
168 [[[InvalidPropertyException alloc]
169 initWithName:relationshipName entity:currentEntity] raise];
171 if([relationship isToMany]) {
172 [[[RelationshipMustBeToOneException alloc]
173 initWithName:relationshipName entity:currentEntity] raise];
175 [self->definitionArray addObject:relationship];
176 currentEntity = [relationship destinationEntity];
178 realAttributeName = [defArray lastObject];
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];
187 [self->definitionArray addObject:self->realAttribute];
190 RELEASE(self->definitionArray);
191 self->definitionArray = nil;
192 [localException raise];
197 [self->definitionArray release];
198 self->definitionArray = nil;
199 self->definitionArray
200 = [[EOExpressionArray parseExpression:definition
202 replacePropertyReferences:YES]
207 - (NSString *)expressionValueForContext:(id<EOExpressionContext>)context {
209 ? [context expressionValueForAttribute:self]
213 - (void)setEntity:(EOEntity*)_entity {
214 self->entity = _entity; /* non-retained */
216 - (EOEntity *)entity {
219 - (void)resetEntity {
223 return (self->entity != nil) ? YES : NO;
226 - (void)setCalendarFormat:(NSString*)format {
227 ASSIGN(self->calendarFormat, format);
229 - (NSString *)calendarFormat {
230 return self->calendarFormat;
233 - (void)setClientTimeZone:(NSTimeZone*)tz {
234 ASSIGN(self->clientTimeZone, tz);
236 - (NSTimeZone *)clientTimeZone {
237 return self->clientTimeZone;
240 - (void)setServerTimeZone:(NSTimeZone*)tz {
241 ASSIGN(self->serverTimeZone, tz);
243 - (NSTimeZone *)serverTimeZone {
244 return self->serverTimeZone;
247 - (void)setColumnName:(NSString*)_name {
248 ASSIGN(self->columnName, _name);
250 - (NSString *)columnName {
251 return self->columnName;
254 - (void)setExternalType:(NSString*)type {
255 ASSIGN(self->externalType, type);
257 - (NSString *)externalType {
258 return ((self->externalType == nil) && self->flags.isFlattened)
259 ? [self->realAttribute externalType]
260 : self->externalType;
263 - (void)setValueClassName:(NSString *)_name {
264 ASSIGN(self->valueClassName, _name);
266 - (NSString *)valueClassName {
267 return ((self->valueClassName == nil) && self->flags.isFlattened)
268 ? [self->realAttribute valueClassName]
269 : self->valueClassName;
272 - (void)setValueType:(NSString *)type {
273 ASSIGN(self->valueType, type);
275 - (NSString *)valueType {
276 return ((self->valueType == nil) && self->flags.isFlattened)
277 ? [self->realAttribute valueType]
281 - (void)setInsertFormat:(NSString *)string {
282 ASSIGN(self->insertFormat, string);
284 - (NSString *)insertFormat {
285 return self->insertFormat;
288 - (void)setSelectFormat:(NSString *)string {
289 ASSIGN(self->selectFormat, string);
291 - (NSString *)selectFormat {
292 return self->selectFormat;
295 - (void)setUpdateFormat:(NSString*)string {
296 ASSIGN(self->updateFormat, string);
298 - (NSString *)updateFormat {
299 return self->updateFormat;
302 - (void)setUserDictionary:(NSDictionary *)dict {
303 ASSIGN(self->userDictionary, dict);
305 - (NSDictionary *)userDictionary {
306 return self->userDictionary;
309 + (NSString *)defaultCalendarFormat {
310 return defaultCalendarFormat;
315 - (NSString *)definition {
316 return self->definition;
318 - (NSMutableArray *)definitionArray {
319 return self->definitionArray;
322 return self->flags.isDerived;
324 - (BOOL)isFlattened {
325 return self->flags.isFlattened;
329 return self->flags.isDerived ? YES : self->flags.isReadOnly;
334 - (NSString *)description {
335 return [[self propertyList] description];
338 @end /* EOAttribute */
341 @implementation EOAttribute (EOAttributePrivate)
343 + (EOAttribute*)attributeFromPropertyList:(id)propertyList {
344 EOAttribute *attribute = nil;
345 NSString *timeZoneName;
348 attribute = [[EOAttribute alloc] init];
349 AUTORELEASE(attribute);
351 [attribute setName:[propertyList objectForKey:@"name"]];
352 [attribute setCalendarFormat:[propertyList objectForKey:@"calendarFormat"]];
354 timeZoneName = [propertyList objectForKey:@"clientTimeZone"];
356 [attribute setClientTimeZone:[NSTimeZone timeZoneWithName:timeZoneName]];
358 timeZoneName = [propertyList objectForKey:@"serverTimeZone"];
360 [attribute setServerTimeZone:[NSTimeZone timeZoneWithName:timeZoneName]];
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"]];
371 [attribute setReadOnly:
372 [[propertyList objectForKey:@"isReadOnly"] isEqual:@"Y"]];
374 if ((tmp = [propertyList objectForKey:@"allowsNull"]))
375 [attribute setAllowsNull:[tmp isEqual:@"Y"]];
377 [attribute setAllowsNull:YES];
380 [[propertyList objectForKey:@"width"] unsignedIntValue]];
382 /* Don't call setDefinition: now. The attributes array in
383 entity is not yet set. */
384 attribute->definition = RETAIN([propertyList objectForKey:@"definition"]);
389 /* WARNING: You should call this method from entity after the relationships
390 were constructed and after the `attributes' array contains the real
392 - (void)replaceStringsWithObjects {
393 if(self->definition) {
395 [self setDefinition:self->definition];
397 //CATCH(PropertyDefinitionException)
398 NSLog([localException reason]);
399 [[self->entity model] errorInReading];
406 NSMutableDictionary *propertyList;
408 propertyList = [NSMutableDictionary dictionaryWithCapacity:16];
409 [self encodeIntoPropertyList:propertyList];
413 - (int)compareByName:(EOAttribute *)_other {
414 return [[(EOAttribute *)self name] compare:[_other name]];
417 @end /* EOAttribute (EOAttributePrivate) */
419 @implementation EOAttribute(ValuesConversion)
421 - (id)convertValue:(id)aValue toClass:(Class)aClass forType:(NSString*)_type {
425 if (aValue == [EONull null])
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])
434 // We have to convert the aValue
436 // Try EOCustomValues
437 if ([aValue respondsToSelector:@selector(stringForType:)]) {
438 // Special case if aClass is NSNumber
439 if (aClass == [NSNumber class]) {
441 numberWithString:[aValue stringForType:_type]
445 // Even more Special case if aClass is NSCalendar date
446 if (aClass == [NSCalendarDate class]) {
449 format = [self calendarFormat];
451 format = [EOAttribute defaultCalendarFormat];
452 date = [NSCalendarDate
453 dateWithString:[aValue stringForType:_type]
454 calendarFormat:format];
455 [date setCalendarFormat:format];
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]
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]
479 // Could not convert if got here
483 - (id)convertValueToModel:(id)aValue {
487 // Check value class from attribute
488 aValueClassName = [self valueClassName];
489 aValueClass = NSClassFromString(aValueClassName);
490 if (aValueClass == Nil)
493 return [self convertValue:aValue
494 toClass:aValueClass forType:[self valueType]];
499 @implementation NSString (EOAttributeTypeCheck)
501 - (BOOL)isNameOfARelationshipPath {
503 char buf[[self cStringLength] + 1];
507 [self getCString:buf];
509 if(!isalnum((int)*s) && *s != '@' && *s != '_' && *s != '#')
513 if(!isalnum((int)*s) && *s != '@' && *s != '_' && *s != '#' && *s != '$'
525 @implementation EOAttribute(PropertyListCoding)
527 static inline void _addToPropList(NSMutableDictionary *propertyList,
528 id _value, NSString *key) {
529 if (_value) [propertyList setObject:_value forKey:key];
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");
545 if (self->clientTimeZone) {
546 #if !LIB_FOUNDATION_LIBRARY
547 [_plist setObject:[self->clientTimeZone name]
548 forKey:@"clientTimeZone"];
550 [_plist setObject:[self->clientTimeZone timeZoneName]
551 forKey:@"clientTimeZone"];
554 if (self->serverTimeZone) {
555 #if !LIB_FOUNDATION_LIBRARY
556 [_plist setObject:[self->serverTimeZone name]
557 forKey:@"serverTimeZone"];
559 [_plist setObject:[self->serverTimeZone timeZoneName]
560 forKey:@"serverTimeZone"];
564 if (self->width != 0) {
565 [_plist setObject:[NSNumber numberWithUnsignedInt:self->width]
569 if (self->flags.isReadOnly) {
570 [_plist setObject:[NSString stringWithCString:"Y"]
571 forKey:@"isReadOnly"];
573 if (self->flags.allowsNull) {
574 [_plist setObject:[NSString stringWithCString:"Y"]
575 forKey:@"allowsNull"];
579 @end /* EOAttribute(PropertyListCoding) */
581 @implementation EOAttribute(EOF2Additions)
583 - (void)beautifyName {
584 [self setName:[[self name] _beautifyAttributeName]];
589 - (void)setAllowsNull:(BOOL)_flag {
590 self->flags.allowsNull = _flag ? 1 : 0;
593 return self->flags.allowsNull ? YES : NO;
596 - (void)setWidth:(unsigned)_width {
597 self->width = _width;
603 - (NSException *)validateValue:(id *)_value {
604 if (_value == NULL) return nil;
606 /* check NULL constraint */
607 if (!self->flags.allowsNull) {
608 if ((*_value == nil) || (*_value == null)) {
612 ui = [NSDictionary dictionaryWithObjectsAndKeys:
613 *_value ? *_value : null, @"value",
617 e = [NSException exceptionWithName:@"EOValidationException"
618 reason:@"violated not-null constraint"
624 /* check width constraint */
626 if (self->width != 0) {
627 static Class NSDataClass = Nil;
628 static Class NSStringClass = Nil;
630 if (NSDataClass == nil) NSDataClass = [NSData class];
631 if (NSStringClass == nil) NSStringClass = [NSString class];
633 if ([[*_value class] isKindOfClass:NSDataClass]) {
636 len = [*_value length];
637 if (len > self->width) {
641 ui = [NSDictionary dictionaryWithObjectsAndKeys:
642 [NSNumber numberWithUnsignedInt:self->width],
644 [NSNumber numberWithUnsignedInt:len], @"width",
645 *_value ? *_value : null, @"value",
649 e = [NSException exceptionWithName:@"EOValidationException"
650 reason:@"data value exceeds allowed attribute width"
655 else if ([[*_value class] isKindOfClass:NSStringClass]) {
658 len = [*_value cStringLength];
659 if (len > self->width) {
663 ui = [NSDictionary dictionaryWithObjectsAndKeys:
664 [NSNumber numberWithUnsignedInt:self->width],
666 [NSNumber numberWithUnsignedInt:len], @"width",
667 *_value ? *_value : null, @"value",
671 e = [NSException exceptionWithName:@"EOValidationException"
672 reason:@"string value exceeds allowed attribute width"
682 - (NSString *)readFormat {
685 - (NSString *)writeFormat {
689 @end /* EOAttribute(EOF2Additions) */
691 @implementation NSString(BeautifyAttributeName)
693 - (NSString *)_beautifyAttributeName {
698 if ([self length] == 0)
701 clen = [self cStringLength];
703 s = objc_atomic_malloc(clen + 4);
705 s = malloc(clen + 4);
708 [self getCString:s maxLength:clen];
710 for (cnt = cnt2 = 0; cnt < clen; cnt++, cnt2++) {
711 if ((s[cnt] == '_') && (s[cnt + 1] != '\0')) {
712 s[cnt2] = toupper(s[cnt + 1]);
715 else if ((s[cnt] == '2') && (s[cnt + 1] != '\0')) {
719 s[cnt2] = toupper(s[cnt]);
722 s[cnt2] = tolower(s[cnt]);
726 #if !LIB_FOUNDATION_LIBRARY
730 os = [NSString stringWithCString:s];
735 return [NSString stringWithCStringNoCopy:s freeWhenDone:YES];
739 @end /* NSString(BeautifyAttributeName) */