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;
109 NSString *sql, *value;
112 #if LIB_FOUNDATION_LIBRARY
118 cm = [GCSChannelManager defaultChannelManager];
119 channel = [cm acquireOpenChannelForURL: [self tableURL]];
123 sql = [NSString stringWithFormat: (@"SELECT %@"
125 @" WHERE %@ = '%@'"),
126 fieldName, [[self tableURL] gcsTableName],
127 uidColumnName, [self uid]];
130 values = [NSMutableDictionary new];
134 ex = [channel evaluateExpressionX: sql];
136 [self errorWithFormat:@"could not run SQL '%@': %@", sql, ex];
140 attrs = [channel describeResults: NO /* don't beautify */];
143 row = [channel fetchAttributes: attrs withZone: NULL];
144 defFlags.isNew = (row == nil);
145 [channel cancelFetch];
147 /* remember values */
148 value = [row objectForKey: fieldName];
149 if ([value isNotNull])
151 #if LIB_FOUNDATION_LIBRARY
152 plistData = [value dataUsingEncoding: NSUTF8StringEncoding];
153 [values setDictionary: [NSDeserializer
154 deserializePropertyListFromData: plistData
155 mutableContainers: YES]];
158 [values setDictionary: [value propertyList]];
162 ASSIGN (lastFetch, [NSCalendarDate date]);
163 defFlags.modified = NO;
167 [cm releaseChannel:channel];
170 [self errorWithFormat:@"failed to acquire channel for URL: %@",
176 - (NSString *) generateSQLForInsert
178 NSMutableString *sql;
179 NSString *serializedDefaults;
181 #if LIB_FOUNDATION_LIBRARY
182 serializedDefaults = [values stringRepresentation];
184 sql = [NSString stringWithFormat: (@"INSERT INTO %@"
186 @" VALUES ('%@', '%@')"),
187 [[self tableURL] gcsTableName], uidColumnName, fieldName,
189 [serializedDefaults stringByReplacingString:@"'" withString:@"''"]];
191 NSData *serializedDefaultsData;
194 serializedDefaultsData
195 = [NSPropertyListSerialization dataFromPropertyList: values
196 format: NSPropertyListOpenStepFormat
197 errorDescription: &error];
203 serializedDefaults = [[NSString alloc] initWithData: serializedDefaultsData
204 encoding: NSUTF8StringEncoding];
206 sql = [NSString stringWithFormat: (@"INSERT INTO %@"
208 @" VALUES ('%@', '%@')"),
209 [[self tableURL] gcsTableName], uidColumnName, fieldName,
211 [serializedDefaults stringByReplacingString:@"'" withString:@"''"]];
212 [serializedDefaults release];
219 - (NSString *) generateSQLForUpdate
221 NSMutableString *sql;
222 NSString *serializedDefaults;
224 #if LIB_FOUNDATION_LIBRARY
225 serializedDefaults = [values stringRepresentation];
227 sql = [NSString stringWithFormat: (@"UPDATE %@"
229 @" WHERE %@ = '%@'"),
230 [[self tableURL] gcsTableName],
232 [serializedDefaults stringByReplacingString:@"'" withString:@"''"],
233 uidColumnName, [self uid]];
235 NSData *serializedDefaultsData;
238 serializedDefaultsData
239 = [NSPropertyListSerialization dataFromPropertyList: values
240 format: NSPropertyListOpenStepFormat
241 errorDescription: &error];
250 serializedDefaults = [[NSString alloc] initWithData: serializedDefaultsData
251 encoding: NSUTF8StringEncoding];
253 sql = [NSString stringWithFormat: (@"UPDATE %@"
255 @" WHERE %@ = '%@'"),
256 [[self tableURL] gcsTableName],
258 [serializedDefaults stringByReplacingString:@"'" withString:@"''"],
259 uidColumnName, [self uid]];
260 [serializedDefaults release];
267 - (BOOL) primaryStoreProfile
269 GCSChannelManager *cm;
270 EOAdaptorChannel *channel;
277 cm = [GCSChannelManager defaultChannelManager];
278 sql = ((defFlags.isNew)
279 ? [self generateSQLForInsert]
280 : [self generateSQLForUpdate]);
283 channel = [cm acquireOpenChannelForURL: [self tableURL]];
286 ex = [channel evaluateExpressionX:sql];
288 [self errorWithFormat: @"could not run SQL '%@': %@", sql, ex];
291 if ([[channel adaptorContext] hasOpenTransaction])
293 ex = [channel evaluateExpressionX: @"COMMIT TRANSACTION"];
295 [self errorWithFormat:@"could not commit transaction for update: %@", ex];
302 defFlags.modified = NO;
306 [cm releaseChannel: channel];
309 [self errorWithFormat: @"failed to acquire channel for URL: %@",
313 [self errorWithFormat: @"failed to generate SQL for storing defaults"];
318 - (BOOL) fetchProfile
320 return (values || [self primaryFetchProfile]);
325 - (void) setObject: (id) value
326 forKey: (NSString *) key
330 if (![self fetchProfile])
333 /* check whether the value is actually modified */
334 if (!defFlags.modified)
336 old = [values objectForKey: key];
337 if (old == value || [old isEqual: value]) /* value didn't change */
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])
349 /* set in hash and mark as modified */
350 [values setObject: value forKey: key];
352 defFlags.modified = YES;
355 - (id) objectForKey: (NSString *) key
359 if (![self fetchProfile])
362 value = [values objectForKey: key];
367 - (void) removeObjectForKey: (NSString *) key
369 [self setObject: nil forKey: key];
376 // if (!defFlags.modified) /* was not modified */
379 /* ensure fetched data (more or less guaranteed by modified!=0) */
380 if (![self fetchProfile])
384 if (![self primaryStoreProfile])
386 [self primaryFetchProfile];
391 return [self primaryFetchProfile];
400 defFlags.modified = NO;
404 /* typed accessors */
406 - (NSArray *) arrayForKey: (NSString *) key
408 return [self objectForKey: key];
411 - (NSDictionary *) dictionaryForKey: (NSString *) key
413 return [self objectForKey: key];
416 - (NSData *) dataForKey: (NSString *) key
418 return [self objectForKey: key];
421 - (NSString *) stringForKey: (NSString *) key
423 return [self objectForKey: key];
426 - (BOOL) boolForKey: (NSString *) key
428 return [[self objectForKey: key] boolValue];
431 - (float) floatForKey: (NSString *) key
433 return [[self objectForKey: key] floatValue];
436 - (int) integerForKey: (NSString *) key
438 return [[self objectForKey: key] intValue];
441 - (void) setBool: (BOOL) value
442 forKey: (NSString *) key
444 // TODO: need special support here for int-DB fields
445 [self setObject: [NSNumber numberWithBool: value]
449 - (void) setFloat: (float) value
450 forKey: (NSString *) key
452 [self setObject: [NSNumber numberWithFloat: value]
456 - (void) setInteger: (int) value
457 forKey: (NSString *) key
459 [self setObject: [NSNumber numberWithInt: value]
463 @end /* AgenorUserDefaults */