]> err.no Git - sope/blob - sope-gdl1/FrontBase2/FBValues.m
include config.make in makefiles
[sope] / sope-gdl1 / FrontBase2 / FBValues.m
1 /* 
2    FBValues.m
3
4    Copyright (C) 1999 MDlink online service center GmbH and Helge Hess
5
6    Author: Helge Hess (helge.hess@mdlink.de)
7
8    This file is part of the FB Adaptor Library
9
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Library General Public
12    License as published by the Free Software Foundation; either
13    version 2 of the License, or (at your option) any later version.
14
15    This library is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    Library General Public License for more details.
19
20    You should have received a copy of the GNU Library General Public
21    License along with this library; see the file COPYING.LIB.
22    If not, write to the Free Software Foundation,
23    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 */
25 // $Id: FBValues.m 1 2004-08-20 10:38:46Z znek $
26
27 #include <string.h>
28 #if HAVE_STRINGS_H
29 #  include <strings.h>
30 #endif
31 #include <stdlib.h>
32 #import "common.h"
33 #include "FBException.h"
34 #import <Foundation/NSDate.h>
35
36 static Class NumberClass = Nil;
37
38 @interface FBDataTypeMappingException : FrontBaseException
39 @end
40
41 @interface NSTimeZone(UsedPrivates)
42 - (NSTimeZoneDetail *)timeZoneDetailForDate:(NSDate *)_date;
43 @end
44
45 @implementation FBDataTypeMappingException
46
47 - (id)initWithObject:(id)_obj forAttribute:(EOAttribute *)_attr
48   andFrontBaseType:(int)_fb inChannel:(FrontBaseChannel *)_channel
49 {
50   NSString *typeName = nil;
51
52   typeName =
53     [(id)[[_channel adaptorContext] adaptor] externalNameForTypeCode:_fb];
54
55   if (typeName == nil)
56     typeName = [NSString stringWithFormat:@"Type[%i]", _fb];
57   
58   [self setName:@"FBDataTypeMappingNotSupported"];
59   [self setReason:[NSString stringWithFormat:
60                               @"mapping between %@<Class:%@> and "
61                               @"frontbase type %@ is not supported",
62                               [_obj description],
63                               NSStringFromClass([_obj class]),
64                               typeName]];
65
66   [self setUserInfo:[NSDictionary dictionaryWithObjectsAndKeys:
67                                     _attr,    @"attribute",
68                                     _channel, @"channel",
69                                     _obj,     @"object",
70                                     nil]];
71   return self;
72 }
73
74 @end /* FBDataTypeMappingException */
75
76 static inline void FmtRaiseMapExc(id _object,
77                                   int _fb,
78                                   EOAttribute *_attribute,
79                                   id _channel) {
80   NSLog(@"%s: FmtRaiseMapExc objectClass=%@ object=%@ "
81         @"type=%i typeName=%@ attribute=%@",
82         __PRETTY_FUNCTION__,
83         NSStringFromClass([_object class]),
84         _object,
85         _fb,
86         [(id)[[_channel adaptorContext] adaptor] externalNameForTypeCode:_fb],
87         _attribute);
88   
89   [[[FBDataTypeMappingException alloc]
90                                      initWithObject:_object
91                                      forAttribute:_attribute
92                                      andFrontBaseType:_fb
93                                      inChannel:_channel] raise];
94 }
95 static inline void RaiseMapExc(id _object, int _type,
96                                EOAttribute *_attribute,
97                                id _channel) {
98   NSLog(@"%s: RaiseMapExc objectClass=%@ object=%@ type=%i name=%@ attribute=%@",
99         __PRETTY_FUNCTION__,
100         NSStringFromClass([_object class]),
101         _object,
102         _type,
103         [(id)[[_channel adaptorContext] adaptor] externalNameForTypeCode:_type],
104         _attribute);
105   
106   [[[FBDataTypeMappingException alloc]
107                                      initWithObject:_object
108                                      forAttribute:_attribute
109                                      andFrontBaseType:_type
110                                      inChannel:_channel] raise];
111 }
112
113 @implementation NSString(FBValues)
114
115 + (id)valueFromBytes:(const char *)_bytes length:(unsigned)_length
116   frontBaseType:(int)_fb attribute:(EOAttribute *)_attribute
117   adaptorChannel:(FrontBaseChannel *)_channel
118 {
119   id result = nil;
120     
121   switch (_fb) {
122     case FB_Character:
123     case FB_VCharacter:
124       result = [self stringWithCString:_bytes length:_length];
125       break;
126
127     case FB_BLOB:
128     case FB_CLOB:
129       result = [self stringWithCString:_bytes length:_length];
130       break;
131
132     case FB_SmallInteger:
133       result = [NSString stringWithFormat:@"%i", *(short *)_bytes];
134       break;
135     case FB_Integer:
136       result = [NSString stringWithFormat:@"%i", *(int *)_bytes];
137       break;
138
139     case FB_Real:
140     case FB_Double:
141       result = [NSString stringWithFormat:@"%g", *(double *)_bytes];
142       break;
143
144     case FB_Float:
145       result = [NSString stringWithFormat:@"%g", *(float *)_bytes];
146       break;
147       
148     default:
149       FmtRaiseMapExc(self, _fb, _attribute, _channel);
150   }
151
152   return result;
153 }
154
155 - (NSData *)dataValueForFrontBaseType:(int)_type
156   attribute:(EOAttribute *)_attribute
157 {
158   /* NSString */
159   return [NSData dataWithBytes:[self cString] length:[self cStringLength]];
160 }
161
162 - (NSString *)stringValueForFrontBaseType:(int)_type
163   attribute:(EOAttribute *)_attribute
164 {
165   /* NSString */
166   switch (_type) {
167     case FB_BLOB:
168       return [[NSData dataWithBytes:[self cString] length:[self cStringLength]]
169                       stringValueForFrontBaseType:_type attribute:_attribute];
170
171     case FB_Character:
172     case FB_VCharacter:
173     case FB_CLOB: {
174       id expr = nil;
175
176       expr = [[[EOQuotedExpression alloc] initWithExpression:self
177                                           quote:@"'" escape:@"''"]
178                                    autorelease];
179       expr = [expr expressionValueForContext:nil];
180       return expr;
181     }
182      
183     case FB_Bit:
184     case FB_SmallInteger:
185     case FB_Integer:
186     case FB_Real:
187     case FB_Double:
188     case FB_Numeric:
189     case FB_Decimal:
190       /* NSLog(@"returning %@ for number type", self); */
191       return self;
192   }
193
194   RaiseMapExc(self, _type, _attribute, nil);
195   
196   NSLog(@"impossible condition reached: %@", self);
197   abort();
198   return nil;
199 }
200
201 @end /* NSString(FBValues) */
202
203 @implementation NSNumber(FBValues)
204   
205 #define ReturnNumber(sybtype, def) {\
206   if (valueType == -1) \
207     return [NumberClass def *(sybtype*)_bytes]; \
208   else if (valueType == 'c') \
209     return [NumberClass numberWithChar:*(sybtype*)_bytes]; \
210   else if (valueType == 'C') \
211     return [NumberClass numberWithUnsignedChar:*(sybtype*)_bytes]; \
212   else if (valueType == 's') \
213     return [NumberClass numberWithShort:*(sybtype*)_bytes]; \
214   else if (valueType == 'S') \
215     return [NumberClass numberWithUnsignedShort:*(sybtype*)_bytes]; \
216   else if (valueType == 'i') \
217     return [NumberClass numberWithInt:*(sybtype*)_bytes]; \
218   else if (valueType == 'I') \
219     return [NumberClass numberWithUnsignedInt:*(sybtype*)_bytes]; \
220   else if (valueType == 'f') \
221     return [NumberClass numberWithFloat:*(sybtype*)_bytes]; \
222   else if (valueType == 'd') \
223     return [NumberClass numberWithDouble:*(sybtype*)_bytes]; \
224   else \
225     [NSException raise:@"InvalidValueTypeException" \
226                  format:@"value type %c is not recognized", valueType];\
227     return nil; \
228   }
229
230 + (id)valueFromBytes:(const char *)_bytes length:(unsigned)_length
231   frontBaseType:(int)_fb attribute:(EOAttribute *)_attribute
232   adaptorChannel:(FrontBaseChannel *)_channel
233 {
234   char valueType;
235
236   if ([_attribute valueType])
237     valueType = [[_attribute valueType] cString][0];
238   else
239     valueType = -1;
240   
241   switch (_fb) {
242     case FB_Character:
243     case FB_VCharacter: {
244       switch (valueType) {
245         case 'c': return [NumberClass numberWithChar:atoi(_bytes)];
246         case 'C': return [NumberClass numberWithUnsignedChar:atol(_bytes)];
247         case 's': return [NumberClass numberWithShort:atoi(_bytes)];
248         case 'S': return [NumberClass numberWithUnsignedShort:atol(_bytes)];
249         case 'i': return [NumberClass numberWithInt:atoi(_bytes)];
250         case 'I': return [NumberClass numberWithUnsignedInt:atol(_bytes)];
251         case 'f': return [NumberClass numberWithFloat:atof(_bytes)];
252         case 'd': return [NumberClass numberWithDouble:atof(_bytes)];
253         default:
254           [NSException raise:@"InvalidValueTypeException"
255                        format:@"value type %c is not recognized",
256                          valueType];
257           return nil;
258       }
259     }
260
261     case FB_Boolean: {
262       unsigned char v;
263       v = *(unsigned char *)_bytes;
264       return [NumberClass numberWithBool:v];
265     }
266       
267     case FB_Integer: {
268       int v;
269       v = *(int *)_bytes;
270       return [NumberClass numberWithInt:v];
271     }
272     case FB_SmallInteger: {
273       short v;
274       v = *(short *)_bytes;
275       return [NumberClass numberWithShort:v];
276     }
277
278     case FB_Real:
279     case FB_Double:
280       ReturnNumber(double, numberWithDouble:);
281
282     case FB_Float:
283       ReturnNumber(float, numberWithFloat:);
284
285     case FB_Numeric:
286     case FB_Decimal:
287 #if 0
288       if (_fmt->scale > 0) {
289         ReturnNumber(CS_REAL, numberWithDouble:);
290       }
291       else {
292         ReturnNumber(int, numberWithInt:);
293       }
294 #endif
295       ReturnNumber(double, numberWithDouble:);
296
297     default:
298       FmtRaiseMapExc(self, _fb, _attribute, _channel);
299   }
300
301   [NSException raise:@"InvalidFrontBaseValueStateException"
302                format:@"reached invalid state in sybase NSNumber handler"];
303
304   return nil;
305 }
306
307 #undef ReturnNumber
308
309 - (NSData *)dataValueForFrontBaseType:(int)_type
310   attribute:(EOAttribute *)_attribute
311 {
312   RaiseMapExc(self, _type, _attribute, nil);
313   return nil;
314 }
315
316 - (NSString *)stringValueForFrontBaseType:(int)_type
317   attribute:(EOAttribute *)_attribute
318 {
319   /* NSNumber */
320   switch (_type) {
321     case FB_Boolean:
322     case FB_Bit:
323     case FB_SmallInteger:
324     case FB_Integer:
325     case FB_Real:
326     case FB_Float:
327     case FB_Numeric:
328     case FB_Decimal:
329       return [self stringValue];
330       
331     case FB_Character:
332     case FB_VCharacter:
333       return [NSString stringWithFormat:@"'%s'", [[self stringValue] cString]];
334       
335     default:
336       RaiseMapExc(self, _type, _attribute, nil);
337   }
338   return nil;
339 }
340
341 @end /* NSNumber(FBValues) */
342
343 @implementation NSData(FBValues)
344
345 + (id)valueFromBytes:(const char *)_bytes length:(unsigned)_length
346   frontBaseType:(int)_fb attribute:(EOAttribute *)_attribute
347   adaptorChannel:(FrontBaseChannel *)_channel
348 {
349   switch (_fb) {
350     case FB_Bit:
351     case FB_SmallInteger:
352     case FB_Integer:
353     case FB_Real:
354     case FB_Float:
355     case FB_Numeric:
356     case FB_Decimal:
357     case FB_Character:
358     case FB_VCharacter:
359     case FB_BLOB:
360     case FB_CLOB:
361       return [[[self alloc] initWithBytes:_bytes length:_length] autorelease];
362
363     default:
364       FmtRaiseMapExc(self, _fb, _attribute, _channel);
365   }
366   return nil;
367 }
368
369 - (NSData *)dataValueForFrontBaseType:(int)_type
370   attribute:(EOAttribute *)_attribute
371 {
372   return self;
373 }
374
375 - (NSString *)stringValueForFrontBaseType:(int)_type
376   attribute:(EOAttribute *)_attribute
377 {
378   switch (_type) {
379     case FB_Boolean:
380       return [[NumberClass numberWithChar:*(char *)[self bytes]] stringValue];
381       
382     case FB_SmallInteger:
383       return [[NumberClass numberWithShort:*(short *)[self bytes]] stringValue];
384       
385     case FB_Integer:
386       return [[NumberClass numberWithInt:*(int *)[self bytes]] stringValue];
387
388     case FB_Real:
389     case FB_Double:
390       return [[NumberClass numberWithDouble:*(double *)[self bytes]]
391                            stringValue];
392
393     case FB_Float:
394       return [[NumberClass numberWithFloat:*(float *)[self bytes]] stringValue];
395
396     case FB_Character:
397     case FB_VCharacter:
398     case FB_CLOB:
399     case FB_BLOB: {
400       unsigned   final_length = [self length];
401       char       *cstr = NULL, *tmp = NULL;
402       unsigned   cnt;
403       const char *iBytes = [self bytes];
404
405       if (final_length == 0) return @"NULL";
406
407       final_length = 2 + 2 * final_length + 1;
408       cstr = objc_atomic_malloc(final_length + 4);
409       tmp = cstr + 2;
410
411       cstr[0] = '\0';
412       strcat(cstr, "0x");
413
414       for (cnt = 0; cnt < [self length]; cnt++, tmp += 2)
415         sprintf(tmp, "%02X", (unsigned char)iBytes[cnt]);
416       *tmp = '\0';
417
418       return [[[NSString alloc] initWithCStringNoCopy:cstr length:cstr?strlen(cstr):0 freeWhenDone:YES] autorelease];
419     }
420       
421     default:
422       RaiseMapExc(self, _type, _attribute, nil);
423   }
424   return nil;
425 }
426
427 @end /* NSData(FBValues) */
428
429 //                 1234567890123456789012345
430 // Frontbase Date: 1997-10-21 21:52:26+08:00
431 static NSString *FRONTBASE_DATE_FORMAT = @"%Y-%m-%d %H:%M:%S%z";
432
433 @implementation NSDate(FBValues)
434
435 + (id)valueFromBytes:(const char *)_bytes length:(unsigned)_length
436   frontBaseType:(int)_fb attribute:(EOAttribute *)_attribute
437   adaptorChannel:(FrontBaseChannel *)_channel
438 {
439   /* NSDate */
440   return [NSCalendarDate valueFromBytes:_bytes length:_length
441                          frontBaseType:_fb attribute:_attribute
442                          adaptorChannel:_channel];
443 }
444
445 - (NSData *)dataValueForFrontBaseType:(int)_type
446   attribute:(EOAttribute *)_attr
447 {
448   /* NSDate */
449   NSCalendarDate *cdate;
450
451   cdate = [NSCalendarDate dateWithTimeIntervalSince1970:
452                             [self timeIntervalSince1970]];
453   
454   return [cdate dataValueForFrontBaseType:_type attribute:_attr];
455 }
456
457 - (NSString *)stringValueForFrontBaseType:(int)_type
458   attribute:(EOAttribute *)_attr
459 {
460   /* NSDate */
461   NSCalendarDate *cdate;
462
463   cdate = [NSCalendarDate dateWithTimeIntervalSince1970:
464                             [self timeIntervalSince1970]];
465   
466   return [cdate stringValueForFrontBaseType:_type attribute:_attr];
467 }
468
469 @end
470
471 @implementation NSCalendarDate(FBValues)
472
473 + (id)valueFromBytes:(const char *)_bytes length:(unsigned)_length
474   frontBaseType:(int)_fb attribute:(EOAttribute *)_attribute
475   adaptorChannel:(FrontBaseChannel *)_channel
476 {
477   /* NSCalendarDate */
478   switch (_fb) {
479     case FB_TimestampTZ: {
480       /* a string of length 25 */
481       NSCalendarDate *date;
482       NSString *str, *stampstr, *tzstr;
483
484 #if DEBUG
485       NSAssert2(_length >= 25,
486                 @"byte values for date creation are too short "
487                 @"(len required is 25, got %i), attribute=%@",
488                _length, _attribute);
489       
490       if (_length != 25) {
491         NSLog(@"WARNING: invalid length, should be '25', got %i, attribute %@",
492               _length, _attribute);
493       }
494 #endif
495
496       stampstr = [NSString stringWithCString:_bytes length:19];
497       tzstr    = [NSString stringWithCString:(_bytes + 19) length:3];
498       str      = [NSString stringWithCString:(_bytes + 23) length:2];
499       tzstr    = [tzstr stringByAppendingString:str];
500       str      = [stampstr stringByAppendingString:tzstr];
501
502       date = [NSCalendarDate dateWithString:str
503                              calendarFormat:FRONTBASE_DATE_FORMAT];
504 #if DEBUG
505       if (date == nil) {
506         NSLog(@"ERROR: couldn't construct calendar date from "
507               @"string %@ (src=%@) column %@.%@ "
508               @"(%i bytes), format=%@",
509               str, [NSString stringWithCString:_bytes length:19],
510               [[_attribute entity] externalName],
511               [_attribute columnName],
512               _length, date);
513       }
514 #if 0
515       else {
516         NSLog(@"info: could construct calendar date from string %@ (len=%i)",
517               str, _length);
518       }
519 #endif
520 #endif
521       return date;
522     }
523     
524     case FB_Character:
525     case FB_VCharacter:
526     case FB_Date:
527     case FB_Timestamp: {
528       NSTimeZone     *serverTimeZone;
529       NSCalendarDate *date;
530       NSString       *formattedDate;
531       NSString       *format;
532
533       formattedDate = [NSString stringWithCString:_bytes length:_length];
534
535       serverTimeZone = [_attribute serverTimeZone];
536       format         = [_attribute calendarFormat];
537       
538       if (serverTimeZone == nil)
539         serverTimeZone = [NSTimeZone localTimeZone];
540       if (format == nil)
541         format = FRONTBASE_DATE_FORMAT;
542
543       date = [NSCalendarDate dateWithString:formattedDate
544                              calendarFormat:format];
545       if (date == nil) {
546         NSLog(@"ERROR(%s): could not construct date from "
547               @"value '%@' with format '%@'",
548               __PRETTY_FUNCTION__, formattedDate, format);
549       }
550       return date;
551     }
552     
553     default:
554       FmtRaiseMapExc(self, _fb, _attribute, _channel);
555   }
556   return nil;
557 }
558
559 - (NSData *)dataValueForFrontBaseType:(int)_type
560   attribute:(EOAttribute *)_attr
561 {
562   /* NSCalendarDate */
563   RaiseMapExc(self, _type, _attr, nil);
564   return nil;
565 }
566
567 - (NSString *)stringValueForFrontBaseType:(int)_type
568   attribute:(EOAttribute *)_attr
569 {
570   /* NSCalendarDate */
571
572   self = [self copy]; /* copy to rescue timezone setting */
573   [self autorelease];
574   
575   switch (_type) {
576     case FB_TimestampTZ: {
577       NSTimeZone *serverTimeZone;
578       NSString *str;
579       NSTimeInterval timeZoneOffset;
580       int i, j;
581
582       serverTimeZone = [_attr serverTimeZone];
583
584       if (serverTimeZone == nil)
585         serverTimeZone = [NSTimeZone localTimeZone];
586       
587 #if NeXT_Foundation_LIBRARY
588       timeZoneOffset = [serverTimeZone secondsFromGMTForDate:self];
589 #else
590       timeZoneOffset = [[serverTimeZone timeZoneDetailForDate:self]
591                                         timeZoneSecondsFromGMT];
592 #endif
593
594       [self setTimeZone:serverTimeZone];
595       
596       i = timeZoneOffset > 0;
597       j = (timeZoneOffset > 0 ? timeZoneOffset : -timeZoneOffset) / 60;
598
599       str = [NSString stringWithFormat:
600                         @"TIMESTAMP '%02i-%02i-%02i %02i:%02i:%02i%s%02i:%02i'",
601                         [self yearOfCommonEra],
602                         [self monthOfYear],
603                         [self dayOfMonth],
604                         [self hourOfDay],
605                         [self minuteOfHour],
606                         [self secondOfMinute],
607                         i ? "+":"-",
608                         j / 60, j % 60];
609       return str;
610     }
611     
612     case FB_Date: {
613       NSTimeZone *serverTimeZone;
614       NSString   *format;
615       id         expr;
616
617       serverTimeZone = [_attr serverTimeZone];
618       format         = [_attr calendarFormat];
619       expr           = nil;
620       
621       if (serverTimeZone == nil)
622         serverTimeZone = [NSTimeZone localTimeZone];
623       
624       if (format == nil)
625         format = FRONTBASE_DATE_FORMAT;
626
627       [self setTimeZone:serverTimeZone];
628       
629       expr = [self descriptionWithCalendarFormat:format];
630       expr = [[EOQuotedExpression alloc] initWithExpression:expr
631                                          quote:@"'" escape:@"''"];
632       [expr autorelease];
633       expr = [expr expressionValueForContext:nil];
634       expr = [@"TIMESTAMP " stringByAppendingString:expr];
635
636       return expr;
637     }
638     
639     default:
640       RaiseMapExc(self, _type, _attr, nil);
641   }
642   return nil;
643 }
644
645 @end /* NSCalendarDate(FBValues) */
646
647 @implementation EONull(FBValues)
648
649 - (NSData *)dataValueForFrontBaseType:(int)_type
650   attribute:(EOAttribute *)_attr
651 {
652   return nil;
653 }
654
655 - (NSString *)stringValueForFrontBaseType:(int)_type
656   attribute:(EOAttribute *)_attr
657 {
658   return @"null";
659 }
660
661 @end /* EONull(FBValues) */
662
663 void __init_FBValues(void) {
664   NumberClass = [NSNumber class];
665 }
666
667 void __link_FBValues() {
668   // used to force linking of object file
669   __link_FBValues();
670   __init_FBValues();
671 }