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 = @"c_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: @"'"
199 NSData *serializedDefaultsData;
203 serializedDefaultsData
204 = [NSPropertyListSerialization dataFromPropertyList: values
205 format: NSPropertyListOpenStepFormat
206 errorDescription: &error];
215 serializedDefaults = [[NSString alloc] initWithData: serializedDefaultsData
216 encoding: NSUTF8StringEncoding];
218 sql = [NSString stringWithFormat: (@"INSERT INTO %@"
220 @" VALUES ('%@', '%@')"),
221 [[self tableURL] gcsTableName], uidColumnName, fieldName,
223 [serializedDefaults stringByReplacingString:@"'" withString:@"''"]];
224 [serializedDefaults release];
231 - (NSString *) generateSQLForUpdate
233 NSMutableString *sql;
234 NSString *serializedDefaults;
236 #if LIB_FOUNDATION_LIBRARY
237 serializedDefaults = [values stringRepresentation];
239 sql = [NSString stringWithFormat: (@"UPDATE %@"
241 @" WHERE %@ = '%@'"),
242 [[self tableURL] gcsTableName],
244 [serializedDefaults stringByReplacingString:@"'" withString:@"''"],
245 uidColumnName, [self uid]];
247 NSData *serializedDefaultsData;
251 serializedDefaultsData
252 = [NSPropertyListSerialization dataFromPropertyList: values
253 format: NSPropertyListOpenStepFormat
254 errorDescription: &error];
262 serializedDefaults = [[NSString alloc] initWithData: serializedDefaultsData
263 encoding: NSUTF8StringEncoding];
265 sql = [NSString stringWithFormat: (@"UPDATE %@"
267 @" WHERE %@ = '%@'"),
268 [[self tableURL] gcsTableName],
270 [serializedDefaults stringByReplacingString: @"'"
272 uidColumnName, [self uid]];
273 [serializedDefaults release];
280 - (BOOL) primaryStoreProfile
282 GCSChannelManager *cm;
283 EOAdaptorChannel *channel;
290 cm = [GCSChannelManager defaultChannelManager];
291 sql = ((defFlags.isNew)
292 ? [self generateSQLForInsert]
293 : [self generateSQLForUpdate]);
296 channel = [cm acquireOpenChannelForURL: [self tableURL]];
299 ex = [channel evaluateExpressionX:sql];
301 [self errorWithFormat: @"could not run SQL '%@': %@", sql, ex];
304 if ([[channel adaptorContext] hasOpenTransaction])
306 ex = [channel evaluateExpressionX: @"COMMIT TRANSACTION"];
308 [self errorWithFormat:@"could not commit transaction for update: %@", ex];
315 defFlags.modified = NO;
319 [cm releaseChannel: channel];
322 [self errorWithFormat: @"failed to acquire channel for URL: %@",
326 [self errorWithFormat: @"failed to generate SQL for storing defaults"];
331 - (BOOL) fetchProfile
333 return (values || [self primaryFetchProfile]);
336 - (NSString *) jsonRepresentation
340 return [values jsonRepresentation];
345 - (void) setObject: (id) value
346 forKey: (NSString *) key
350 if (![self fetchProfile])
353 /* check whether the value is actually modified */
354 if (!defFlags.modified)
356 old = [values objectForKey: key];
357 if (old == value || [old isEqual: value]) /* value didn't change */
360 /* we need to this because our typed accessors convert to strings */
361 // TODO: especially problematic with bools
362 if ([value isKindOfClass: [NSString class]]) {
363 if (![old isKindOfClass: [NSString class]])
364 if ([[old description] isEqualToString: value])
369 /* set in hash and mark as modified */
370 [values setObject: value forKey: key];
372 defFlags.modified = YES;
375 - (id) objectForKey: (NSString *) key
379 if (![self fetchProfile])
382 value = [values objectForKey: key];
387 - (void) removeObjectForKey: (NSString *) key
389 [self setObject: nil forKey: key];
396 // if (!defFlags.modified) /* was not modified */
399 /* ensure fetched data (more or less guaranteed by modified!=0) */
400 if (![self fetchProfile])
404 if (![self primaryStoreProfile])
406 [self primaryFetchProfile];
411 return [self primaryFetchProfile];
420 defFlags.modified = NO;
424 /* typed accessors */
426 - (NSArray *) arrayForKey: (NSString *) key
428 return [self objectForKey: key];
431 - (NSDictionary *) dictionaryForKey: (NSString *) key
433 return [self objectForKey: key];
436 - (NSData *) dataForKey: (NSString *) key
438 return [self objectForKey: key];
441 - (NSString *) stringForKey: (NSString *) key
443 return [self objectForKey: key];
446 - (BOOL) boolForKey: (NSString *) key
448 return [[self objectForKey: key] boolValue];
451 - (float) floatForKey: (NSString *) key
453 return [[self objectForKey: key] floatValue];
456 - (int) integerForKey: (NSString *) key
458 return [[self objectForKey: key] intValue];
461 - (void) setBool: (BOOL) value
462 forKey: (NSString *) key
464 // TODO: need special support here for int-DB fields
465 [self setObject: [NSNumber numberWithBool: value]
469 - (void) setFloat: (float) value
470 forKey: (NSString *) key
472 [self setObject: [NSNumber numberWithFloat: value]
476 - (void) setInteger: (int) value
477 forKey: (NSString *) key
479 [self setObject: [NSNumber numberWithInt: value]
483 @end /* AgenorUserDefaults */