![]() |
API 0.9.5
|
00001 /* 00002 * CPUserDefaults.j 00003 * Foundation 00004 * 00005 * Created by Nicholas Small. 00006 * Copyright 2010, 280 North, Inc. 00007 * 00008 * This library is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Lesser General Public 00010 * License as published by the Free Software Foundation; either 00011 * version 2.1 of the License, or (at your option) any later version. 00012 * 00013 * This library is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Lesser General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Lesser General Public 00019 * License along with this library; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00021 */ 00022 00023 00024 00025 CPArgumentDomain = @"CPArgumentDomain"; 00026 CPApplicationDomain = [[[CPBundle mainBundle] infoDictionary] objectForKey:@"CPBundleIdentifier"] || @"CPApplicationDomain"; 00027 CPGlobalDomain = @"CPGlobalDomain"; 00028 CPLocaleDomain = @"CPLocaleDomain"; 00029 CPRegistrationDomain = @"CPRegistrationDomain"; 00030 00031 CPUserDefaultsDidChangeNotification = @"CPUserDefaultsDidChangeNotification"; 00032 00033 var StandardUserDefaults; 00034 00051 @implementation CPUserDefaults : CPObject 00052 { 00053 CPDictionary _domains; 00054 CPDictionary _stores; 00055 00056 CPDictionary _searchList; 00057 BOOL _searchListNeedsReload; 00058 } 00059 00063 + (id)standardUserDefaults 00064 { 00065 if (!StandardUserDefaults) 00066 StandardUserDefaults = [[CPUserDefaults alloc] init]; 00067 00068 return StandardUserDefaults; 00069 } 00070 00075 + (void)resetStandardUserDefaults 00076 { 00077 if (StandardUserDefaults) 00078 [StandardUserDefaults synchronize]; 00079 00080 StandardUserDefaults = nil; 00081 } 00082 00083 /* 00084 @ignore 00085 */ 00086 - (id)init 00087 { 00088 self = [super init]; 00089 00090 if (self) 00091 { 00092 _domains = [CPDictionary dictionary]; 00093 [self _setupArgumentsDomain]; 00094 00095 var defaultStore = [CPUserDefaultsLocalStore supportsLocalStorage] ? CPUserDefaultsLocalStore : CPUserDefaultsCookieStore; 00096 00097 _stores = [CPDictionary dictionary]; 00098 [self setPersistentStoreClass:defaultStore forDomain:CPGlobalDomain reloadData:YES]; 00099 [self setPersistentStoreClass:defaultStore forDomain:CPApplicationDomain reloadData:YES]; 00100 } 00101 00102 return self; 00103 } 00104 00105 /* 00106 @ignore 00107 */ 00108 - (void)_setupArgumentsDomain 00109 { 00110 var args = [CPApp namedArguments], 00111 keys = [args allKeys], 00112 count = [keys count], 00113 i = 0; 00114 00115 for (; i < count; i++) 00116 { 00117 var key = keys[i]; 00118 [self setObject:[args objectForKey:key] forKey:key inDomain:CPArgumentDomain]; 00119 } 00120 } 00121 00126 - (id)objectForKey:(CPString)aKey 00127 { 00128 if (_searchListNeedsReload) 00129 [self _reloadSearchList]; 00130 00131 return [_searchList objectForKey:aKey]; 00132 } 00133 00137 - (void)setObject:(id)anObject forKey:(CPString)aKey 00138 { 00139 [self setObject:anObject forKey:aKey inDomain:CPApplicationDomain]; 00140 } 00141 00147 - (id)objectForKey:(CPString)aKey inDomain:(CPString)aDomain 00148 { 00149 var domain = [_domains objectForKey:aDomain]; 00150 00151 if (!domain) 00152 return nil; 00153 00154 return [domain objectForKey:aKey]; 00155 } 00156 00161 - (void)setObject:(id)anObject forKey:(CPString)aKey inDomain:(CPString)aDomain 00162 { 00163 if (!aKey || !aDomain) 00164 return; 00165 00166 var domain = [_domains objectForKey:aDomain]; 00167 if (!domain) 00168 { 00169 domain = [CPDictionary dictionary]; 00170 [_domains setObject:domain forKey:aDomain]; 00171 } 00172 00173 [domain setObject:anObject forKey:aKey]; 00174 _searchListNeedsReload = YES; 00175 [self domainDidChange:aDomain]; 00176 } 00177 00182 - (void)removeObjectForKey:(CPString)aKey 00183 { 00184 [self removeObjectForKey:aKey inDomain:CPApplicationDomain]; 00185 } 00186 00190 - (void)removeObjectForKey:(CPString)aKey inDomain:(CPString)aDomain 00191 { 00192 if (!aKey || !aDomain) 00193 return; 00194 00195 var domain = [_domains objectForKey:aDomain]; 00196 if (!domain) 00197 return; 00198 00199 [domain removeObjectForKey:aKey]; 00200 _searchListNeedsReload = YES; 00201 [self domainDidChange:aDomain]; 00202 } 00203 00212 - (void)registerDefaults:(CPDictionary)aDictionary 00213 { 00214 var keys = [aDictionary allKeys], 00215 count = [keys count], 00216 i = 0; 00217 00218 for (; i < count; i++) 00219 { 00220 var key = keys[i]; 00221 [self setObject:[aDictionary objectForKey:key] forKey:key inDomain:CPRegistrationDomain]; 00222 } 00223 } 00224 00231 - (void)registerDefaultsFromContentsOfFile:(CPURL)aURL 00232 { 00233 var contents = [CPURLConnection sendSynchronousRequest:[CPURLRequest requestWithURL:aURL] returningResponse:nil], 00234 data = [CPData dataWithRawString:[contents rawString]], 00235 plist = [data plistObject]; 00236 00237 [self registerDefaults:plist]; 00238 } 00239 00240 /* 00241 @ignore 00242 */ 00243 - (void)_reloadSearchList 00244 { 00245 _searchListNeedsReload = NO; 00246 00247 var dicts = [CPRegistrationDomain, CPGlobalDomain, CPApplicationDomain, CPArgumentDomain], 00248 count = [dicts count], 00249 i = 0; 00250 00251 _searchList = [CPDictionary dictionary]; 00252 00253 for (; i < count; i++) 00254 { 00255 var domain = [_domains objectForKey:dicts[i]]; 00256 if (!domain) 00257 continue; 00258 00259 var keys = [domain allKeys], 00260 keysCount = [keys count], 00261 j = 0; 00262 00263 for (; j < keysCount; j++) 00264 { 00265 var key = keys[j]; 00266 [_searchList setObject:[domain objectForKey:key] forKey:key]; 00267 } 00268 } 00269 } 00270 00271 // Synchronization 00272 00276 - (CPArray)volatileDomainNames 00277 { 00278 return [CPArgumentDomain, CPLocaleDomain, CPRegistrationDomain]; 00279 } 00280 00284 - (CPArray)persistentDomainNames 00285 { 00286 return [CPGlobalDomain, CPApplicationDomain]; 00287 } 00288 00292 - (CPUserDefaultsStore)persistentStoreForDomain:(CPString)aDomain 00293 { 00294 return [_stores objectForKey:aDomain]; 00295 } 00296 00305 - (CPUserDefaultsStore)setPersistentStoreClass:(Class)aStoreClass forDomain:(CPString)aDomain reloadData:(BOOL)aFlag 00306 { 00307 var currentStore = [_stores objectForKey:aDomain]; 00308 if (currentStore && [currentStore class] === aStoreClass) 00309 return currentStore; 00310 00311 var store = [[aStoreClass alloc] init]; 00312 [store setDomain:aDomain]; 00313 [_stores setObject:store forKey:aDomain]; 00314 00315 if (aFlag) 00316 [self reloadDataFromStoreForDomain:aDomain]; 00317 00318 return store; 00319 } 00320 00324 - (void)reloadDataFromStoreForDomain:(CPString)aDomain 00325 { 00326 var data = [[self persistentStoreForDomain:aDomain] data], 00327 domain = data ? [CPKeyedUnarchiver unarchiveObjectWithData:data] : nil; 00328 00329 [_domains setObject:domain forKey:aDomain]; 00330 00331 _searchListNeedsReload = YES; 00332 } 00333 00337 - (void)domainDidChange:(CPString)aDomain 00338 { 00339 if (aDomain === CPGlobalDomain || aDomain === CPApplicationDomain) 00340 [[CPRunLoop currentRunLoop] performSelector:@selector(synchronize) target:self argument:nil order:0 modes:[CPDefaultRunLoopMode]]; 00341 00342 [[CPNotificationCenter defaultCenter] postNotificationName:CPUserDefaultsDidChangeNotification object:self]; 00343 } 00344 00348 - (void)synchronize 00349 { 00350 var globalDomain = [_domains objectForKey:CPGlobalDomain]; 00351 if (globalDomain) 00352 { 00353 var data = [CPKeyedArchiver archivedDataWithRootObject:globalDomain]; 00354 [[self persistentStoreForDomain:CPGlobalDomain] setData:data]; 00355 } 00356 00357 var appDomain = [_domains objectForKey:CPApplicationDomain]; 00358 if (appDomain) 00359 { 00360 var data = [CPKeyedArchiver archivedDataWithRootObject:appDomain]; 00361 [[self persistentStoreForDomain:CPApplicationDomain] setData:data]; 00362 } 00363 } 00364 00365 #pragma mark Getting Default Values 00366 00370 - (CPArray)arrayForKey:(CPString)aKey 00371 { 00372 var value = [self objectForKey:aKey]; 00373 if ([value isKindOfClass:CPArray]) 00374 return value; 00375 00376 return nil; 00377 } 00378 00382 - (BOOL)boolForKey:(CPString)aKey 00383 { 00384 var value = [self objectForKey:aKey]; 00385 if ([value respondsToSelector:@selector(boolValue)]) 00386 return [value boolValue]; 00387 00388 return NO; 00389 } 00390 00391 00395 - (CPData)dataForKey:(CPString)aKey 00396 { 00397 var value = [self objectForKey:aKey]; 00398 if ([value isKindOfClass:CPData]) 00399 return value; 00400 00401 return nil; 00402 } 00403 00407 - (CPDictionary)dictionaryForKey:(CPString)aKey 00408 { 00409 var value = [self objectForKey:aKey]; 00410 if ([value isKindOfClass:CPDictionary]) 00411 return value; 00412 00413 return nil; 00414 } 00415 00419 - (float)floatForKey:(CPString)aKey 00420 { 00421 var value = [self objectForKey:aKey]; 00422 if (value === nil) 00423 return 0; 00424 00425 if ([value respondsToSelector:@selector(floatValue)]) 00426 value = [value floatValue]; 00427 00428 return parseFloat(value); 00429 } 00430 00434 - (int)integerForKey:(CPString)aKey 00435 { 00436 var value = [self objectForKey:aKey]; 00437 if (value === nil) 00438 return 0; 00439 00440 if ([value respondsToSelector:@selector(intValue)]) 00441 value = [value intValue]; 00442 00443 return parseInt(value); 00444 } 00445 00449 - (double)doubleForKey:(CPString)aKey 00450 { 00451 return [self floatForKey:aKey]; 00452 } 00453 00457 - (CPString)stringForKey:(CPString)aKey 00458 { 00459 var value = [self objectForKey:aKey]; 00460 00461 if ([value isKindOfClass:CPString]) 00462 return value; 00463 00464 else if ([value respondsToSelector:@selector(stringValue)]) 00465 return [value stringValue]; 00466 00467 return nil; 00468 } 00469 00473 - (CPArray)stringArrayForKey:(CPString)aKey 00474 { 00475 var value = [self objectForKey:aKey]; 00476 if (![value isKindOfClass:CPArray]) 00477 return nil; 00478 00479 for (var i = 0, count = [value count]; i < count; i++) 00480 if (![value[i] isKindOfClass:CPString]) 00481 return nil; 00482 00483 return value; 00484 } 00485 00489 - (CPURL)URLForKey:(CPString)aKey 00490 { 00491 var value = [self objectForKey:aKey]; 00492 if ([value isKindOfClass:CPURL]) 00493 return value; 00494 00495 if ([value isKindOfClass:CPString]) 00496 return [CPURL URLWithString:value]; 00497 00498 return nil; 00499 } 00500 00501 #pragma mark Setting Default Values 00502 00507 - (void)setBool:(BOOL)aValue forKey:(CPString)aKey 00508 { 00509 if ([aValue respondsToSelector:@selector(boolValue)]) 00510 [self setObject:[aValue boolValue] forKey:aKey]; 00511 } 00512 00517 - (void)setFloat:(float)aValue forKey:(CPString)aKey 00518 { 00519 if ([aValue respondsToSelector:@selector(aValue)]) 00520 aValue = [aValue floatValue]; 00521 00522 [self setObject:parseFloat(aValue) forKey:aKey]; 00523 } 00524 00528 - (void)setDouble:(double)aValue forKey:(CPString)aKey 00529 { 00530 [self setFloat:aValue forKey:aKey]; 00531 } 00532 00537 - (void)setInteger:(int)aValue forKey:(CPString)aKey 00538 { 00539 if ([aValue respondsToSelector:@selector(intValue)]) 00540 aValue = [aValue intValue]; 00541 00542 [self setObject:parseInt(aValue) forKey:aKey]; 00543 } 00544 00549 - (void)setURL:(CPURL)aValue forKey:(CPString)aKey 00550 { 00551 if ([aValue isKindOfClass:CPString]) 00552 aValue = [CPURL URLWithString:aValue]; 00553 00554 [self setObject:aValue forKey:aKey]; 00555 } 00556 00557 @end 00558 00559 @implementation CPUserDefaultsStore : CPObject 00560 { 00561 CPString _domain; 00562 } 00563 00564 - (CPData)data 00565 { 00566 _CPRaiseInvalidAbstractInvocation(self, _cmd); 00567 return nil; 00568 } 00569 00570 - (void)setData:(CPData)aData 00571 { 00572 _CPRaiseInvalidAbstractInvocation(self, _cmd); 00573 } 00574 00575 @end 00576 00577 @implementation CPUserDefaultsCookieStore : CPUserDefaultsStore 00578 { 00579 CPCookie _cookie; 00580 } 00581 00582 - (void)setDomain:(CPString)aDomain 00583 { 00584 if (_domain === aDomain) 00585 return; 00586 00587 _domain = aDomain; 00588 00589 _cookie = [[CPCookie alloc] initWithName:_domain]; 00590 } 00591 00592 - (CPData)data 00593 { 00594 var result = [_cookie value]; 00595 if (!result || [result length] < 1) 00596 return nil; 00597 00598 return [CPData dataWithRawString:decodeURIComponent(result)]; 00599 } 00600 00601 - (void)setData:(CPData)aData 00602 { 00603 [_cookie setValue:encodeURIComponent([aData rawString]) expires:[CPDate distantFuture] domain:window.location.href.hostname]; 00604 } 00605 00606 @end 00607 00608 var CPUserDefaultsLocalStoreTestKey = "9961800812587769-Cappuccino-Storage-Test"; 00609 @implementation CPUserDefaultsLocalStore : CPUserDefaultsStore 00610 { 00611 id __doxygen__; 00612 } 00613 00614 + (BOOL)supportsLocalStorage 00615 { 00616 if (!window.localStorage) 00617 return NO; 00618 00619 try 00620 { 00621 // Just because localStorage exists does not mean it works. In particular it might be disabled 00622 // as it is when Safari's private browsing mode is active. 00623 localStorage.setItem(CPUserDefaultsLocalStoreTestKey, "1"); 00624 if (localStorage.getItem(CPUserDefaultsLocalStoreTestKey) != "1") 00625 return NO; 00626 localStorage.removeItem(CPUserDefaultsLocalStoreTestKey); 00627 } 00628 catch (e) 00629 { 00630 return NO; 00631 } 00632 return YES; 00633 } 00634 00635 - (id)init 00636 { 00637 if (![[self class] supportsLocalStorage]) 00638 { 00639 [CPException raise:@"UnsupportedFeature" reason:@"Browser does not support localStorage for CPUserDefaultsLocalStore"]; 00640 return self = nil; 00641 } 00642 00643 return self = [super init]; 00644 } 00645 00646 - (CPData)data 00647 { 00648 var result = localStorage.getItem(_domain); 00649 if (!result || [result length] < 1) 00650 return nil; 00651 00652 return [CPData dataWithRawString:decodeURIComponent(result)]; 00653 } 00654 00655 - (void)setData:(CPData)aData 00656 { 00657 try 00658 { 00659 localStorage.setItem(_domain, encodeURIComponent([aData rawString])); 00660 } 00661 catch (e) 00662 { 00663 CPLog.warn("Unable to write to local storage: " + e); 00664 } 00665 } 00666 00667 @end 00668 00669 @implementation CPUserDefaultsStore (CPSynthesizedAccessors) 00670 00674 - (CPString)domain 00675 { 00676 return _domain; 00677 } 00678 00682 - (void)setDomain:(CPString)aValue 00683 { 00684 _domain = aValue; 00685 } 00686 00687 @end