mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-06-02 08:59:45 +00:00
Monotone-Parent: a262ef7ad120d7456b43032b5f2840b51fa16354
Monotone-Revision: ad1245283296dc9882631027852d56c0b02756cc Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2007-03-27T18:12:02 Monotone-Branch: ca.inverse.sogo
This commit is contained in:
@@ -1,5 +1,12 @@
|
||||
2007-03-27 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* SoObjects/SOGo/AgenorUserDefaults.m: rewrote a big part of the
|
||||
module to bind the userdefaults to a property list contained in a
|
||||
specified field of the sogo_user_profile table.
|
||||
([AgenorUserDefaults
|
||||
-initWithTableURL:tableURLuid:userIDfieldName:defaultsFieldName]):
|
||||
added a "fieldName" parameter.
|
||||
|
||||
* UI/Common/UIxFolderActions.m: new module implementing web
|
||||
actions common to all GCS-based folders.
|
||||
|
||||
|
||||
@@ -34,51 +34,54 @@
|
||||
@class NSString, NSURL, NSUserDefaults, NSArray, NSDictionary, NSData;
|
||||
@class NSCalendarDate, NSMutableDictionary;
|
||||
|
||||
@interface AgenorUserDefaults : NSObject
|
||||
@interface AgenorUserDefaults : NSObject
|
||||
{
|
||||
NSUserDefaults *parent;
|
||||
NSURL *url;
|
||||
NSString *uid;
|
||||
NSString *fieldName;
|
||||
|
||||
NSArray *fieldNames;
|
||||
NSDictionary *attributes;
|
||||
NSArray *fieldNames;
|
||||
NSMutableDictionary *values;
|
||||
NSCalendarDate *lastFetch;
|
||||
NSCalendarDate *lastFetch;
|
||||
|
||||
struct {
|
||||
int modified:1;
|
||||
int isNew:1;
|
||||
int reserved:30;
|
||||
struct
|
||||
{
|
||||
int modified: 1;
|
||||
int isNew: 1;
|
||||
int reserved: 30;
|
||||
} defFlags;
|
||||
}
|
||||
|
||||
- (id)initWithTableURL:(NSURL *)_url uid:(NSString *)_uid;
|
||||
- (id) initWithTableURL: (NSURL *) url
|
||||
uid: (NSString *) uid
|
||||
fieldName: (NSString *) fieldName;
|
||||
|
||||
/* value access */
|
||||
|
||||
- (void)setObject:(id)_value forKey:(NSString *)_key;
|
||||
- (id)objectForKey:(NSString *)_key;
|
||||
- (void)removeObjectForKey:(NSString *)_key;
|
||||
- (void) setObject: (id) value
|
||||
forKey: (NSString *) key;
|
||||
- (id) objectForKey: (NSString *) key;
|
||||
- (void) removeObjectForKey: (NSString *) key;
|
||||
|
||||
/* typed accessors */
|
||||
|
||||
- (NSArray *)arrayForKey:(NSString *)_key;
|
||||
- (NSDictionary *)dictionaryForKey:(NSString *)_key;
|
||||
- (NSData *)dataForKey:(NSString *)_key;
|
||||
- (NSArray *)stringArrayForKey:(NSString *)_key;
|
||||
- (NSString *)stringForKey:(NSString *)_key;
|
||||
- (BOOL)boolForKey:(NSString *)_key;
|
||||
- (float)floatForKey:(NSString *)_key;
|
||||
- (int)integerForKey:(NSString *)_key;
|
||||
- (NSArray *) arrayForKey: (NSString *)key;
|
||||
- (NSDictionary *) dictionaryForKey: (NSString *)key;
|
||||
- (NSData *) dataForKey: (NSString *)key;
|
||||
- (NSString *) stringForKey: (NSString *)key;
|
||||
- (BOOL) boolForKey: (NSString *) key;
|
||||
- (float) floatForKey: (NSString *) key;
|
||||
- (int) integerForKey: (NSString *) key;
|
||||
|
||||
- (void)setBool:(BOOL)value forKey:(NSString *)_key;
|
||||
- (void)setFloat:(float)value forKey:(NSString *)_key;
|
||||
- (void)setInteger:(int)value forKey:(NSString *)_key;
|
||||
- (void) setBool: (BOOL) value forKey: (NSString *) key;
|
||||
- (void) setFloat: (float) value forKey: (NSString *) key;
|
||||
- (void) setInteger: (int) value forKey: (NSString *) key;
|
||||
|
||||
/* saving changes */
|
||||
|
||||
- (BOOL)synchronize;
|
||||
- (BOOL) synchronize;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* __AgenorUserDefaults_H_ */
|
||||
#endif /* __AgenorUserDefaults_H__ */
|
||||
|
||||
+307
-354
@@ -19,454 +19,407 @@
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "AgenorUserDefaults.h"
|
||||
#include <GDLContentStore/GCSChannelManager.h>
|
||||
#include <GDLContentStore/NSURL+GCS.h>
|
||||
#include <GDLAccess/EOAdaptorChannel.h>
|
||||
#include <GDLAccess/EOAdaptorContext.h>
|
||||
#include <GDLAccess/EOAttribute.h>
|
||||
#include "common.h"
|
||||
#import <Foundation/NSValue.h>
|
||||
#import <GDLContentStore/GCSChannelManager.h>
|
||||
#import <GDLContentStore/NSURL+GCS.h>
|
||||
#import <GDLAccess/EOAdaptorChannel.h>
|
||||
#import <GDLAccess/EOAdaptorContext.h>
|
||||
#import <GDLAccess/EOAttribute.h>
|
||||
|
||||
#import "common.h"
|
||||
#import "AgenorUserDefaults.h"
|
||||
|
||||
@implementation AgenorUserDefaults
|
||||
|
||||
static NSString *uidColumnName = @"uid";
|
||||
|
||||
- (id)initWithTableURL:(NSURL *)_url uid:(NSString *)_uid {
|
||||
if ((self = [super init])) {
|
||||
if (_url == nil || [_uid length] < 1) {
|
||||
[self errorWithFormat:@"tried to create AgenorUserDefaults w/o args!"];
|
||||
[self release];
|
||||
return nil;
|
||||
- (id) initWithTableURL: (NSURL *) tableURL
|
||||
uid: (NSString *) userID
|
||||
fieldName: (NSString *) defaultsFieldName
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
if (tableURL && [userID length] > 0
|
||||
&& [defaultsFieldName length] > 0)
|
||||
{
|
||||
parent = [[NSUserDefaults standardUserDefaults] retain];
|
||||
fieldName = [defaultsFieldName copy];
|
||||
url = [tableURL copy];
|
||||
uid = [userID copy];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorWithFormat: @"missing arguments"];
|
||||
[self release];
|
||||
self = nil;
|
||||
}
|
||||
}
|
||||
|
||||
self->parent = [[NSUserDefaults standardUserDefaults] retain];
|
||||
self->url = [_url copy];
|
||||
self->uid = [_uid copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (id)init {
|
||||
return [self initWithTableURL:nil uid:nil];
|
||||
|
||||
- (id) init
|
||||
{
|
||||
[self release];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self->attributes release];
|
||||
[self->lastFetch release];
|
||||
[self->parent release];
|
||||
[self->url release];
|
||||
[self->uid release];
|
||||
- (void) dealloc
|
||||
{
|
||||
[values release];
|
||||
[lastFetch release];
|
||||
[parent release];
|
||||
[url release];
|
||||
[uid release];
|
||||
[fieldName release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
/* accessors */
|
||||
|
||||
- (NSURL *)tableURL {
|
||||
return self->url;
|
||||
}
|
||||
- (NSString *)uid {
|
||||
return self->uid;
|
||||
- (NSURL *) tableURL
|
||||
{
|
||||
return url;
|
||||
}
|
||||
|
||||
- (NSUserDefaults *)parentDefaults {
|
||||
return self->parent;
|
||||
- (NSString *) uid
|
||||
{
|
||||
return uid;
|
||||
}
|
||||
|
||||
- (NSString *) fieldName
|
||||
{
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
- (NSUserDefaults *) parentDefaults
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
|
||||
/* operation */
|
||||
|
||||
- (void)_loadAttributes:(NSArray *)_attrs {
|
||||
NSMutableArray *fields;
|
||||
NSMutableDictionary *attrmap;
|
||||
unsigned i, count;
|
||||
|
||||
fields = [[NSMutableArray alloc] initWithCapacity:16];
|
||||
attrmap = [[NSMutableDictionary alloc] initWithCapacity:16];
|
||||
for (i = 0, count = [_attrs count]; i < count; i++) {
|
||||
EOAttribute *attr;
|
||||
NSString *name;
|
||||
|
||||
attr = [_attrs objectAtIndex:i];
|
||||
name = [attr valueForKey:@"name"];
|
||||
[attrmap setObject:attr forKey:name];
|
||||
|
||||
if (![name isEqual:uidColumnName])
|
||||
[fields addObject:name];
|
||||
}
|
||||
|
||||
ASSIGNCOPY(self->fieldNames, fields);
|
||||
ASSIGNCOPY(self->attributes, attrmap);
|
||||
[attrmap release];
|
||||
[fields release];
|
||||
}
|
||||
|
||||
- (BOOL)primaryFetchProfile {
|
||||
- (BOOL) primaryFetchProfile
|
||||
{
|
||||
GCSChannelManager *cm;
|
||||
EOAdaptorChannel *channel;
|
||||
NSDictionary *row;
|
||||
NSException *ex;
|
||||
NSString *sql;
|
||||
NSArray *attrs;
|
||||
EOAdaptorChannel *channel;
|
||||
NSDictionary *row;
|
||||
NSException *ex;
|
||||
NSString *sql;
|
||||
NSArray *attrs;
|
||||
BOOL rc;
|
||||
|
||||
rc = NO;
|
||||
|
||||
cm = [GCSChannelManager defaultChannelManager];
|
||||
if ((channel = [cm acquireOpenChannelForURL:[self tableURL]]) == nil) {
|
||||
channel = [cm acquireOpenChannelForURL: [self tableURL]];
|
||||
if (channel)
|
||||
{
|
||||
/* generate SQL */
|
||||
sql = [NSString stringWithFormat: (@"SELECT %@"
|
||||
@" FROM %@"
|
||||
@" WHERE %@ = '%@'"),
|
||||
fieldName, [[self tableURL] gcsTableName],
|
||||
uidColumnName, [self uid]];
|
||||
|
||||
/* run SQL */
|
||||
|
||||
ex = [channel evaluateExpressionX: sql];
|
||||
if (ex)
|
||||
{
|
||||
[self errorWithFormat:@"could not run SQL '%@': %@", sql, ex];
|
||||
values = [NSMutableDictionary new];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* fetch schema */
|
||||
attrs = [channel describeResults: NO /* don't beautify */];
|
||||
|
||||
/* fetch values */
|
||||
row = [channel fetchAttributes: attrs withZone: NULL];
|
||||
defFlags.isNew = (row == nil);
|
||||
[channel cancelFetch];
|
||||
|
||||
/* remember values */
|
||||
[values release];
|
||||
values = [[row objectForKey: fieldName] propertyList];
|
||||
if (values)
|
||||
[values retain];
|
||||
else
|
||||
values = [NSMutableDictionary new];
|
||||
|
||||
ASSIGN(lastFetch, [NSCalendarDate date]);
|
||||
defFlags.modified = NO;
|
||||
rc = YES;
|
||||
}
|
||||
|
||||
[cm releaseChannel:channel];
|
||||
}
|
||||
else
|
||||
[self errorWithFormat:@"failed to acquire channel for URL: %@",
|
||||
[self tableURL]];
|
||||
return NO;
|
||||
}
|
||||
|
||||
/* generate SQL */
|
||||
|
||||
sql = [[self tableURL] gcsTableName];
|
||||
sql = [@"SELECT * FROM " stringByAppendingString:sql];
|
||||
sql = [sql stringByAppendingFormat:@" WHERE %@ = '%@'",
|
||||
uidColumnName, [self uid]];
|
||||
|
||||
/* run SQL */
|
||||
|
||||
if ((ex = [channel evaluateExpressionX:sql]) != nil) {
|
||||
[self errorWithFormat:@"could not run SQL '%@': %@", sql, ex];
|
||||
[cm releaseChannel:channel];
|
||||
return NO;
|
||||
}
|
||||
|
||||
/* fetch schema */
|
||||
|
||||
attrs = [channel describeResults:NO /* don't beautify */];
|
||||
[self _loadAttributes:attrs];
|
||||
|
||||
/* fetch values */
|
||||
|
||||
row = [channel fetchAttributes:attrs withZone:NULL];
|
||||
self->defFlags.isNew = (row != nil) ? 0 : 1;
|
||||
[channel cancelFetch];
|
||||
|
||||
/* remember values */
|
||||
|
||||
[self->values release]; self->values = nil;
|
||||
self->values = (row != nil)
|
||||
? [row mutableCopy]
|
||||
: [[NSMutableDictionary alloc] initWithCapacity:8];
|
||||
[self->values removeObjectForKey:uidColumnName];
|
||||
|
||||
ASSIGN(self->lastFetch, [NSCalendarDate date]);
|
||||
self->defFlags.modified = 0;
|
||||
|
||||
[cm releaseChannel:channel];
|
||||
return YES;
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (NSString *)formatValue:(id)_value forAttribute:(EOAttribute *)_attribute {
|
||||
NSString *s;
|
||||
|
||||
if (![_value isNotNull])
|
||||
return @"NULL";
|
||||
|
||||
if ([[_attribute externalType] hasPrefix:@"int"])
|
||||
return [_value stringValue];
|
||||
|
||||
s = [_value stringValue];
|
||||
s = [s stringByReplacingString:@"'" withString:@"''"];
|
||||
s = [[@"'" stringByAppendingString:s] stringByAppendingString:@"'"];
|
||||
return s;
|
||||
}
|
||||
|
||||
- (NSString *)generateSQLForInsert {
|
||||
- (NSString *) generateSQLForInsert
|
||||
{
|
||||
NSMutableString *sql;
|
||||
unsigned i, count;
|
||||
|
||||
if ([self->values count] == 0)
|
||||
return nil;
|
||||
|
||||
sql = [NSMutableString stringWithCapacity:2048];
|
||||
|
||||
[sql appendString:@"INSERT INTO "];
|
||||
[sql appendString:[[self tableURL] gcsTableName]];
|
||||
[sql appendString:@" ( uid"];
|
||||
NSString *serializedDefaults, *error;
|
||||
NSData *serializedDefaultsData;
|
||||
|
||||
serializedDefaultsData
|
||||
= [NSPropertyListSerialization dataFromPropertyList: values
|
||||
format: NSPropertyListOpenStepFormat
|
||||
errorDescription: &error];
|
||||
if (error)
|
||||
sql = nil;
|
||||
else
|
||||
{
|
||||
serializedDefaults = [[NSString alloc] initWithData: serializedDefaultsData
|
||||
encoding: NSUTF8StringEncoding];
|
||||
|
||||
sql = [NSString stringWithFormat: (@"INSERT INTO %@"
|
||||
@" (%@, %@)"
|
||||
@" VALUES ('%@', '%@')"),
|
||||
[[self tableURL] gcsTableName], uidColumnName, fieldName,
|
||||
[self uid],
|
||||
[serializedDefaults stringByReplacingString:@"'" withString:@"''"]];
|
||||
[serializedDefaults release];
|
||||
}
|
||||
|
||||
for (i = 0, count = [self->fieldNames count]; i < count; i++) {
|
||||
EOAttribute *attr;
|
||||
|
||||
attr = [self->attributes objectForKey:[self->fieldNames objectAtIndex:i]];
|
||||
[sql appendString:@", "];
|
||||
[sql appendString:[attr columnName]];
|
||||
}
|
||||
|
||||
[sql appendString:@") VALUES ("];
|
||||
|
||||
[sql appendString:@"'"];
|
||||
[sql appendString:[self uid]]; // TODO: escaping necessary?
|
||||
[sql appendString:@"'"];
|
||||
|
||||
for (i = 0, count = [self->fieldNames count]; i < count; i++) {
|
||||
EOAttribute *attr;
|
||||
id value;
|
||||
|
||||
attr = [self->attributes objectForKey:[self->fieldNames objectAtIndex:i]];
|
||||
value = [self->values objectForKey:[self->fieldNames objectAtIndex:i]];
|
||||
|
||||
[sql appendString:@", "];
|
||||
[sql appendString:[self formatValue:value forAttribute:attr]];
|
||||
}
|
||||
|
||||
[sql appendString:@")"];
|
||||
return sql;
|
||||
}
|
||||
|
||||
- (NSString *)generateSQLForUpdate {
|
||||
- (NSString *) generateSQLForUpdate
|
||||
{
|
||||
NSMutableString *sql;
|
||||
unsigned i, count;
|
||||
|
||||
if ([self->values count] == 0)
|
||||
return nil;
|
||||
|
||||
sql = [NSMutableString stringWithCapacity:2048];
|
||||
|
||||
[sql appendString:@"UPDATE "];
|
||||
[sql appendString:[[self tableURL] gcsTableName]];
|
||||
[sql appendString:@" SET "];
|
||||
|
||||
for (i = 0, count = [self->fieldNames count]; i < count; i++) {
|
||||
EOAttribute *attr;
|
||||
NSString *name;
|
||||
id value;
|
||||
|
||||
name = [self->fieldNames objectAtIndex:i];
|
||||
value = [self->values objectForKey:name];
|
||||
attr = [self->attributes objectForKey:name];
|
||||
|
||||
if (i != 0) [sql appendString:@", "];
|
||||
[sql appendString:[attr columnName]];
|
||||
[sql appendString:@" = "];
|
||||
[sql appendString:[self formatValue:value forAttribute:attr]];
|
||||
}
|
||||
|
||||
[sql appendString:@" WHERE "];
|
||||
[sql appendString:uidColumnName];
|
||||
[sql appendString:@" = '"];
|
||||
[sql appendString:[self uid]];
|
||||
[sql appendString:@"'"];
|
||||
NSString *serializedDefaults, *error;
|
||||
NSData *serializedDefaultsData;
|
||||
|
||||
error = nil;
|
||||
serializedDefaultsData
|
||||
= [NSPropertyListSerialization dataFromPropertyList: values
|
||||
format: NSPropertyListOpenStepFormat
|
||||
errorDescription: &error];
|
||||
if (error)
|
||||
{
|
||||
sql = nil;
|
||||
[error release];
|
||||
}
|
||||
else
|
||||
{
|
||||
serializedDefaults = [[NSString alloc] initWithData: serializedDefaultsData
|
||||
encoding: NSUTF8StringEncoding];
|
||||
|
||||
sql = [NSString stringWithFormat: (@"UPDATE %@"
|
||||
@" SET %@ = '%@'"
|
||||
@" WHERE %@ = '%@'"),
|
||||
[[self tableURL] gcsTableName],
|
||||
fieldName,
|
||||
[serializedDefaults stringByReplacingString:@"'" withString:@"''"],
|
||||
uidColumnName, [self uid]];
|
||||
[serializedDefaults release];
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
- (BOOL)primaryStoreProfile {
|
||||
- (BOOL) primaryStoreProfile
|
||||
{
|
||||
GCSChannelManager *cm;
|
||||
EOAdaptorChannel *channel;
|
||||
NSException *ex;
|
||||
NSString *sql;
|
||||
EOAdaptorChannel *channel;
|
||||
NSException *ex;
|
||||
NSString *sql;
|
||||
BOOL rc;
|
||||
|
||||
rc = NO;
|
||||
|
||||
cm = [GCSChannelManager defaultChannelManager];
|
||||
if ((channel = [cm acquireOpenChannelForURL:[self tableURL]]) == nil) {
|
||||
[self errorWithFormat:@"failed to acquire channel for URL: %@",
|
||||
[self tableURL]];
|
||||
return NO;
|
||||
}
|
||||
|
||||
/* run SQL */
|
||||
|
||||
sql = self->defFlags.isNew
|
||||
? [self generateSQLForInsert]
|
||||
: [self generateSQLForUpdate];
|
||||
if ((ex = [channel evaluateExpressionX:sql]) != nil) {
|
||||
[self errorWithFormat:@"could not run SQL '%@': %@", sql, ex];
|
||||
[cm releaseChannel:channel];
|
||||
return NO;
|
||||
}
|
||||
|
||||
/* commit */
|
||||
|
||||
ex = nil;
|
||||
if ([[channel adaptorContext] hasOpenTransaction])
|
||||
ex = [channel evaluateExpressionX:@"COMMIT TRANSACTION"];
|
||||
|
||||
[cm releaseChannel:channel];
|
||||
|
||||
if (ex != nil) {
|
||||
[self errorWithFormat:@"could not commit transaction for update: %@", ex];
|
||||
return NO;
|
||||
}
|
||||
|
||||
self->defFlags.modified = 0;
|
||||
self->defFlags.isNew = 0;
|
||||
return YES;
|
||||
sql = ((defFlags.isNew)
|
||||
? [self generateSQLForInsert]
|
||||
: [self generateSQLForUpdate]);
|
||||
if (sql)
|
||||
{
|
||||
channel = [cm acquireOpenChannelForURL: [self tableURL]];
|
||||
if (channel)
|
||||
{
|
||||
ex = [channel evaluateExpressionX:sql];
|
||||
if (ex)
|
||||
[self errorWithFormat: @"could not run SQL '%@': %@", sql, ex];
|
||||
else
|
||||
{
|
||||
if ([[channel adaptorContext] hasOpenTransaction])
|
||||
{
|
||||
ex = [channel evaluateExpressionX: @"COMMIT TRANSACTION"];
|
||||
if (ex)
|
||||
[self errorWithFormat:@"could not commit transaction for update: %@", ex];
|
||||
else
|
||||
rc = YES;
|
||||
}
|
||||
else
|
||||
rc = YES;
|
||||
|
||||
defFlags.modified = NO;
|
||||
defFlags.isNew = NO;
|
||||
}
|
||||
|
||||
[cm releaseChannel: channel];
|
||||
}
|
||||
else
|
||||
[self errorWithFormat: @"failed to acquire channel for URL: %@",
|
||||
[self tableURL]];
|
||||
}
|
||||
else
|
||||
[self errorWithFormat: @"failed to generate SQL for storing defaults"];
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)fetchProfile {
|
||||
if (self->values != nil)
|
||||
return YES;
|
||||
|
||||
return [self primaryFetchProfile];
|
||||
}
|
||||
|
||||
- (NSArray *)primaryDefaultNames {
|
||||
if (![self fetchProfile])
|
||||
return nil;
|
||||
|
||||
return self->fieldNames;
|
||||
- (BOOL) fetchProfile
|
||||
{
|
||||
return (values || [self primaryFetchProfile]);
|
||||
}
|
||||
|
||||
/* value access */
|
||||
|
||||
- (void)setObject:(id)_value forKey:(NSString *)_key {
|
||||
- (void) setObject: (id) value
|
||||
forKey: (NSString *) key
|
||||
{
|
||||
id old;
|
||||
|
||||
if (![self fetchProfile])
|
||||
return;
|
||||
|
||||
if (![self->fieldNames containsObject:_key]) {
|
||||
[self errorWithFormat:@"tried to write key: '%@'", _key];
|
||||
return;
|
||||
}
|
||||
|
||||
/* check whether the value is actually modified */
|
||||
if (!self->defFlags.modified) {
|
||||
id old;
|
||||
|
||||
old = [self->values objectForKey:_key];
|
||||
if (old == _value || [old isEqual:_value]) /* value didn't change */
|
||||
return;
|
||||
/* check whether the value is actually modified */
|
||||
if (!defFlags.modified)
|
||||
{
|
||||
old = [values objectForKey: key];
|
||||
if (old == value || [old isEqual: value]) /* value didn't change */
|
||||
return;
|
||||
|
||||
/* we need to this because our typed accessors convert to strings */
|
||||
// TODO: especially problematic with bools
|
||||
if ([_value isKindOfClass:[NSString class]]) {
|
||||
if (![old isKindOfClass:[NSString class]])
|
||||
if ([[old description] isEqualToString:_value])
|
||||
return;
|
||||
/* we need to this because our typed accessors convert to strings */
|
||||
// TODO: especially problematic with bools
|
||||
if ([value isKindOfClass: [NSString class]]) {
|
||||
if (![old isKindOfClass: [NSString class]])
|
||||
if ([[old description] isEqualToString: value])
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* set in hash and mark as modified */
|
||||
[self->values setObject:(_value ? _value : [NSNull null]) forKey:_key];
|
||||
self->defFlags.modified = 1;
|
||||
[values setObject: value forKey: key];
|
||||
|
||||
defFlags.modified = YES;
|
||||
}
|
||||
|
||||
- (id)objectForKey:(NSString *)_key {
|
||||
- (id) objectForKey: (NSString *) key
|
||||
{
|
||||
id value;
|
||||
|
||||
if (![self fetchProfile])
|
||||
return nil;
|
||||
|
||||
if (![self->fieldNames containsObject:_key])
|
||||
return [self->parent objectForKey:_key];
|
||||
|
||||
value = [self->values objectForKey:_key];
|
||||
return [value isNotNull] ? value : nil;
|
||||
value = nil;
|
||||
else
|
||||
value = [values objectForKey: key];
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
- (void)removeObjectForKey:(NSString *)_key {
|
||||
[self setObject:nil forKey:_key];
|
||||
- (void) removeObjectForKey: (NSString *) key
|
||||
{
|
||||
[self setObject: nil forKey: key];
|
||||
}
|
||||
|
||||
/* saving changes */
|
||||
|
||||
- (BOOL)synchronize {
|
||||
if (!self->defFlags.modified) /* was not modified */
|
||||
return YES;
|
||||
- (BOOL) synchronize
|
||||
{
|
||||
// if (!defFlags.modified) /* was not modified */
|
||||
// return YES;
|
||||
|
||||
/* ensure fetched data (more or less guaranteed by modified!=0) */
|
||||
if (![self fetchProfile])
|
||||
return NO;
|
||||
|
||||
/* store */
|
||||
if (![self primaryStoreProfile]) {
|
||||
[self primaryFetchProfile];
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (![self primaryStoreProfile])
|
||||
{
|
||||
[self primaryFetchProfile];
|
||||
return NO;
|
||||
}
|
||||
|
||||
/* refetch */
|
||||
return [self primaryFetchProfile];
|
||||
}
|
||||
|
||||
- (void)flush {
|
||||
[self->values release]; self->values = nil;
|
||||
[self->fieldNames release]; self->fieldNames = nil;
|
||||
[self->attributes release]; self->attributes = nil;
|
||||
[self->lastFetch release]; self->lastFetch = nil;
|
||||
self->defFlags.modified = 0;
|
||||
self->defFlags.isNew = 0;
|
||||
- (void) flush
|
||||
{
|
||||
[values release];
|
||||
[lastFetch release];
|
||||
values = nil;
|
||||
lastFetch = nil;
|
||||
defFlags.modified = NO;
|
||||
defFlags.isNew = NO;
|
||||
}
|
||||
|
||||
/* typed accessors */
|
||||
|
||||
- (NSArray *)arrayForKey:(NSString *)_key {
|
||||
id obj = [self objectForKey:_key];
|
||||
return [obj isKindOfClass:[NSArray class]] ? obj : nil;
|
||||
- (NSArray *) arrayForKey: (NSString *) key
|
||||
{
|
||||
return [self objectForKey: key];
|
||||
}
|
||||
|
||||
- (NSDictionary *)dictionaryForKey:(NSString *)_key {
|
||||
id obj = [self objectForKey:_key];
|
||||
return [obj isKindOfClass:[NSDictionary class]] ? obj : nil;
|
||||
- (NSDictionary *) dictionaryForKey: (NSString *) key
|
||||
{
|
||||
return [self objectForKey: key];
|
||||
}
|
||||
|
||||
- (NSData *)dataForKey:(NSString *)_key {
|
||||
id obj = [self objectForKey:_key];
|
||||
return [obj isKindOfClass:[NSData class]] ? obj : nil;
|
||||
- (NSData *) dataForKey: (NSString *) key
|
||||
{
|
||||
return [self objectForKey: key];
|
||||
}
|
||||
|
||||
- (NSArray *)stringArrayForKey:(NSString *)_key {
|
||||
id obj = [self objectForKey:_key];
|
||||
int n;
|
||||
Class strClass = [NSString class];
|
||||
|
||||
if (![obj isKindOfClass:[NSArray class]])
|
||||
return nil;
|
||||
|
||||
for (n = [obj count]-1; n >= 0; n--) {
|
||||
if (![[obj objectAtIndex:n] isKindOfClass:strClass])
|
||||
return nil;
|
||||
}
|
||||
return obj;
|
||||
- (NSString *) stringForKey: (NSString *) key
|
||||
{
|
||||
return [self objectForKey: key];
|
||||
}
|
||||
|
||||
- (NSString *)stringForKey:(NSString *)_key {
|
||||
id obj = [self objectForKey:_key];
|
||||
return [obj isKindOfClass:[NSString class]] ? obj : nil;
|
||||
- (BOOL) boolForKey: (NSString *) key
|
||||
{
|
||||
return [[self objectForKey: key] boolValue];
|
||||
}
|
||||
|
||||
- (BOOL)boolForKey:(NSString *)_key {
|
||||
- (float) floatForKey: (NSString *) key
|
||||
{
|
||||
return [[self objectForKey: key] floatValue];
|
||||
}
|
||||
|
||||
- (int) integerForKey: (NSString *) key
|
||||
{
|
||||
return [[self objectForKey: key] intValue];
|
||||
}
|
||||
|
||||
- (void) setBool: (BOOL) value
|
||||
forKey: (NSString *) key
|
||||
{
|
||||
// TODO: need special support here for int-DB fields
|
||||
id obj;
|
||||
|
||||
if ((obj = [self objectForKey:_key]) == nil)
|
||||
return NO;
|
||||
if ([obj isKindOfClass:[NSString class]]) {
|
||||
if ([obj compare:@"YES" options:NSCaseInsensitiveSearch] == NSOrderedSame)
|
||||
return YES;
|
||||
}
|
||||
if ([obj respondsToSelector:@selector(intValue)])
|
||||
return [obj intValue] ? YES : NO;
|
||||
return NO;
|
||||
[self setObject: [NSNumber numberWithBool: value]
|
||||
forKey: key];
|
||||
}
|
||||
|
||||
- (float)floatForKey:(NSString *)_key {
|
||||
id obj = [self stringForKey:_key];
|
||||
return (obj != nil) ? [obj floatValue] : 0.0;
|
||||
}
|
||||
- (int)integerForKey:(NSString *)_key {
|
||||
id obj = [self stringForKey:_key];
|
||||
return (obj != nil) ? [obj intValue] : 0;
|
||||
- (void) setFloat: (float) value
|
||||
forKey: (NSString *) key
|
||||
{
|
||||
[self setObject: [NSNumber numberWithFloat: value]
|
||||
forKey: key];
|
||||
}
|
||||
|
||||
- (void)setBool:(BOOL)value forKey:(NSString *)_key {
|
||||
// TODO: need special support here for int-DB fields
|
||||
[self setObject:(value ? @"YES" : @"NO") forKey:_key];
|
||||
}
|
||||
- (void)setFloat:(float)value forKey:(NSString *)_key {
|
||||
[self setObject:[NSString stringWithFormat:@"%f", value] forKey:_key];
|
||||
}
|
||||
- (void)setInteger:(int)value forKey:(NSString *)_key {
|
||||
[self setObject:[NSString stringWithFormat:@"%d", value] forKey:_key];
|
||||
}
|
||||
|
||||
/* description */
|
||||
|
||||
- (NSString *)description {
|
||||
NSMutableString *ms;
|
||||
|
||||
ms = [NSMutableString stringWithCapacity:16];
|
||||
[ms appendFormat:@"<0x%08X[%@]>", self, NSStringFromClass([self class])];
|
||||
[ms appendFormat:@" uid=%@", self->uid];
|
||||
[ms appendFormat:@" url=%@", [self->url absoluteString]];
|
||||
[ms appendFormat:@" parent=0x%08X", self->parent];
|
||||
[ms appendString:@">"];
|
||||
return ms;
|
||||
- (void) setInteger: (int) value
|
||||
forKey: (NSString *) key
|
||||
{
|
||||
[self setObject: [NSNumber numberWithInt: value]
|
||||
forKey: key];
|
||||
}
|
||||
|
||||
@end /* AgenorUserDefaults */
|
||||
|
||||
Reference in New Issue
Block a user