]> err.no Git - scalable-opengroupware.org/blob - SoObjects/SOGo/AgenorUserDefaults.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1045 d1b88da0-ebda-0310...
[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;
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       /* run SQL */
127
128       ex = [channel evaluateExpressionX: sql];
129       if (ex)
130         {
131           [self errorWithFormat:@"could not run SQL '%@': %@", sql, ex];
132           values = [NSMutableDictionary new];
133         }
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           [values release];
146           values = [[row objectForKey: fieldName] propertyList];
147           if (values)
148             [values retain];
149           else
150             values = [NSMutableDictionary new];
151
152           ASSIGN(lastFetch, [NSCalendarDate date]);
153           defFlags.modified = NO;
154           rc = YES;
155         }
156
157       [cm releaseChannel:channel];
158     }
159   else
160     [self errorWithFormat:@"failed to acquire channel for URL: %@", 
161           [self tableURL]];
162
163   return rc;
164 }
165
166 - (NSString *) generateSQLForInsert
167 {
168   NSMutableString *sql;
169   NSString *serializedDefaults;
170
171 #if LIB_FOUNDATION_LIBRARY
172   serializedDefaults = [values stringRepresentation];
173
174   sql = [NSString stringWithFormat: (@"INSERT INTO %@"
175                                      @"            (%@, %@)"
176                                      @"     VALUES ('%@', '%@')"),
177                   [[self tableURL] gcsTableName], uidColumnName, fieldName,
178                   [self uid],
179                   [serializedDefaults stringByReplacingString:@"'" withString:@"''"]];
180 #else
181   NSData *serializedDefaultsData;
182   NSString *error;
183
184   serializedDefaultsData
185     = [NSPropertyListSerialization dataFromPropertyList: values
186                                    format: NSPropertyListOpenStepFormat
187                                    errorDescription: &error];
188
189   if (error)
190     sql = nil;
191   else
192     {
193       serializedDefaults = [[NSString alloc] initWithData: serializedDefaultsData
194                                              encoding: NSUTF8StringEncoding];
195
196       sql = [NSString stringWithFormat: (@"INSERT INTO %@"
197                                          @"            (%@, %@)"
198                                          @"     VALUES ('%@', '%@')"),
199                       [[self tableURL] gcsTableName], uidColumnName, fieldName,
200                       [self uid],
201                       [serializedDefaults stringByReplacingString:@"'" withString:@"''"]];
202       [serializedDefaults release];
203     }
204 #endif
205
206   return sql;
207 }
208
209 - (NSString *) generateSQLForUpdate
210 {
211   NSMutableString *sql;
212   NSString *serializedDefaults;
213
214 #if LIB_FOUNDATION_LIBRARY
215   serializedDefaults = [values stringRepresentation];
216
217   sql = [NSString stringWithFormat: (@"UPDATE %@"
218                                      @"     SET %@ = '%@'"
219                                      @"   WHERE %@ = '%@'"),
220                   [[self tableURL] gcsTableName],
221                   fieldName,
222                   [serializedDefaults stringByReplacingString:@"'" withString:@"''"],
223                   uidColumnName, [self uid]];
224 #else
225   NSData *serializedDefaultsData;
226   NSString *error;
227
228   serializedDefaultsData
229     = [NSPropertyListSerialization dataFromPropertyList: values
230                                    format: NSPropertyListOpenStepFormat
231                                    errorDescription: &error];
232   error = nil;
233   if (error)
234     {
235       sql = nil;
236       [error release];
237     }
238   else
239     {
240       serializedDefaults = [[NSString alloc] initWithData: serializedDefaultsData
241                                              encoding: NSUTF8StringEncoding];
242
243       sql = [NSString stringWithFormat: (@"UPDATE %@"
244                                          @"     SET %@ = '%@'"
245                                          @"   WHERE %@ = '%@'"),
246                       [[self tableURL] gcsTableName],
247                       fieldName,
248                       [serializedDefaults stringByReplacingString:@"'" withString:@"''"],
249                       uidColumnName, [self uid]];
250       [serializedDefaults release];
251     }
252 #endif
253
254   return sql;
255 }
256
257 - (BOOL) primaryStoreProfile
258 {
259   GCSChannelManager *cm;
260   EOAdaptorChannel *channel;
261   NSException *ex;
262   NSString *sql;
263   BOOL rc;
264
265   rc = NO;
266   
267   cm = [GCSChannelManager defaultChannelManager];
268   sql = ((defFlags.isNew)
269          ? [self generateSQLForInsert] 
270          : [self generateSQLForUpdate]);
271   if (sql)
272     {
273       channel = [cm acquireOpenChannelForURL: [self tableURL]];
274       if (channel)
275         {
276           ex = [channel evaluateExpressionX:sql];
277           if (ex)
278             [self errorWithFormat: @"could not run SQL '%@': %@", sql, ex];
279           else
280             {
281               if ([[channel adaptorContext] hasOpenTransaction])
282                 {
283                   ex = [channel evaluateExpressionX: @"COMMIT TRANSACTION"];
284                   if (ex)
285                     [self errorWithFormat:@"could not commit transaction for update: %@", ex];
286                   else
287                     rc = YES;
288                 }
289               else
290                 rc = YES;
291               
292               defFlags.modified = NO;
293               defFlags.isNew = NO;
294             }
295           
296           [cm releaseChannel: channel];
297         }
298       else
299         [self errorWithFormat: @"failed to acquire channel for URL: %@", 
300               [self tableURL]];
301     }
302   else
303     [self errorWithFormat: @"failed to generate SQL for storing defaults"];
304
305   return rc;
306 }
307
308 - (BOOL) fetchProfile
309 {
310   return (values || [self primaryFetchProfile]);
311 }
312
313 /* value access */
314
315 - (void) setObject: (id) value
316             forKey: (NSString *) key
317
318   id old;
319   
320   if (![self fetchProfile])
321     return;
322
323   /* check whether the value is actually modified */
324   if (!defFlags.modified)
325     {
326       old = [values objectForKey: key];
327       if (old == value || [old isEqual: value]) /* value didn't change */
328         return;
329   
330       /* we need to this because our typed accessors convert to strings */
331       // TODO: especially problematic with bools
332       if ([value isKindOfClass: [NSString class]]) {
333         if (![old isKindOfClass: [NSString class]])
334           if ([[old description] isEqualToString: value])
335             return;
336       }
337     }
338
339   /* set in hash and mark as modified */
340   [values setObject: value forKey: key];
341
342   defFlags.modified = YES;
343 }
344
345 - (id) objectForKey: (NSString *) key
346 {
347   id value;
348   
349   if (![self fetchProfile])
350     value = nil;
351   else
352     value = [values objectForKey: key];
353
354   return value;
355 }
356
357 - (void) removeObjectForKey: (NSString *) key
358 {
359   [self setObject: nil forKey: key];
360 }
361
362 /* saving changes */
363
364 - (BOOL) synchronize
365 {
366 //   if (!defFlags.modified) /* was not modified */
367 //     return YES;
368   
369   /* ensure fetched data (more or less guaranteed by modified!=0) */
370   if (![self fetchProfile])
371     return NO;
372   
373   /* store */
374   if (![self primaryStoreProfile])
375     {
376       [self primaryFetchProfile];
377       return NO;
378     }
379
380   /* refetch */
381   return [self primaryFetchProfile];
382 }
383
384 - (void) flush
385 {
386   [values release];
387   [lastFetch release];
388   values = nil;
389   lastFetch = nil;
390   defFlags.modified = NO;
391   defFlags.isNew = NO;
392 }
393
394 /* typed accessors */
395
396 - (NSArray *) arrayForKey: (NSString *) key
397 {
398   return [self objectForKey: key];
399 }
400
401 - (NSDictionary *) dictionaryForKey: (NSString *) key
402 {
403   return [self objectForKey: key];
404 }
405
406 - (NSData *) dataForKey: (NSString *) key
407 {
408   return [self objectForKey: key];
409 }
410
411 - (NSString *) stringForKey: (NSString *) key
412 {
413   return [self objectForKey: key];
414 }
415
416 - (BOOL) boolForKey: (NSString *) key
417 {
418   return [[self objectForKey: key] boolValue];
419 }
420
421 - (float) floatForKey: (NSString *) key
422 {
423   return [[self objectForKey: key] floatValue];
424 }
425
426 - (int) integerForKey: (NSString *) key
427 {
428   return [[self objectForKey: key] intValue];
429 }
430
431 - (void) setBool: (BOOL) value
432           forKey: (NSString *) key
433 {
434   // TODO: need special support here for int-DB fields
435   [self setObject: [NSNumber numberWithBool: value]
436         forKey: key];
437 }
438
439 - (void) setFloat: (float) value
440            forKey: (NSString *) key
441 {
442   [self setObject: [NSNumber numberWithFloat: value]
443         forKey: key];
444 }
445
446 - (void) setInteger: (int) value
447              forKey: (NSString *) key
448 {
449   [self setObject: [NSNumber numberWithInt: value]
450         forKey: key];
451 }
452
453 @end /* AgenorUserDefaults */