User defaults language/setup restructuring

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@32454 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
rfm 2011-03-05 07:54:05 +00:00
parent aa9300f786
commit f169caa814
6 changed files with 319 additions and 335 deletions

View file

@ -1,3 +1,20 @@
2011-03-05 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSNumberFormatter.m: Make error text compatible with OSX
* Source/NSSpellServer.m: Use NSLanguages user default
* Source/NSBundle.m: Use NSLanguages user default
* Source/NSUserDefaults.m:
Revert recent attempt to avoid deadlocks (broke thread-safety).
Rewrite shared defaults initialisation and user language management
to break the complicated interrelationshp between methods which
made the initialisation fragile and incomprehensible.
Language information derived from operating system or environment
variables (rather than the defaults database) is now stored in a
new volatile domain.
The +userLanguages method just returns the NSLanguages default,
and +setUserLanguages: now sets NSLanguages in the new volatile domain.
Simplify setup of shared user defaults to avoid deadlocks.
2011-03-04 Richard Frith-Macdonald <rfm@gnu.org> 2011-03-04 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSOperation.m: retain operation while finishing so that it * Source/NSOperation.m: retain operation while finishing so that it

View file

@ -1685,7 +1685,8 @@ IF_NO_GC(
NSEnumerator *enumerate; NSEnumerator *enumerate;
array = [NSMutableArray arrayWithCapacity: 8]; array = [NSMutableArray arrayWithCapacity: 8];
languages = [NSUserDefaults userLanguages]; languages = [[NSUserDefaults standardUserDefaults]
stringArrayForKey: @"NSLanguages"];
primary = [rootPath stringByAppendingPathComponent: @"Resources"]; primary = [rootPath stringByAppendingPathComponent: @"Resources"];
[array addObject: _bundle_resource_path(primary, subPath, nil)]; [array addObject: _bundle_resource_path(primary, subPath, nil)];
@ -1945,7 +1946,8 @@ IF_NO_GC(
+ (NSArray *) preferredLocalizationsFromArray: (NSArray *)localizationsArray + (NSArray *) preferredLocalizationsFromArray: (NSArray *)localizationsArray
{ {
return [self preferredLocalizationsFromArray: localizationsArray return [self preferredLocalizationsFromArray: localizationsArray
forPreferences: [NSUserDefaults userLanguages]]; forPreferences: [[NSUserDefaults standardUserDefaults]
stringArrayForKey: @"NSLanguages"]];
} }
+ (NSArray *) preferredLocalizationsFromArray: (NSArray *)localizationsArray + (NSArray *) preferredLocalizationsFromArray: (NSArray *)localizationsArray

View file

@ -543,8 +543,10 @@ static NSRecursiveLock *classLock = nil;
NSMutableArray *mArray; NSMutableArray *mArray;
NSUInteger cnt; NSUInteger cnt;
NSUInteger idx = 0; NSUInteger idx = 0;
NSArray *languages = [NSUserDefaults userLanguages]; NSArray *languages;
languages = [[NSUserDefaults standardUserDefaults]
stringArrayForKey: @"NSLanguages"];
if (languages == nil) if (languages == nil)
return [NSArray arrayWithObject: @"en"]; return [NSArray arrayWithObject: @"en"];

View file

@ -419,7 +419,7 @@ static NSUInteger _defaultBehavior = NSNumberFormatterBehavior10_4;
{ {
if (0 != error) if (0 != error)
{ {
*error = _(@"floating point values not allowed"); *error = _(@"Floating Point not allowed");
} }
return NO; return NO;
} }

View file

@ -81,9 +81,12 @@ GSSpellServerName(NSString *vendor, NSString *language)
// Non-private Instance methods // Non-private Instance methods
- (id) init - (id) init
{ {
NSArray *userLanguages = [NSUserDefaults userLanguages]; NSArray *userPreference;
NSString *currentLanguage = [userLanguages objectAtIndex: 0]; NSString *currentLanguage;
userPreference = [[NSUserDefaults standardUserDefaults]
stringArrayForKey: @"NSLanguages"];
currentLanguage = [userPreference objectAtIndex: 0];
if ((self = [super init]) != nil) if ((self = [super init]) != nil)
{ {
_delegate = nil; _delegate = nil;

View file

@ -86,14 +86,19 @@ static Class NSNumberClass;
static Class NSMutableDictionaryClass; static Class NSMutableDictionaryClass;
static Class NSStringClass; static Class NSStringClass;
static NSString *GSPrimaryDomain = @"GSPrimaryDomain";
static NSString *defaultsFile = @".GNUstepDefaults"; static NSString *defaultsFile = @".GNUstepDefaults";
static NSUserDefaults *sharedDefaults = nil; static NSUserDefaults *sharedDefaults = nil;
static NSMutableString *processName = nil; static NSMutableString *processName = nil;
static NSMutableArray *userLanguages = nil;
static BOOL invalidatedLanguages = NO;
static NSRecursiveLock *classLock = nil; static NSRecursiveLock *classLock = nil;
/* Flag to say whether the sharedDefaults variable has been set up by a
* call to the +standardUserDefaults method. If this is YES but the variable
* is nil then there was a problem initialising the shared object and we
* have no defaults available.
*/
static BOOL hasSharedDefaults = NO;
/* /*
* Caching some defaults. * Caching some defaults.
*/ */
@ -173,6 +178,76 @@ writeDictionary(NSDictionary *dict, NSString *file)
return NO; return NO;
} }
static NSMutableArray *
newLanguages(NSArray *oldNames)
{
NSMutableArray *newNames;
NSEnumerator *enumerator;
NSString *language;
NSString *locale = nil;
#ifdef HAVE_LOCALE_H
#ifdef LC_MESSAGES
locale = GSSetLocale(LC_MESSAGES, nil);
#endif
#endif
newNames = [NSMutableArray arrayWithCapacity: 5];
if (oldNames == nil && locale != nil)
{
NSString *locLang = GSLanguageFromLocale(locale);
if (nil != locLang)
{
oldNames = [NSArray arrayWithObject: locLang];
}
#ifdef __MINGW__
if (oldNames == nil)
{
/* Check for language as the first part of the locale string */
NSRange under = [locale rangeOfString: @"_"];
if (under.location)
{
oldNames = [NSArray arrayWithObject:
[locale substringToIndex: under.location]];
}
}
#endif
}
if (oldNames == nil)
{
NSString *env;
env = [[[NSProcessInfo processInfo] environment]
objectForKey: @"LANGUAGES"];
if (env != nil)
{
oldNames = [env componentsSeparatedByString: @";"];
}
}
enumerator = [oldNames objectEnumerator];
while (nil != (language = [enumerator nextObject]))
{
language = [language stringByTrimmingSpaces];
if ([language length] > 0 && NO == [newNames containsObject: language])
{
[newNames addObject: language];
}
}
/* Check if "English" is included. We do this to make sure all the
* required language constants are set somewhere if they aren't set
* in the default language.
*/
if (NO == [newNames containsObject: @"English"])
{
[newNames addObject: @"English"];
}
return newNames;
}
/************************************************************************* /*************************************************************************
*** Local method definitions *** Local method definitions
*************************************************************************/ *************************************************************************/
@ -207,6 +282,12 @@ writeDictionary(NSDictionary *dict, NSString *file)
* looks through the various domains in a particular order. * looks through the various domains in a particular order.
* </p> * </p>
* <deflist> * <deflist>
* <term><code>GSPrimaryDomain</code> ... volatile</term>
* <desc>
* Contains values set at runtime and intended to supercede any values
* set in other domains. This should be used with great care since it
* overrides values which may have been set explicitly by the user.
* </desc>
* <term><code>NSArgumentDomain</code> ... volatile</term> * <term><code>NSArgumentDomain</code> ... volatile</term>
* <desc> * <desc>
* Contains defaults read from the arguments provided * Contains defaults read from the arguments provided
@ -220,8 +301,10 @@ writeDictionary(NSDictionary *dict, NSString *file)
* </desc> * </desc>
* <term>Application (name of the current process) ... persistent</term> * <term>Application (name of the current process) ... persistent</term>
* <desc> * <desc>
* Contains application specific defaults, * Contains application specific defaults, such as window positions.
* such as window positions.</desc> * This is the domain used by the -setObject:forKey: method and is
* the domain normally used when setting preferences for an application.
* </desc>
* <term><code>NSGlobalDomain</code> ... persistent</term> * <term><code>NSGlobalDomain</code> ... persistent</term>
* <desc> * <desc>
* Global defaults applicable to all applications. * Global defaults applicable to all applications.
@ -280,8 +363,6 @@ writeDictionary(NSDictionary *dict, NSString *file)
*/ */
@implementation NSUserDefaults: NSObject @implementation NSUserDefaults: NSObject
static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
+ (void) initialize + (void) initialize
{ {
if (self == [NSUserDefaults class]) if (self == [NSUserDefaults class])
@ -305,12 +386,14 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
+ (void) resetStandardUserDefaults + (void) resetStandardUserDefaults
{ {
NSDictionary *regDefs;
[classLock lock]; [classLock lock];
NS_DURING NS_DURING
{ {
if (sharedDefaults != nil) regDefs = [sharedDefaults volatileDomainForName: @"NSRegistrationDomain"];
if (nil != sharedDefaults)
{ {
NSDictionary *regDefs;
/* To ensure that we don't try to synchronise the old defaults to disk /* To ensure that we don't try to synchronise the old defaults to disk
* after creating the new ones, remove as housekeeping notification * after creating the new ones, remove as housekeeping notification
@ -323,22 +406,9 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
*/ */
[sharedDefaults synchronize]; [sharedDefaults synchronize];
DESTROY(sharedDefaults->_changedDomains); DESTROY(sharedDefaults->_changedDomains);
regDefs = RETAIN([sharedDefaults->_tempDomains
objectForKey: NSRegistrationDomain]);
setSharedDefaults = NO;
DESTROY(sharedDefaults); DESTROY(sharedDefaults);
if (regDefs != nil) }
{ hasSharedDefaults = NO;
[self standardUserDefaults];
if (sharedDefaults != nil)
{
[sharedDefaults->_tempDomains setObject: regDefs
forKey: NSRegistrationDomain];
}
RELEASE(regDefs);
}
}
[classLock unlock]; [classLock unlock];
} }
NS_HANDLER NS_HANDLER
@ -347,6 +417,15 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
[localException raise]; [localException raise];
} }
NS_ENDHANDLER NS_ENDHANDLER
if (nil != regDefs)
{
[self standardUserDefaults];
if (sharedDefaults != nil)
{
[sharedDefaults->_tempDomains setObject: regDefs
forKey: NSRegistrationDomain];
}
}
} }
/* Create a locale dictionary when we have absolutely no information /* Create a locale dictionary when we have absolutely no information
@ -457,109 +536,113 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
+ (NSUserDefaults*) standardUserDefaults + (NSUserDefaults*) standardUserDefaults
{ {
NSUserDefaults *defs;
BOOL added_lang, added_locale; BOOL added_lang, added_locale;
BOOL setup;
id lang; id lang;
NSArray *nL;
NSArray *uL; NSArray *uL;
NSEnumerator *enumerator; NSEnumerator *enumerator;
/* /* If the shared instance is already available ... return it.
* Calling +standardUserDefaults and +userLanguages is horribly interrelated.
* Messing with the order of assignments and calls within these methods can
* break things very easily ... take care.
*
* If +standardUserDefaults is called first, it sets up initial information
* in sharedDefaults then calls +userLanguages to get language information.
* +userLanguages then calls +standardUserDefaults to read NSLanguages
* and returns the language information.
* +standardUserDefaults then sets up language domains and loads localisation
* information before returning.
*
* If +userLanguages is called first, it calls +standardUserDefaults
* to obtain the NSLanguages array.
* +standardUserDefaults loads basic information initialising the
* sharedDefaults variable, then calls back to +userLanguages to set up
* its search list.
* +userLanguages calls +standardUserDefaults again to get NSLanguages.
* +standardUserDefaults returns the partially initialised sharedDefaults.
* +userLanguages uses this to create and return the user languages.
* +standardUserDefaults uses the languages to update the search list
* and load in localisation information then returns sharedDefaults.
* +userLanguages uses this to rebuild language information and return it.
*/ */
/* Return the sharedDefaults without locking in the simple case.
* We need to lock and check again before CREATING sharedDefaults if it doesn't exist,
* so that two threads can't create it at once (or call resetStandardUserDefaults at
* the same time).
* By not locking here, we avoid a deadlock that can occur between classLock and _lock. */
if (setSharedDefaults == YES)
{
IF_NO_GC([sharedDefaults retain];)
return AUTORELEASE(sharedDefaults);
}
[classLock lock]; [classLock lock];
defs = [sharedDefaults retain];
/* setup = hasSharedDefaults;
* NB. The use of the setSharedDefaults flag ensures that a recursive [classLock unlock];
* call to this method is quietly suppressed ... so we get a more if (YES == setup)
* manageable problem.
*/
if (setSharedDefaults == YES)
{ {
IF_NO_GC([sharedDefaults retain];) return [defs autorelease];
[classLock unlock];
return AUTORELEASE(sharedDefaults);
} }
setSharedDefaults = YES;
NS_DURING NS_DURING
{ {
// Create new sharedDefaults (NOTE: Not added to the autorelease pool!) /* Create new NSUserDefaults (NOTE: Not added to the autorelease pool!)
* NB. The following code avoids deadlocks by creating a minimally
* initialised instance, locking that instance, locking the class-wide
* lock, installing the instance as the new shared defaults, unlocking
* the class wide lock, completing the setup of the instance, and then
* unlocking the instance. This means we already have the shared
* instance locked ourselves at the point when it first becomes
* visible to other threads.
*/
#if defined(__MINGW__) #if defined(__MINGW__)
{ {
NSString *path = GSDefaultsRootForUser(NSUserName()); NSString *path = GSDefaultsRootForUser(NSUserName());
NSRange r = [path rangeOfString: @":REGISTRY:"]; NSRange r = [path rangeOfString: @":REGISTRY:"];
if (r.length > 0) if (r.length > 0)
{ {
sharedDefaults = [[NSUserDefaultsWin32 alloc] init]; defs = [[NSUserDefaultsWin32 alloc] init];
} }
else else
{ {
sharedDefaults = [[self alloc] init]; defs = [[self alloc] init];
} }
} }
#else #else
sharedDefaults = [[self alloc] init]; defs = [[self alloc] init];
#endif #endif
if (sharedDefaults == nil)
{ /* Install the new defaults as the shared copy, but lock it so that
NSLog(@"WARNING - unable to create shared user defaults!\n"); * we can complete setup without other threads interfering.
[classLock unlock]; */
NS_VALRETURN(nil); if (nil != defs)
} {
[defs->_lock lock];
[classLock lock];
if (NO == hasSharedDefaults)
{
hasSharedDefaults = YES;
sharedDefaults = [defs retain];
}
else
{
/* Already set up by another thread.
*/
[defs->_lock unlock];
[defs release];
defs = nil;
}
[classLock unlock];
}
if (nil == defs)
{
NSLog(@"WARNING - unable to create shared user defaults!\n");
NS_VALRETURN(nil);
}
/* /*
* Set up search list (excluding language list, which we don't know yet) * Set up search list (excluding language list, which we don't know yet)
*/ */
[sharedDefaults->_searchList addObject: NSArgumentDomain]; [defs->_searchList addObject: GSPrimaryDomain];
[sharedDefaults->_searchList addObject: processName]; [defs->_searchList addObject: NSArgumentDomain];
[sharedDefaults->_searchList addObject: NSGlobalDomain]; [defs->_searchList addObject: processName];
[sharedDefaults->_searchList addObject: GSConfigDomain]; [defs->_searchList addObject: NSGlobalDomain];
[sharedDefaults->_searchList addObject: NSRegistrationDomain]; [defs->_searchList addObject: GSConfigDomain];
[defs->_searchList addObject: NSRegistrationDomain];
/* Load persistent data into the new instance.
*/
[defs synchronize];
/* /*
* Look up user languages list and insert language specific domains * Look up user languages list and insert language specific domains
* into search list before NSRegistrationDomain * into search list before NSRegistrationDomain
*/ */
uL = [self userLanguages]; uL = [defs stringArrayForKey: @"NSLanguages"];
enumerator = [uL objectEnumerator]; nL = newLanguages(uL);
if (NO == [uL isEqual: nL])
{
[self setUserLanguages: nL];
}
enumerator = [nL objectEnumerator];
while ((lang = [enumerator nextObject])) while ((lang = [enumerator nextObject]))
{ {
unsigned index = [sharedDefaults->_searchList count] - 1; unsigned index = [defs->_searchList count] - 1;
[sharedDefaults->_searchList insertObject: lang atIndex: index]; [defs->_searchList insertObject: lang atIndex: index];
} }
/* Set up language constants */ /* Set up language constants */
@ -623,7 +706,7 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
} }
if (dict != nil) if (dict != nil)
{ {
[sharedDefaults setVolatileDomain: dict forName: lang]; [defs setVolatileDomain: dict forName: lang];
added_lang = YES; added_lang = YES;
} }
else if (added_locale == NO) else if (added_locale == NO)
@ -663,8 +746,7 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
dict = GSDomainFromDefaultLocale (); dict = GSDomainFromDefaultLocale ();
if (dict != nil) if (dict != nil)
{ {
[sharedDefaults setVolatileDomain: dict [defs setVolatileDomain: dict forName: lang];
forName: lang];
/* We do not set added_lang to YES here /* We do not set added_lang to YES here
* because we want the basic hardcoded defaults * because we want the basic hardcoded defaults
@ -684,154 +766,49 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
* We need to use hard-coded defaults. * We need to use hard-coded defaults.
*/ */
/* FIXME - should we set this as volatile domain for English ? */ /* FIXME - should we set this as volatile domain for English ? */
[sharedDefaults registerDefaults: [self _unlocalizedDefaults]]; [defs registerDefaults: [self _unlocalizedDefaults]];
} }
IF_NO_GC([sharedDefaults retain];)
updateCache(sharedDefaults); updateCache(sharedDefaults);
[classLock unlock]; [defs->_lock unlock];
} }
NS_HANDLER NS_HANDLER
{ {
[classLock unlock]; [defs->_lock unlock];
[defs release];
[localException raise]; [localException raise];
} }
NS_ENDHANDLER NS_ENDHANDLER
return AUTORELEASE(sharedDefaults); return [defs autorelease];
} }
+ (NSArray*) userLanguages + (NSArray*) userLanguages
{ {
NSArray *result = nil; return [[NSUserDefaults standardUserDefaults]
stringArrayForKey: @"NSLanguages"];
/*
* Calling +standardUserDefaults and +userLanguages is horribly interrelated.
* Messing with the order of assignments and calls within these methods can
* break things very easily ... take care.
*
* If +standardUserDefaults is called first, it sets up initial information
* in sharedDefaults then calls +userLanguages to get language information.
* +userLanguages then calls +standardUserDefaults to read NSLanguages
* and returns the language information.
* +standardUserDefaults then sets up language domains and loads localisation
* information before returning.
*
* If +userLanguages is called first, it calls +standardUserDefaults
* to obtain the NSLanguages array.
* +standardUserDefaults loads basic information initialising the
* sharedDefaults variable, then calls back to +userLanguages to set up
* its search list.
* +userLanguages calls +standardUserDefaults again to get NSLanguages.
* +standardUserDefaults returns the partially initialised sharedDefaults.
* +userLanguages uses this to create and return the user languages.
* +standardUserDefaults uses the languages to update the search list
* and load in localisation information then returns sharedDefaults.
* +userLanguages uses this to rebuild language information and return it.
*/
[classLock lock];
NS_DURING
{
if (invalidatedLanguages == YES)
{
invalidatedLanguages = NO;
DESTROY(userLanguages);
}
if (userLanguages == nil)
{
NSArray *currLang = nil;
NSString *locale = nil;
#ifdef HAVE_LOCALE_H
#ifdef LC_MESSAGES
locale = GSSetLocale(LC_MESSAGES, nil);
#endif
#endif
currLang = [[NSUserDefaults standardUserDefaults]
stringArrayForKey: @"NSLanguages"];
userLanguages = [[NSMutableArray alloc] initWithCapacity: 5];
if (currLang == nil && locale != nil && GSLanguageFromLocale(locale))
{
currLang = [NSArray arrayWithObject: GSLanguageFromLocale(locale)];
}
#ifdef __MINGW__
if (currLang == nil && locale != nil)
{
/* Check for language as the first part of the locale string */
NSRange under = [locale rangeOfString: @"_"];
if (under.location)
currLang = [NSArray arrayWithObject:
[locale substringToIndex: under.location]];
}
#endif
if (currLang == nil)
{
NSString *env;
env = [[[NSProcessInfo processInfo] environment]
objectForKey: @"LANGUAGES"];
if (env != nil)
{
currLang = [env componentsSeparatedByString: @";"];
}
}
if (currLang != nil)
{
NSMutableArray *a = [currLang mutableCopy];
unsigned c = [a count];
while (c-- > 0)
{
NSString *s = [[a objectAtIndex: c] stringByTrimmingSpaces];
if ([s length] == 0)
{
[a removeObjectAtIndex: c];
}
else
{
[a replaceObjectAtIndex: c withObject: s];
}
}
[userLanguages addObjectsFromArray: a];
RELEASE(a);
}
/* Check if "English" is included. We do this to make sure all the
required language constants are set somewhere if they aren't set
in the default language */
if ([userLanguages containsObject: @"English"] == NO)
{
[userLanguages addObject: @"English"];
}
}
result = RETAIN(userLanguages);
[classLock unlock];
}
NS_HANDLER
{
[classLock unlock];
[localException raise];
}
NS_ENDHANDLER
return AUTORELEASE(result);
} }
+ (void) setUserLanguages: (NSArray*)languages + (void) setUserLanguages: (NSArray*)languages
{ {
NSMutableDictionary *globDict; NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *dict;
globDict = [[[self standardUserDefaults] dict = [[defs volatileDomainForName: GSPrimaryDomain] mutableCopy];
persistentDomainForName: NSGlobalDomain] mutableCopy];
if (languages == nil) // Remove the entry if (languages == nil) // Remove the entry
[globDict removeObjectForKey: @"NSLanguages"]; {
[dict removeObjectForKey: @"NSLanguages"];
}
else else
[globDict setObject: languages forKey: @"NSLanguages"]; {
[[self standardUserDefaults] if (nil == dict)
setPersistentDomain: globDict forName: NSGlobalDomain]; {
RELEASE(globDict); dict = [NSMutableDictionary new];
}
languages = newLanguages(languages);
[dict setObject: languages forKey: @"NSLanguages"];
}
[defs removeVolatileDomainForName: GSPrimaryDomain];
[defs setVolatileDomain: dict forName: GSPrimaryDomain];
[dict release];
} }
- (id) init - (id) init
@ -918,27 +895,6 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
{ {
// Initialize _persDomains from the archived user defaults (persistent) // Initialize _persDomains from the archived user defaults (persistent)
_persDomains = [[NSMutableDictionaryClass alloc] initWithCapacity: 10]; _persDomains = [[NSMutableDictionaryClass alloc] initWithCapacity: 10];
if ([self synchronize] == NO)
{
DESTROY(self);
return self;
}
}
// Check and if not existent add the Application and the Global domains
if ([_persDomains objectForKey: processName] == nil)
{
[_persDomains
setObject: [NSMutableDictionaryClass dictionaryWithCapacity: 10]
forKey: processName];
[self __changePersistentDomain: processName];
}
if ([_persDomains objectForKey: NSGlobalDomain] == nil)
{
[_persDomains
setObject: [NSMutableDictionaryClass dictionaryWithCapacity: 10]
forKey: NSGlobalDomain];
[self __changePersistentDomain: NSGlobalDomain];
} }
// Create volatile defaults and add the Argument and the Registration domains // Create volatile defaults and add the Argument and the Registration domains
@ -1007,7 +963,6 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
NS_DURING NS_DURING
{ {
DESTROY(_dictionaryRep); DESTROY(_dictionaryRep);
if (self == sharedDefaults) invalidatedLanguages = YES;
[_searchList removeObject: aName]; [_searchList removeObject: aName];
index = [_searchList indexOfObject: processName]; index = [_searchList indexOfObject: processName];
index = (index == NSNotFound) ? 0 : (index + 1); index = (index == NSNotFound) ? 0 : (index + 1);
@ -1357,7 +1312,6 @@ static BOOL isPlistObject(id o)
NS_DURING NS_DURING
{ {
DESTROY(_dictionaryRep); DESTROY(_dictionaryRep);
if (self == sharedDefaults) invalidatedLanguages = YES;
RELEASE(_searchList); RELEASE(_searchList);
_searchList = [newList mutableCopy]; _searchList = [newList mutableCopy];
[_lock unlock]; [_lock unlock];
@ -1705,107 +1659,118 @@ NSLog(@"Creating empty user defaults database");
- (BOOL) synchronize - (BOOL) synchronize
{ {
NSMutableDictionary *newDict; NSMutableDictionary *newDict;
NSDate *saved;
BOOL wasLocked; BOOL wasLocked;
BOOL result = YES;
[_lock lock]; [_lock lock];
saved = _lastSync;
_lastSync = [NSDate new]; // Record timestamp of this sync.
NS_DURING NS_DURING
{ {
/* /*
* If we haven't changed anything, we only need to synchronise if * If we haven't changed anything, we only need to synchronise if
* the on-disk database has been changed by someone else. * the on-disk database has been changed by someone else.
*/ */
if (_changedDomains != nil
if (_changedDomains == nil) || YES == [self wantToReadDefaultsSince: saved])
{ {
if ([self wantToReadDefaultsSince: _lastSync] == NO) DESTROY(_dictionaryRep);
if ([self lockDefaultsFile: &wasLocked] == NO)
{ {
[_lock unlock]; result = NO;
NS_VALRETURN(YES);
} }
} else if (nil == (newDict = [self readDefaults]))
{
if (wasLocked == NO)
{
[self unlockDefaultsFile];
}
result = NO;
}
else if (_changedDomains != nil)
{ // Synchronize both dictionaries
NSEnumerator *enumerator;
NSString *domainName;
NSDictionary *domain;
NSDictionary *oldData = AUTORELEASE([newDict copy]);
DESTROY(_dictionaryRep); enumerator = [_changedDomains objectEnumerator];
if (self == sharedDefaults) DESTROY(_changedDomains); // Retained by enumerator.
{ while ((domainName = [enumerator nextObject]) != nil)
invalidatedLanguages = YES; {
} domain = [_persDomains objectForKey: domainName];
if (domain != nil) // Domain was added or changed
if ([self lockDefaultsFile: &wasLocked] == NO) {
{ [newDict setObject: domain forKey: domainName];
[_lock unlock]; }
NS_VALRETURN(NO); else // Domain was removed
} {
[newDict removeObjectForKey: domainName];
newDict = [self readDefaults]; }
}
if (newDict == nil) ASSIGN(_persDomains, newDict);
{ if ([self writeDefaults: _persDomains oldData: oldData] == NO)
if (wasLocked == NO) {
if (wasLocked == NO)
{
[self unlockDefaultsFile];
}
result = NO;
}
}
else
{
if ([_persDomains isEqual: newDict] == NO)
{
ASSIGN(_persDomains, newDict);
updateCache(self);
[[NSNotificationCenter defaultCenter]
postNotificationName: NSUserDefaultsDidChangeNotification
object: self];
}
}
if (wasLocked == NO)
{ {
[self unlockDefaultsFile]; [self unlockDefaultsFile];
} }
[_lock unlock]; }
NS_VALRETURN(NO);
}
if (_changedDomains != nil)
{ // Synchronize both dictionaries
NSEnumerator *enumerator = [_changedDomains objectEnumerator];
NSString *domainName;
NSDictionary *domain;
NSDictionary *oldData = AUTORELEASE([newDict copy]);
DESTROY(_changedDomains); // Retained by enumerator.
while ((domainName = [enumerator nextObject]) != nil)
{
domain = [_persDomains objectForKey: domainName];
if (domain != nil) // Domain was added or changed
{
[newDict setObject: domain forKey: domainName];
}
else // Domain was removed
{
[newDict removeObjectForKey: domainName];
}
}
ASSIGN(_persDomains, newDict);
if ([self writeDefaults: _persDomains oldData: oldData] == NO)
{
if (wasLocked == NO)
{
[self unlockDefaultsFile];
}
[_lock unlock];
NS_VALRETURN(NO);
}
ASSIGN(_lastSync, [NSDateClass date]);
}
else
{
ASSIGN(_lastSync, [NSDateClass date]);
if ([_persDomains isEqual: newDict] == NO)
{
ASSIGN(_persDomains, newDict);
updateCache(self);
[[NSNotificationCenter defaultCenter]
postNotificationName: NSUserDefaultsDidChangeNotification
object: self];
}
}
if (wasLocked == NO)
{
[self unlockDefaultsFile];
}
[_lock unlock];
} }
NS_HANDLER NS_HANDLER
{ {
[_lastSync release];
_lastSync = saved;
[_lock unlock]; [_lock unlock];
[localException raise]; [localException raise];
} }
NS_ENDHANDLER NS_ENDHANDLER
return YES;
if (YES == result)
{
[saved release];
}
else
{
[_lastSync release];
_lastSync = saved;
}
// Check and if not existent add the Application and the Global domains
if ([_persDomains objectForKey: processName] == nil)
{
[_persDomains
setObject: [NSMutableDictionaryClass dictionaryWithCapacity: 10]
forKey: processName];
[self __changePersistentDomain: processName];
}
if ([_persDomains objectForKey: NSGlobalDomain] == nil)
{
[_persDomains
setObject: [NSMutableDictionaryClass dictionaryWithCapacity: 10]
forKey: NSGlobalDomain];
[self __changePersistentDomain: NSGlobalDomain];
}
[_lock unlock];
return result;
} }
@ -1815,7 +1780,6 @@ NSLog(@"Creating empty user defaults database");
NS_DURING NS_DURING
{ {
DESTROY(_dictionaryRep); DESTROY(_dictionaryRep);
if (self == sharedDefaults) invalidatedLanguages = YES;
[_tempDomains removeObjectForKey: domainName]; [_tempDomains removeObjectForKey: domainName];
[_lock unlock]; [_lock unlock];
} }
@ -1849,7 +1813,6 @@ NSLog(@"Creating empty user defaults database");
} }
DESTROY(_dictionaryRep); DESTROY(_dictionaryRep);
if (self == sharedDefaults) invalidatedLanguages = YES;
domain = [domain mutableCopy]; domain = [domain mutableCopy];
[_tempDomains setObject: domain forKey: domainName]; [_tempDomains setObject: domain forKey: domainName];
RELEASE(domain); RELEASE(domain);
@ -1968,7 +1931,6 @@ NSLog(@"Creating empty user defaults database");
[_tempDomains setObject: regDefs forKey: NSRegistrationDomain]; [_tempDomains setObject: regDefs forKey: NSRegistrationDomain];
} }
DESTROY(_dictionaryRep); DESTROY(_dictionaryRep);
if (self == sharedDefaults) invalidatedLanguages = YES;
[regDefs addEntriesFromDictionary: newVals]; [regDefs addEntriesFromDictionary: newVals];
[_lock unlock]; [_lock unlock];
} }
@ -1991,7 +1953,6 @@ NSLog(@"Creating empty user defaults database");
NS_DURING NS_DURING
{ {
DESTROY(_dictionaryRep); DESTROY(_dictionaryRep);
if (self == sharedDefaults) invalidatedLanguages = YES;
[_searchList removeObject: aName]; [_searchList removeObject: aName];
[_lock unlock]; [_lock unlock];
} }
@ -2111,7 +2072,6 @@ NSLog(@"Creating empty user defaults database");
NS_DURING NS_DURING
{ {
DESTROY(_dictionaryRep); DESTROY(_dictionaryRep);
if (self == sharedDefaults) invalidatedLanguages = YES;
if (_changedDomains == nil) if (_changedDomains == nil)
{ {
_changedDomains = [[NSMutableArray alloc] initWithObjects: &domainName _changedDomains = [[NSMutableArray alloc] initWithObjects: &domainName