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;
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]];
127 values = [NSMutableDictionary new];
131 ex = [channel evaluateExpressionX: sql];
133 [self errorWithFormat:@"could not run SQL '%@': %@", sql, ex];
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 */
145 value = [row objectForKey: fieldName];
146 if ([value isNotNull])
147 [values setDictionary: [value propertyList]];
149 ASSIGN (lastFetch, [NSCalendarDate date]);
150 defFlags.modified = NO;
154 [cm releaseChannel:channel];
157 [self errorWithFormat:@"failed to acquire channel for URL: %@",
163 - (NSString *) generateSQLForInsert
165 NSMutableString *sql;
166 NSString *serializedDefaults;
168 #if LIB_FOUNDATION_LIBRARY
169 serializedDefaults = [values stringRepresentation];
171 sql = [NSString stringWithFormat: (@"INSERT INTO %@"
173 @" VALUES ('%@', '%@')"),
174 [[self tableURL] gcsTableName], uidColumnName, fieldName,
176 [serializedDefaults stringByReplacingString:@"'" withString:@"''"]];
178 NSData *serializedDefaultsData;
181 serializedDefaultsData
182 = [NSPropertyListSerialization dataFromPropertyList: values
183 format: NSPropertyListOpenStepFormat
184 errorDescription: &error];
190 serializedDefaults = [[NSString alloc] initWithData: serializedDefaultsData
191 encoding: NSUTF8StringEncoding];
193 sql = [NSString stringWithFormat: (@"INSERT INTO %@"
195 @" VALUES ('%@', '%@')"),
196 [[self tableURL] gcsTableName], uidColumnName, fieldName,
198 [serializedDefaults stringByReplacingString:@"'" withString:@"''"]];
199 [serializedDefaults release];
206 - (NSString *) generateSQLForUpdate
208 NSMutableString *sql;
209 NSString *serializedDefaults;
211 #if LIB_FOUNDATION_LIBRARY
212 serializedDefaults = [values stringRepresentation];
214 sql = [NSString stringWithFormat: (@"UPDATE %@"
216 @" WHERE %@ = '%@'"),
217 [[self tableURL] gcsTableName],
219 [serializedDefaults stringByReplacingString:@"'" withString:@"''"],
220 uidColumnName, [self uid]];
222 NSData *serializedDefaultsData;
225 serializedDefaultsData
226 = [NSPropertyListSerialization dataFromPropertyList: values
227 format: NSPropertyListOpenStepFormat
228 errorDescription: &error];
237 serializedDefaults = [[NSString alloc] initWithData: serializedDefaultsData
238 encoding: NSUTF8StringEncoding];
240 sql = [NSString stringWithFormat: (@"UPDATE %@"
242 @" WHERE %@ = '%@'"),
243 [[self tableURL] gcsTableName],
245 [serializedDefaults stringByReplacingString:@"'" withString:@"''"],
246 uidColumnName, [self uid]];
247 [serializedDefaults release];
254 - (BOOL) primaryStoreProfile
256 GCSChannelManager *cm;
257 EOAdaptorChannel *channel;
264 cm = [GCSChannelManager defaultChannelManager];
265 sql = ((defFlags.isNew)
266 ? [self generateSQLForInsert]
267 : [self generateSQLForUpdate]);
270 channel = [cm acquireOpenChannelForURL: [self tableURL]];
273 ex = [channel evaluateExpressionX:sql];
275 [self errorWithFormat: @"could not run SQL '%@': %@", sql, ex];
278 if ([[channel adaptorContext] hasOpenTransaction])
280 ex = [channel evaluateExpressionX: @"COMMIT TRANSACTION"];
282 [self errorWithFormat:@"could not commit transaction for update: %@", ex];
289 defFlags.modified = NO;
293 [cm releaseChannel: channel];
296 [self errorWithFormat: @"failed to acquire channel for URL: %@",
300 [self errorWithFormat: @"failed to generate SQL for storing defaults"];
305 - (BOOL) fetchProfile
307 return (values || [self primaryFetchProfile]);
312 - (void) setObject: (id) value
313 forKey: (NSString *) key
317 if (![self fetchProfile])
320 /* check whether the value is actually modified */
321 if (!defFlags.modified)
323 old = [values objectForKey: key];
324 if (old == value || [old isEqual: value]) /* value didn't change */
327 /* we need to this because our typed accessors convert to strings */
328 // TODO: especially problematic with bools
329 if ([value isKindOfClass: [NSString class]]) {
330 if (![old isKindOfClass: [NSString class]])
331 if ([[old description] isEqualToString: value])
336 /* set in hash and mark as modified */
337 [values setObject: value forKey: key];
339 defFlags.modified = YES;
342 - (id) objectForKey: (NSString *) key
346 if (![self fetchProfile])
349 value = [values objectForKey: key];
354 - (void) removeObjectForKey: (NSString *) key
356 [self setObject: nil forKey: key];
363 // if (!defFlags.modified) /* was not modified */
366 /* ensure fetched data (more or less guaranteed by modified!=0) */
367 if (![self fetchProfile])
371 if (![self primaryStoreProfile])
373 [self primaryFetchProfile];
378 return [self primaryFetchProfile];
387 defFlags.modified = NO;
391 /* typed accessors */
393 - (NSArray *) arrayForKey: (NSString *) key
395 return [self objectForKey: key];
398 - (NSDictionary *) dictionaryForKey: (NSString *) key
400 return [self objectForKey: key];
403 - (NSData *) dataForKey: (NSString *) key
405 return [self objectForKey: key];
408 - (NSString *) stringForKey: (NSString *) key
410 return [self objectForKey: key];
413 - (BOOL) boolForKey: (NSString *) key
415 return [[self objectForKey: key] boolValue];
418 - (float) floatForKey: (NSString *) key
420 return [[self objectForKey: key] floatValue];
423 - (int) integerForKey: (NSString *) key
425 return [[self objectForKey: key] intValue];
428 - (void) setBool: (BOOL) value
429 forKey: (NSString *) key
431 // TODO: need special support here for int-DB fields
432 [self setObject: [NSNumber numberWithBool: value]
436 - (void) setFloat: (float) value
437 forKey: (NSString *) key
439 [self setObject: [NSNumber numberWithFloat: value]
443 - (void) setInteger: (int) value
444 forKey: (NSString *) key
446 [self setObject: [NSNumber numberWithInt: value]
450 @end /* AgenorUserDefaults */