2 Copyright (C) 2005 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
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
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.
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
22 #import <Foundation/NSPropertyList.h>
23 #import <Foundation/NSUserDefaults.h>
24 #import <Foundation/NSValue.h>
26 #import <NGExtensions/NSNull+misc.h>
27 #import <NGExtensions/NSObject+Logs.h>
29 #import <GDLContentStore/GCSChannelManager.h>
30 #import <GDLContentStore/NSURL+GCS.h>
31 #import <GDLAccess/EOAdaptorChannel.h>
32 #import <GDLAccess/EOAdaptorContext.h>
33 #import <GDLAccess/EOAttribute.h>
35 #import "NSObject+Utilities.h"
37 #import "AgenorUserDefaults.h"
39 @implementation AgenorUserDefaults
41 static NSString *uidColumnName = @"uid";
43 - (id) initWithTableURL: (NSURL *) tableURL
44 uid: (NSString *) userID
45 fieldName: (NSString *) defaultsFieldName
47 if ((self = [super init]))
49 if (tableURL && [userID length] > 0
50 && [defaultsFieldName length] > 0)
52 parent = [[NSUserDefaults standardUserDefaults] retain];
53 fieldName = [defaultsFieldName copy];
54 url = [tableURL copy];
59 [self errorWithFormat: @"missing arguments"];
98 - (NSString *) fieldName
103 - (NSUserDefaults *) parentDefaults
110 - (BOOL) primaryFetchProfile
112 GCSChannelManager *cm;
113 EOAdaptorChannel *channel;
116 NSString *sql, *value;
119 #if LIB_FOUNDATION_LIBRARY
125 cm = [GCSChannelManager defaultChannelManager];
126 channel = [cm acquireOpenChannelForURL: [self tableURL]];
130 sql = [NSString stringWithFormat: (@"SELECT %@"
132 @" WHERE %@ = '%@'"),
133 fieldName, [[self tableURL] gcsTableName],
134 uidColumnName, [self uid]];
137 values = [NSMutableDictionary new];
141 ex = [channel evaluateExpressionX: sql];
143 [self errorWithFormat:@"could not run SQL '%@': %@", sql, ex];
147 attrs = [channel describeResults: NO /* don't beautify */];
150 row = [channel fetchAttributes: attrs withZone: NULL];
151 defFlags.isNew = (row == nil);
152 [channel cancelFetch];
154 /* remember values */
155 value = [row objectForKey: fieldName];
156 if ([value isNotNull])
158 #if LIB_FOUNDATION_LIBRARY
159 plistData = [value dataUsingEncoding: NSUTF8StringEncoding];
160 [values setDictionary: [NSDeserializer
161 deserializePropertyListFromData: plistData
162 mutableContainers: YES]];
165 [values setDictionary: [value propertyList]];
169 ASSIGN (lastFetch, [NSCalendarDate date]);
170 defFlags.modified = NO;
174 [cm releaseChannel:channel];
177 [self errorWithFormat:@"failed to acquire channel for URL: %@",
183 - (NSString *) generateSQLForInsert
185 NSMutableString *sql;
186 NSString *serializedDefaults;
188 #if LIB_FOUNDATION_LIBRARY
189 serializedDefaults = [values stringRepresentation];
191 sql = [NSString stringWithFormat: (@"INSERT INTO %@"
193 @" VALUES ('%@', '%@')"),
194 [[self tableURL] gcsTableName], uidColumnName, fieldName,
196 [serializedDefaults stringByReplacingString:@"'" withString:@"''"]];
198 NSData *serializedDefaultsData;
201 serializedDefaultsData
202 = [NSPropertyListSerialization dataFromPropertyList: values
203 format: NSPropertyListOpenStepFormat
204 errorDescription: &error];
210 serializedDefaults = [[NSString alloc] initWithData: serializedDefaultsData
211 encoding: NSUTF8StringEncoding];
213 sql = [NSString stringWithFormat: (@"INSERT INTO %@"
215 @" VALUES ('%@', '%@')"),
216 [[self tableURL] gcsTableName], uidColumnName, fieldName,
218 [serializedDefaults stringByReplacingString:@"'" withString:@"''"]];
219 [serializedDefaults release];
226 - (NSString *) generateSQLForUpdate
228 NSMutableString *sql;
229 NSString *serializedDefaults;
231 #if LIB_FOUNDATION_LIBRARY
232 serializedDefaults = [values stringRepresentation];
234 sql = [NSString stringWithFormat: (@"UPDATE %@"
236 @" WHERE %@ = '%@'"),
237 [[self tableURL] gcsTableName],
239 [serializedDefaults stringByReplacingString:@"'" withString:@"''"],
240 uidColumnName, [self uid]];
242 NSData *serializedDefaultsData;
245 serializedDefaultsData
246 = [NSPropertyListSerialization dataFromPropertyList: values
247 format: NSPropertyListOpenStepFormat
248 errorDescription: &error];
257 serializedDefaults = [[NSString alloc] initWithData: serializedDefaultsData
258 encoding: NSUTF8StringEncoding];
260 sql = [NSString stringWithFormat: (@"UPDATE %@"
262 @" WHERE %@ = '%@'"),
263 [[self tableURL] gcsTableName],
265 [serializedDefaults stringByReplacingString:@"'" withString:@"''"],
266 uidColumnName, [self uid]];
267 [serializedDefaults release];
274 - (BOOL) primaryStoreProfile
276 GCSChannelManager *cm;
277 EOAdaptorChannel *channel;
284 cm = [GCSChannelManager defaultChannelManager];
285 sql = ((defFlags.isNew)
286 ? [self generateSQLForInsert]
287 : [self generateSQLForUpdate]);
290 channel = [cm acquireOpenChannelForURL: [self tableURL]];
293 ex = [channel evaluateExpressionX:sql];
295 [self errorWithFormat: @"could not run SQL '%@': %@", sql, ex];
298 if ([[channel adaptorContext] hasOpenTransaction])
300 ex = [channel evaluateExpressionX: @"COMMIT TRANSACTION"];
302 [self errorWithFormat:@"could not commit transaction for update: %@", ex];
309 defFlags.modified = NO;
313 [cm releaseChannel: channel];
316 [self errorWithFormat: @"failed to acquire channel for URL: %@",
320 [self errorWithFormat: @"failed to generate SQL for storing defaults"];
325 - (BOOL) fetchProfile
327 return (values || [self primaryFetchProfile]);
330 - (NSString *) jsonRepresentation
334 return [values jsonRepresentation];
339 - (void) setObject: (id) value
340 forKey: (NSString *) key
344 if (![self fetchProfile])
347 /* check whether the value is actually modified */
348 if (!defFlags.modified)
350 old = [values objectForKey: key];
351 if (old == value || [old isEqual: value]) /* value didn't change */
354 /* we need to this because our typed accessors convert to strings */
355 // TODO: especially problematic with bools
356 if ([value isKindOfClass: [NSString class]]) {
357 if (![old isKindOfClass: [NSString class]])
358 if ([[old description] isEqualToString: value])
363 /* set in hash and mark as modified */
364 [values setObject: value forKey: key];
366 defFlags.modified = YES;
369 - (id) objectForKey: (NSString *) key
373 if (![self fetchProfile])
376 value = [values objectForKey: key];
381 - (void) removeObjectForKey: (NSString *) key
383 [self setObject: nil forKey: key];
390 // if (!defFlags.modified) /* was not modified */
393 /* ensure fetched data (more or less guaranteed by modified!=0) */
394 if (![self fetchProfile])
398 if (![self primaryStoreProfile])
400 [self primaryFetchProfile];
405 return [self primaryFetchProfile];
414 defFlags.modified = NO;
418 /* typed accessors */
420 - (NSArray *) arrayForKey: (NSString *) key
422 return [self objectForKey: key];
425 - (NSDictionary *) dictionaryForKey: (NSString *) key
427 return [self objectForKey: key];
430 - (NSData *) dataForKey: (NSString *) key
432 return [self objectForKey: key];
435 - (NSString *) stringForKey: (NSString *) key
437 return [self objectForKey: key];
440 - (BOOL) boolForKey: (NSString *) key
442 return [[self objectForKey: key] boolValue];
445 - (float) floatForKey: (NSString *) key
447 return [[self objectForKey: key] floatValue];
450 - (int) integerForKey: (NSString *) key
452 return [[self objectForKey: key] intValue];
455 - (void) setBool: (BOOL) value
456 forKey: (NSString *) key
458 // TODO: need special support here for int-DB fields
459 [self setObject: [NSNumber numberWithBool: value]
463 - (void) setFloat: (float) value
464 forKey: (NSString *) key
466 [self setObject: [NSNumber numberWithFloat: value]
470 - (void) setInteger: (int) value
471 forKey: (NSString *) key
473 [self setObject: [NSNumber numberWithInt: value]
477 @end /* AgenorUserDefaults */