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/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>
30 #import "AgenorUserDefaults.h"
32 @implementation AgenorUserDefaults
34 static NSString *uidColumnName = @"uid";
36 - (id) initWithTableURL: (NSURL *) tableURL
37 uid: (NSString *) userID
38 fieldName: (NSString *) defaultsFieldName
40 if ((self = [super init]))
42 if (tableURL && [userID length] > 0
43 && [defaultsFieldName length] > 0)
45 parent = [[NSUserDefaults standardUserDefaults] retain];
46 fieldName = [defaultsFieldName copy];
47 url = [tableURL copy];
52 [self errorWithFormat: @"missing arguments"];
91 - (NSString *) fieldName
96 - (NSUserDefaults *) parentDefaults
103 - (BOOL) primaryFetchProfile
105 GCSChannelManager *cm;
106 EOAdaptorChannel *channel;
115 cm = [GCSChannelManager defaultChannelManager];
116 channel = [cm acquireOpenChannelForURL: [self tableURL]];
120 sql = [NSString stringWithFormat: (@"SELECT %@"
122 @" WHERE %@ = '%@'"),
123 fieldName, [[self tableURL] gcsTableName],
124 uidColumnName, [self uid]];
128 ex = [channel evaluateExpressionX: sql];
131 [self errorWithFormat:@"could not run SQL '%@': %@", sql, ex];
132 values = [NSMutableDictionary new];
137 attrs = [channel describeResults: NO /* don't beautify */];
140 row = [channel fetchAttributes: attrs withZone: NULL];
141 defFlags.isNew = (row == nil);
142 [channel cancelFetch];
144 /* remember values */
146 values = [[row objectForKey: fieldName] propertyList];
150 values = [NSMutableDictionary new];
152 ASSIGN(lastFetch, [NSCalendarDate date]);
153 defFlags.modified = NO;
157 [cm releaseChannel:channel];
160 [self errorWithFormat:@"failed to acquire channel for URL: %@",
166 - (NSString *) generateSQLForInsert
168 NSMutableString *sql;
169 NSString *serializedDefaults;
171 #if LIB_FOUNDATION_LIBRARY
172 serializedDefaults = [values stringRepresentation];
174 sql = [NSString stringWithFormat: (@"INSERT INTO %@"
176 @" VALUES ('%@', '%@')"),
177 [[self tableURL] gcsTableName], uidColumnName, fieldName,
179 [serializedDefaults stringByReplacingString:@"'" withString:@"''"]];
181 NSData *serializedDefaultsData;
184 serializedDefaultsData
185 = [NSPropertyListSerialization dataFromPropertyList: values
186 format: NSPropertyListOpenStepFormat
187 errorDescription: &error];
193 serializedDefaults = [[NSString alloc] initWithData: serializedDefaultsData
194 encoding: NSUTF8StringEncoding];
196 sql = [NSString stringWithFormat: (@"INSERT INTO %@"
198 @" VALUES ('%@', '%@')"),
199 [[self tableURL] gcsTableName], uidColumnName, fieldName,
201 [serializedDefaults stringByReplacingString:@"'" withString:@"''"]];
202 [serializedDefaults release];
209 - (NSString *) generateSQLForUpdate
211 NSMutableString *sql;
212 NSString *serializedDefaults;
214 #if LIB_FOUNDATION_LIBRARY
215 serializedDefaults = [values stringRepresentation];
217 sql = [NSString stringWithFormat: (@"UPDATE %@"
219 @" WHERE %@ = '%@'"),
220 [[self tableURL] gcsTableName],
222 [serializedDefaults stringByReplacingString:@"'" withString:@"''"],
223 uidColumnName, [self uid]];
225 NSData *serializedDefaultsData;
228 serializedDefaultsData
229 = [NSPropertyListSerialization dataFromPropertyList: values
230 format: NSPropertyListOpenStepFormat
231 errorDescription: &error];
240 serializedDefaults = [[NSString alloc] initWithData: serializedDefaultsData
241 encoding: NSUTF8StringEncoding];
243 sql = [NSString stringWithFormat: (@"UPDATE %@"
245 @" WHERE %@ = '%@'"),
246 [[self tableURL] gcsTableName],
248 [serializedDefaults stringByReplacingString:@"'" withString:@"''"],
249 uidColumnName, [self uid]];
250 [serializedDefaults release];
257 - (BOOL) primaryStoreProfile
259 GCSChannelManager *cm;
260 EOAdaptorChannel *channel;
267 cm = [GCSChannelManager defaultChannelManager];
268 sql = ((defFlags.isNew)
269 ? [self generateSQLForInsert]
270 : [self generateSQLForUpdate]);
273 channel = [cm acquireOpenChannelForURL: [self tableURL]];
276 ex = [channel evaluateExpressionX:sql];
278 [self errorWithFormat: @"could not run SQL '%@': %@", sql, ex];
281 if ([[channel adaptorContext] hasOpenTransaction])
283 ex = [channel evaluateExpressionX: @"COMMIT TRANSACTION"];
285 [self errorWithFormat:@"could not commit transaction for update: %@", ex];
292 defFlags.modified = NO;
296 [cm releaseChannel: channel];
299 [self errorWithFormat: @"failed to acquire channel for URL: %@",
303 [self errorWithFormat: @"failed to generate SQL for storing defaults"];
308 - (BOOL) fetchProfile
310 return (values || [self primaryFetchProfile]);
315 - (void) setObject: (id) value
316 forKey: (NSString *) key
320 if (![self fetchProfile])
323 /* check whether the value is actually modified */
324 if (!defFlags.modified)
326 old = [values objectForKey: key];
327 if (old == value || [old isEqual: value]) /* value didn't change */
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])
339 /* set in hash and mark as modified */
340 [values setObject: value forKey: key];
342 defFlags.modified = YES;
345 - (id) objectForKey: (NSString *) key
349 if (![self fetchProfile])
352 value = [values objectForKey: key];
357 - (void) removeObjectForKey: (NSString *) key
359 [self setObject: nil forKey: key];
366 // if (!defFlags.modified) /* was not modified */
369 /* ensure fetched data (more or less guaranteed by modified!=0) */
370 if (![self fetchProfile])
374 if (![self primaryStoreProfile])
376 [self primaryFetchProfile];
381 return [self primaryFetchProfile];
390 defFlags.modified = NO;
394 /* typed accessors */
396 - (NSArray *) arrayForKey: (NSString *) key
398 return [self objectForKey: key];
401 - (NSDictionary *) dictionaryForKey: (NSString *) key
403 return [self objectForKey: key];
406 - (NSData *) dataForKey: (NSString *) key
408 return [self objectForKey: key];
411 - (NSString *) stringForKey: (NSString *) key
413 return [self objectForKey: key];
416 - (BOOL) boolForKey: (NSString *) key
418 return [[self objectForKey: key] boolValue];
421 - (float) floatForKey: (NSString *) key
423 return [[self objectForKey: key] floatValue];
426 - (int) integerForKey: (NSString *) key
428 return [[self objectForKey: key] intValue];
431 - (void) setBool: (BOOL) value
432 forKey: (NSString *) key
434 // TODO: need special support here for int-DB fields
435 [self setObject: [NSNumber numberWithBool: value]
439 - (void) setFloat: (float) value
440 forKey: (NSString *) key
442 [self setObject: [NSNumber numberWithFloat: value]
446 - (void) setInteger: (int) value
447 forKey: (NSString *) key
449 [self setObject: [NSNumber numberWithInt: value]
453 @end /* AgenorUserDefaults */