]> err.no Git - scalable-opengroupware.org/blob - SoObjects/SOGo/AgenorUserDefaults.m
e7031c7c7269cb4a16e67631b3a7716a451da3e1
[scalable-opengroupware.org] / SoObjects / SOGo / AgenorUserDefaults.m
1 /*
2   Copyright (C) 2005 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
6   OGo is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with OGo; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #import <Foundation/NSValue.h>
23 #import <GDLContentStore/GCSChannelManager.h>
24 #import <GDLContentStore/NSURL+GCS.h>
25 #import <GDLAccess/EOAdaptorChannel.h>
26 #import <GDLAccess/EOAdaptorContext.h>
27 #import <GDLAccess/EOAttribute.h>
28
29 #import "common.h"
30 #import "AgenorUserDefaults.h"
31
32 @implementation AgenorUserDefaults
33
34 static NSString *uidColumnName = @"uid";
35
36 - (id) initWithTableURL: (NSURL *) tableURL
37                     uid: (NSString *) userID
38               fieldName: (NSString *) defaultsFieldName
39 {
40   if ((self = [super init]))
41     {
42       if (tableURL && [userID length] > 0
43           && [defaultsFieldName length] > 0)
44         {
45           parent = [[NSUserDefaults standardUserDefaults] retain];
46           fieldName = [defaultsFieldName copy];
47           url = [tableURL copy];
48           uid = [userID copy];
49         }
50       else
51         {
52           [self errorWithFormat: @"missing arguments"];
53           [self release];
54           self = nil;
55         }
56     }
57
58   return self;
59 }
60
61 - (id) init
62 {
63   [self release];
64
65   return nil;
66 }
67
68 - (void) dealloc
69 {
70   [values release];
71   [lastFetch release];
72   [parent release];
73   [url release];
74   [uid release];
75   [fieldName release];
76   [super dealloc];
77 }
78
79 /* accessors */
80
81 - (NSURL *) tableURL
82 {
83   return url;
84 }
85
86 - (NSString *) uid
87 {
88   return uid;
89 }
90
91 - (NSString *) fieldName
92 {
93   return fieldName;
94 }
95
96 - (NSUserDefaults *) parentDefaults
97 {
98   return parent;
99 }
100
101 /* operation */
102
103 - (BOOL) primaryFetchProfile
104 {
105   GCSChannelManager *cm;
106   EOAdaptorChannel *channel;
107   NSDictionary *row;
108   NSException *ex;
109   NSString *sql, *value;
110   NSArray *attrs;
111   BOOL rc;
112
113   rc = NO;
114   
115   cm = [GCSChannelManager defaultChannelManager];
116   channel = [cm acquireOpenChannelForURL: [self tableURL]];
117   if (channel)
118     {
119       /* generate SQL */
120       sql = [NSString stringWithFormat: (@"SELECT %@"
121                                          @"  FROM %@"
122                                          @" WHERE %@ = '%@'"),
123                       fieldName, [[self tableURL] gcsTableName],
124                       uidColumnName, [self uid]];
125       
126       [values release];
127       values = [NSMutableDictionary new];
128
129       /* run SQL */
130
131       ex = [channel evaluateExpressionX: sql];
132       if (ex)
133         [self errorWithFormat:@"could not run SQL '%@': %@", sql, ex];
134       else
135         {
136           /* fetch schema */
137           attrs = [channel describeResults: NO /* don't beautify */];
138   
139           /* fetch values */
140           row = [channel fetchAttributes: attrs withZone: NULL];
141           defFlags.isNew = (row == nil);
142           [channel cancelFetch];
143   
144           /* remember values */
145           value = [row objectForKey: fieldName];
146           if ([value isNotNull])
147             [values setDictionary: [value propertyList]];
148
149           ASSIGN (lastFetch, [NSCalendarDate date]);
150           defFlags.modified = NO;
151           rc = YES;
152         }
153
154       [cm releaseChannel:channel];
155     }
156   else
157     [self errorWithFormat:@"failed to acquire channel for URL: %@", 
158           [self tableURL]];
159
160   return rc;
161 }
162
163 - (NSString *) generateSQLForInsert
164 {
165   NSMutableString *sql;
166   NSString *serializedDefaults;
167
168 #if LIB_FOUNDATION_LIBRARY
169   serializedDefaults = [values stringRepresentation];
170
171   sql = [NSString stringWithFormat: (@"INSERT INTO %@"
172                                      @"            (%@, %@)"
173                                      @"     VALUES ('%@', '%@')"),
174                   [[self tableURL] gcsTableName], uidColumnName, fieldName,
175                   [self uid],
176                   [serializedDefaults stringByReplacingString:@"'" withString:@"''"]];
177 #else
178   NSData *serializedDefaultsData;
179   NSString *error;
180
181   serializedDefaultsData
182     = [NSPropertyListSerialization dataFromPropertyList: values
183                                    format: NSPropertyListOpenStepFormat
184                                    errorDescription: &error];
185
186   if (error)
187     sql = nil;
188   else
189     {
190       serializedDefaults = [[NSString alloc] initWithData: serializedDefaultsData
191                                              encoding: NSUTF8StringEncoding];
192
193       sql = [NSString stringWithFormat: (@"INSERT INTO %@"
194                                          @"            (%@, %@)"
195                                          @"     VALUES ('%@', '%@')"),
196                       [[self tableURL] gcsTableName], uidColumnName, fieldName,
197                       [self uid],
198                       [serializedDefaults stringByReplacingString:@"'" withString:@"''"]];
199       [serializedDefaults release];
200     }
201 #endif
202
203   return sql;
204 }
205
206 - (NSString *) generateSQLForUpdate
207 {
208   NSMutableString *sql;
209   NSString *serializedDefaults;
210
211 #if LIB_FOUNDATION_LIBRARY
212   serializedDefaults = [values stringRepresentation];
213
214   sql = [NSString stringWithFormat: (@"UPDATE %@"
215                                      @"     SET %@ = '%@'"
216                                      @"   WHERE %@ = '%@'"),
217                   [[self tableURL] gcsTableName],
218                   fieldName,
219                   [serializedDefaults stringByReplacingString:@"'" withString:@"''"],
220                   uidColumnName, [self uid]];
221 #else
222   NSData *serializedDefaultsData;
223   NSString *error;
224
225   serializedDefaultsData
226     = [NSPropertyListSerialization dataFromPropertyList: values
227                                    format: NSPropertyListOpenStepFormat
228                                    errorDescription: &error];
229   error = nil;
230   if (error)
231     {
232       sql = nil;
233       [error release];
234     }
235   else
236     {
237       serializedDefaults = [[NSString alloc] initWithData: serializedDefaultsData
238                                              encoding: NSUTF8StringEncoding];
239
240       sql = [NSString stringWithFormat: (@"UPDATE %@"
241                                          @"     SET %@ = '%@'"
242                                          @"   WHERE %@ = '%@'"),
243                       [[self tableURL] gcsTableName],
244                       fieldName,
245                       [serializedDefaults stringByReplacingString:@"'" withString:@"''"],
246                       uidColumnName, [self uid]];
247       [serializedDefaults release];
248     }
249 #endif
250
251   return sql;
252 }
253
254 - (BOOL) primaryStoreProfile
255 {
256   GCSChannelManager *cm;
257   EOAdaptorChannel *channel;
258   NSException *ex;
259   NSString *sql;
260   BOOL rc;
261
262   rc = NO;
263   
264   cm = [GCSChannelManager defaultChannelManager];
265   sql = ((defFlags.isNew)
266          ? [self generateSQLForInsert] 
267          : [self generateSQLForUpdate]);
268   if (sql)
269     {
270       channel = [cm acquireOpenChannelForURL: [self tableURL]];
271       if (channel)
272         {
273           ex = [channel evaluateExpressionX:sql];
274           if (ex)
275             [self errorWithFormat: @"could not run SQL '%@': %@", sql, ex];
276           else
277             {
278               if ([[channel adaptorContext] hasOpenTransaction])
279                 {
280                   ex = [channel evaluateExpressionX: @"COMMIT TRANSACTION"];
281                   if (ex)
282                     [self errorWithFormat:@"could not commit transaction for update: %@", ex];
283                   else
284                     rc = YES;
285                 }
286               else
287                 rc = YES;
288               
289               defFlags.modified = NO;
290               defFlags.isNew = NO;
291             }
292           
293           [cm releaseChannel: channel];
294         }
295       else
296         [self errorWithFormat: @"failed to acquire channel for URL: %@", 
297               [self tableURL]];
298     }
299   else
300     [self errorWithFormat: @"failed to generate SQL for storing defaults"];
301
302   return rc;
303 }
304
305 - (BOOL) fetchProfile
306 {
307   return (values || [self primaryFetchProfile]);
308 }
309
310 /* value access */
311
312 - (void) setObject: (id) value
313             forKey: (NSString *) key
314
315   id old;
316   
317   if (![self fetchProfile])
318     return;
319
320   /* check whether the value is actually modified */
321   if (!defFlags.modified)
322     {
323       old = [values objectForKey: key];
324       if (old == value || [old isEqual: value]) /* value didn't change */
325         return;
326   
327       /* we need to this because our typed accessors convert to strings */
328       // TODO: especially problematic with bools
329       if ([value isKindOfClass: [NSString class]]) {
330         if (![old isKindOfClass: [NSString class]])
331           if ([[old description] isEqualToString: value])
332             return;
333       }
334     }
335
336   /* set in hash and mark as modified */
337   [values setObject: value forKey: key];
338
339   defFlags.modified = YES;
340 }
341
342 - (id) objectForKey: (NSString *) key
343 {
344   id value;
345   
346   if (![self fetchProfile])
347     value = nil;
348   else
349     value = [values objectForKey: key];
350
351   return value;
352 }
353
354 - (void) removeObjectForKey: (NSString *) key
355 {
356   [self setObject: nil forKey: key];
357 }
358
359 /* saving changes */
360
361 - (BOOL) synchronize
362 {
363 //   if (!defFlags.modified) /* was not modified */
364 //     return YES;
365   
366   /* ensure fetched data (more or less guaranteed by modified!=0) */
367   if (![self fetchProfile])
368     return NO;
369   
370   /* store */
371   if (![self primaryStoreProfile])
372     {
373       [self primaryFetchProfile];
374       return NO;
375     }
376
377   /* refetch */
378   return [self primaryFetchProfile];
379 }
380
381 - (void) flush
382 {
383   [values release];
384   [lastFetch release];
385   values = nil;
386   lastFetch = nil;
387   defFlags.modified = NO;
388   defFlags.isNew = NO;
389 }
390
391 /* typed accessors */
392
393 - (NSArray *) arrayForKey: (NSString *) key
394 {
395   return [self objectForKey: key];
396 }
397
398 - (NSDictionary *) dictionaryForKey: (NSString *) key
399 {
400   return [self objectForKey: key];
401 }
402
403 - (NSData *) dataForKey: (NSString *) key
404 {
405   return [self objectForKey: key];
406 }
407
408 - (NSString *) stringForKey: (NSString *) key
409 {
410   return [self objectForKey: key];
411 }
412
413 - (BOOL) boolForKey: (NSString *) key
414 {
415   return [[self objectForKey: key] boolValue];
416 }
417
418 - (float) floatForKey: (NSString *) key
419 {
420   return [[self objectForKey: key] floatValue];
421 }
422
423 - (int) integerForKey: (NSString *) key
424 {
425   return [[self objectForKey: key] intValue];
426 }
427
428 - (void) setBool: (BOOL) value
429           forKey: (NSString *) key
430 {
431   // TODO: need special support here for int-DB fields
432   [self setObject: [NSNumber numberWithBool: value]
433         forKey: key];
434 }
435
436 - (void) setFloat: (float) value
437            forKey: (NSString *) key
438 {
439   [self setObject: [NSNumber numberWithFloat: value]
440         forKey: key];
441 }
442
443 - (void) setInteger: (int) value
444              forKey: (NSString *) key
445 {
446   [self setObject: [NSNumber numberWithInt: value]
447         forKey: key];
448 }
449
450 @end /* AgenorUserDefaults */