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