API  0.9.10
CPUserDefaults.j
Go to the documentation of this file.
1 /*
2  * CPUserDefaults.j
3  * Foundation
4  *
5  * Created by Nicholas Small.
6  * Copyright 2010, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 
25 @global CPApp
26 
27 CPArgumentDomain = @"CPArgumentDomain";
28 CPApplicationDomain = [[[CPBundle mainBundle] infoDictionary] objectForKey:@"CPBundleIdentifier"] || @"CPApplicationDomain";
29 CPGlobalDomain = @"CPGlobalDomain";
30 CPLocaleDomain = @"CPLocaleDomain";
31 CPRegistrationDomain = @"CPRegistrationDomain";
32 
33 CPUserDefaultsDidChangeNotification = @"CPUserDefaultsDidChangeNotification";
34 
36 
52 @implementation CPUserDefaults : CPObject
53 {
54  CPDictionary _domains;
55  CPDictionary _stores;
56 
57  CPDictionary _searchList;
58  BOOL _searchListNeedsReload;
59 }
60 
65 {
68 
69  return StandardUserDefaults;
70 }
71 
77 {
79  [StandardUserDefaults synchronize];
80 
82 }
83 
84 /*
85  @ignore
86 */
87 - (id)init
88 {
89  self = [super init];
90 
91  if (self)
92  {
93  _domains = @{};
94  [self _setupArgumentsDomain];
95 
97 
98  _stores = @{};
99  [self setPersistentStoreClass:defaultStore forDomain:CPGlobalDomain reloadData:YES];
100  [self setPersistentStoreClass:defaultStore forDomain:CPApplicationDomain reloadData:YES];
101  }
102 
103  return self;
104 }
105 
106 /*
107  @ignore
108 */
109 - (void)_setupArgumentsDomain
110 {
111  var args = [CPApp namedArguments],
112  keys = [args allKeys],
113  count = [keys count],
114  i = 0;
115 
116  for (; i < count; i++)
117  {
118  var key = keys[i];
119  [self setObject:[args objectForKey:key] forKey:key inDomain:CPArgumentDomain];
120  }
121 }
122 
127 - (id)objectForKey:(CPString)aKey
128 {
129  if (_searchListNeedsReload)
130  [self _reloadSearchList];
131 
132  return [_searchList objectForKey:aKey];
133 }
134 
138 - (void)setObject:(id)anObject forKey:(CPString)aKey
139 {
140  [self setObject:anObject forKey:aKey inDomain:CPApplicationDomain];
141 }
142 
148 - (id)objectForKey:(CPString)aKey inDomain:(CPString)aDomain
149 {
150  var domain = [_domains objectForKey:aDomain];
151 
152  if (!domain)
153  return nil;
154 
155  return [domain objectForKey:aKey];
156 }
157 
162 - (void)setObject:(id)anObject forKey:(CPString)aKey inDomain:(CPString)aDomain
163 {
164  if (!aKey || !aDomain)
165  return;
166 
167  var domain = [_domains objectForKey:aDomain];
168  if (!domain)
169  {
170  domain = @{};
171  [_domains setObject:domain forKey:aDomain];
172  }
173 
174  [domain setObject:anObject forKey:aKey];
175  _searchListNeedsReload = YES;
176  [self domainDidChange:aDomain];
177 }
178 
183 - (void)removeObjectForKey:(CPString)aKey
184 {
185  [self removeObjectForKey:aKey inDomain:CPApplicationDomain];
186 }
187 
191 - (void)removeObjectForKey:(CPString)aKey inDomain:(CPString)aDomain
192 {
193  if (!aKey || !aDomain)
194  return;
195 
196  var domain = [_domains objectForKey:aDomain];
197  if (!domain)
198  return;
199 
200  [domain removeObjectForKey:aKey];
201  _searchListNeedsReload = YES;
202  [self domainDidChange:aDomain];
203 }
204 
213 - (void)registerDefaults:(CPDictionary)aDictionary
214 {
215  var keys = [aDictionary allKeys],
216  count = [keys count],
217  i = 0;
218 
219  for (; i < count; i++)
220  {
221  var key = keys[i];
222  [self setObject:[aDictionary objectForKey:key] forKey:key inDomain:CPRegistrationDomain];
223  }
224 }
225 
232 - (void)registerDefaultsFromContentsOfFile:(CPURL)aURL
233 {
235  data = [CPData dataWithRawString:[contents rawString]],
236  plist = [data plistObject];
237 
238  [self registerDefaults:plist];
239 }
240 
241 /*
242  @ignore
243 */
244 - (void)_reloadSearchList
245 {
246  _searchListNeedsReload = NO;
247 
248  var dicts = [CPRegistrationDomain, CPGlobalDomain, CPApplicationDomain, CPArgumentDomain],
249  count = [dicts count],
250  i = 0;
251 
252  _searchList = @{};
253 
254  for (; i < count; i++)
255  {
256  var domain = [_domains objectForKey:dicts[i]];
257  if (!domain)
258  continue;
259 
260  var keys = [domain allKeys],
261  keysCount = [keys count],
262  j = 0;
263 
264  for (; j < keysCount; j++)
265  {
266  var key = keys[j];
267  [_searchList setObject:[domain objectForKey:key] forKey:key];
268  }
269  }
270 }
271 
272 // Synchronization
273 
278 {
279  return [CPArgumentDomain, CPLocaleDomain, CPRegistrationDomain];
280 }
281 
286 {
287  return [CPGlobalDomain, CPApplicationDomain];
288 }
289 
293 - (CPUserDefaultsStore)persistentStoreForDomain:(CPString)aDomain
294 {
295  return [_stores objectForKey:aDomain];
296 }
297 
306 - (CPUserDefaultsStore)setPersistentStoreClass:(Class)aStoreClass forDomain:(CPString)aDomain reloadData:(BOOL)aFlag
307 {
308  var currentStore = [_stores objectForKey:aDomain];
309  if (currentStore && [currentStore class] === aStoreClass)
310  return currentStore;
311 
312  var store = [[aStoreClass alloc] init];
313  [store setDomain:aDomain];
314  [_stores setObject:store forKey:aDomain];
315 
316  if (aFlag)
317  [self reloadDataFromStoreForDomain:aDomain];
318 
319  return store;
320 }
321 
325 - (void)reloadDataFromStoreForDomain:(CPString)aDomain
326 {
327  var data = [[self persistentStoreForDomain:aDomain] data],
328  domain = data ? [CPKeyedUnarchiver unarchiveObjectWithData:data] : nil;
329 
330  if (domain === nil)
331  [_domains removeObjectForKey:aDomain];
332  else
333  [_domains setObject:domain forKey:aDomain];
334 
335  _searchListNeedsReload = YES;
336 }
337 
341 - (void)domainDidChange:(CPString)aDomain
342 {
343  if (aDomain === CPGlobalDomain || aDomain === CPApplicationDomain)
344  [[CPRunLoop currentRunLoop] performSelector:@selector(synchronize) target:self argument:nil order:0 modes:[CPDefaultRunLoopMode]];
345 
346  [[CPNotificationCenter defaultCenter] postNotificationName:CPUserDefaultsDidChangeNotification object:self];
347 }
348 
352 - (void)synchronize
353 {
354  var globalDomain = [_domains objectForKey:CPGlobalDomain];
355  if (globalDomain)
356  {
357  var data = [CPKeyedArchiver archivedDataWithRootObject:globalDomain];
358  [[self persistentStoreForDomain:CPGlobalDomain] setData:data];
359  }
360 
361  var appDomain = [_domains objectForKey:CPApplicationDomain];
362  if (appDomain)
363  {
364  var data = [CPKeyedArchiver archivedDataWithRootObject:appDomain];
365  [[self persistentStoreForDomain:CPApplicationDomain] setData:data];
366  }
367 }
368 
369 #pragma mark Getting Default Values
370 
374 - (CPArray)arrayForKey:(CPString)aKey
375 {
376  var value = [self objectForKey:aKey];
377  if ([value isKindOfClass:CPArray])
378  return value;
379 
380  return nil;
381 }
382 
386 - (BOOL)boolForKey:(CPString)aKey
387 {
388  var value = [self objectForKey:aKey];
389  if ([value respondsToSelector:@selector(boolValue)])
390  return [value boolValue];
391 
392  return NO;
393 }
394 
395 
399 - (CPData)dataForKey:(CPString)aKey
400 {
401  var value = [self objectForKey:aKey];
402  if ([value isKindOfClass:CPData])
403  return value;
404 
405  return nil;
406 }
407 
411 - (CPDictionary)dictionaryForKey:(CPString)aKey
412 {
413  var value = [self objectForKey:aKey];
414  if ([value isKindOfClass:CPDictionary])
415  return value;
416 
417  return nil;
418 }
419 
423 - (float)floatForKey:(CPString)aKey
424 {
425  var value = [self objectForKey:aKey];
426  if (value === nil)
427  return 0;
428 
429  if ([value respondsToSelector:@selector(floatValue)])
430  value = [value floatValue];
431 
432  return parseFloat(value);
433 }
434 
438 - (int)integerForKey:(CPString)aKey
439 {
440  var value = [self objectForKey:aKey];
441  if (value === nil)
442  return 0;
443 
444  if ([value respondsToSelector:@selector(intValue)])
445  value = [value intValue];
446 
447  return parseInt(value);
448 }
449 
453 - (double)doubleForKey:(CPString)aKey
454 {
455  return [self floatForKey:aKey];
456 }
457 
461 - (CPString)stringForKey:(CPString)aKey
462 {
463  var value = [self objectForKey:aKey];
464 
465  if ([value isKindOfClass:CPString])
466  return value;
467 
468  else if ([value respondsToSelector:@selector(stringValue)])
469  return [value stringValue];
470 
471  return nil;
472 }
473 
477 - (CPArray)stringArrayForKey:(CPString)aKey
478 {
479  var value = [self objectForKey:aKey];
480  if (![value isKindOfClass:CPArray])
481  return nil;
482 
483  for (var i = 0, count = [value count]; i < count; i++)
484  if (![value[i] isKindOfClass:CPString])
485  return nil;
486 
487  return value;
488 }
489 
493 - (CPURL)URLForKey:(CPString)aKey
494 {
495  var value = [self objectForKey:aKey];
496  if ([value isKindOfClass:CPURL])
497  return value;
498 
499  if ([value isKindOfClass:CPString])
500  return [CPURL URLWithString:value];
501 
502  return nil;
503 }
504 
505 #pragma mark Setting Default Values
506 
511 - (void)setBool:(BOOL)aValue forKey:(CPString)aKey
512 {
513  if ([aValue respondsToSelector:@selector(boolValue)])
514  [self setObject:[aValue boolValue] forKey:aKey];
515 }
516 
521 - (void)setFloat:(float)aValue forKey:(CPString)aKey
522 {
523  if ([aValue respondsToSelector:@selector(floatValue)])
524  aValue = [aValue floatValue];
525 
526  [self setObject:parseFloat(aValue) forKey:aKey];
527 }
528 
532 - (void)setDouble:(double)aValue forKey:(CPString)aKey
533 {
534  [self setFloat:aValue forKey:aKey];
535 }
536 
541 - (void)setInteger:(int)aValue forKey:(CPString)aKey
542 {
543  if ([aValue respondsToSelector:@selector(intValue)])
544  aValue = [aValue intValue];
545 
546  [self setObject:parseInt(aValue) forKey:aKey];
547 }
548 
553 - (void)setURL:(CPURL)aValue forKey:(CPString)aKey
554 {
555  if ([aValue isKindOfClass:CPString])
556  aValue = [CPURL URLWithString:aValue];
557 
558  [self setObject:aValue forKey:aKey];
559 }
560 
561 @end
562 
563 @implementation CPUserDefaultsStore : CPObject
564 {
565  CPString _domain;
566 }
567 
568 - (CPData)data
569 {
570  _CPRaiseInvalidAbstractInvocation(self, _cmd);
571  return nil;
572 }
573 
574 - (void)setData:(CPData)aData
575 {
576  _CPRaiseInvalidAbstractInvocation(self, _cmd);
577 }
578 
579 @end
580 
582 {
583  CPCookie _cookie;
584 }
585 
586 - (void)setDomain:(CPString)aDomain
587 {
588  if (_domain === aDomain)
589  return;
590 
591  _domain = aDomain;
592 
593  _cookie = [[CPCookie alloc] initWithName:_domain];
594 }
595 
596 - (CPData)data
597 {
598  var result = [_cookie value];
599  if (!result || [result length] < 1)
600  return nil;
601 
602  return [CPData dataWithRawString:decodeURIComponent(result)];
603 }
604 
605 - (void)setData:(CPData)aData
606 {
607  [_cookie setValue:encodeURIComponent([aData rawString]) expires:[CPDate distantFuture] domain:window.location.href.hostname];
608 }
609 
610 @end
611 
612 var CPUserDefaultsLocalStoreTestKey = "9961800812587769-Cappuccino-Storage-Test";
614 {
615  id __doxygen__;
616 }
617 
618 + (BOOL)supportsLocalStorage
619 {
620  if (!window.localStorage)
621  return NO;
622 
623  try
624  {
625  // Just because localStorage exists does not mean it works. In particular it might be disabled
626  // as it is when Safari's private browsing mode is active.
627  localStorage.setItem(CPUserDefaultsLocalStoreTestKey, "1");
628  if (localStorage.getItem(CPUserDefaultsLocalStoreTestKey) != "1")
629  return NO;
630  localStorage.removeItem(CPUserDefaultsLocalStoreTestKey);
631  }
632  catch (e)
633  {
634  return NO;
635  }
636  return YES;
637 }
638 
639 - (id)init
640 {
641  if (![[self class] supportsLocalStorage])
642  {
643  [CPException raise:@"UnsupportedFeature" reason:@"Browser does not support localStorage for CPUserDefaultsLocalStore"];
644  return self = nil;
645  }
646 
647  return self = [super init];
648 }
649 
650 - (CPData)data
651 {
652  var result = localStorage.getItem(_domain);
653  if (!result || [result length] < 1)
654  return nil;
655 
656  return [CPData dataWithRawString:decodeURIComponent(result)];
657 }
658 
659 - (void)setData:(CPData)aData
660 {
661  try
662  {
663  localStorage.setItem(_domain, encodeURIComponent([aData rawString]));
664  }
665  catch (e)
666  {
667  CPLog.warn("Unable to write to local storage: " + e);
668  }
669 }
670 
671 @end
672 
674 
678 - (CPString)domain
679 {
680  return _domain;
681 }
682 
686 - (void)setDomain:(CPString)aValue
687 {
688  _domain = aValue;
689 }
690 
691 @end
Used to implement exception handling (creating & raising).
Definition: CPException.h:2
void setObject:forKey:(id anObject, [forKey] CPString aKey)
void setData:(CPData aData)
A representation of a single point in time.
Definition: CPDate.h:2
The main run loop for the application.
Definition: CPRunLoop.h:2
CPString rawString()
Definition: CPData.j:121
void performSelector:target:argument:order:modes:(SEL aSelector, [target] id aTarget, [argument] id anArgument, [order] int anOrder, [modes] CPArray modes)
Definition: CPRunLoop.j:253
id objectForKey:(CPString aKey)
CGImage contents()
Definition: CALayer.j:419
void setObject:forKey:inDomain:(id anObject, [forKey] CPString aKey, [inDomain] CPString aDomain)
A Cappuccino wrapper for any data type.
Definition: CPData.h:2
void postNotificationName:object:(CPString aNotificationName, [object] id anObject)
void raise:reason:(CPString aName, [reason] CPString aReason)
Definition: CPException.j:66
CPArray persistentDomainNames()
Provides loading of a URL request.
Unarchives objects created using CPKeyedArchiver.
CPArray volatileDomainNames()
CPData sendSynchronousRequest:returningResponse:(CPURLRequest aRequest, [returningResponse]/*{ */CPURLResponse/*} */aURLResponse)
void domainDidChange:(CPString aDomain)
id distantFuture()
Definition: CPDate.j:67
CPNotificationCenter defaultCenter()
A mutable key-value pair collection.
Definition: CPDictionary.h:2
CPRunLoop currentRunLoop()
Definition: CPRunLoop.j:232
CPApplicationDomain
id requestWithURL:(CPURL aURL)
Definition: CPURLRequest.j:56
CPGlobalDomain
Implements keyed archiving of object graphs (e.g. for storing data).
An immutable string (collection of characters).
Definition: CPString.h:2
CPBundle mainBundle()
Definition: CPBundle.j:82
void reloadDataFromStoreForDomain:(CPString aDomain)
id objectForKey:(id aKey)
Definition: CPDictionary.j:515
CPUserDefaultsStore setPersistentStoreClass:forDomain:reloadData:(Class aStoreClass, [forDomain] CPString aDomain, [reloadData] BOOL aFlag)
var StandardUserDefaults
void registerDefaults:(CPDictionary aDictionary)
CPUserDefaultsDidChangeNotification
CPData dataWithRawString:(CPString aString)
Definition: CPData.j:45
var CPUserDefaultsLocalStoreTestKey
CPUserDefaultsStore persistentStoreForDomain:(CPString aDomain)
global CPApp CPArgumentDomain
void resetStandardUserDefaults()
CPLocaleDomain
void removeObjectForKey:inDomain:(CPString aKey, [inDomain] CPString aDomain)
id unarchiveObjectWithData:(CPData aData)
id init()
Definition: CPObject.j:145
Sends messages (CPNotification) between objects.
float floatForKey:(CPString aKey)
Contains data obtained during a request made with CPURLConnection.
Definition: CPURLRequest.h:2
CPArray allKeys()
Definition: CPDictionary.j:308
id URLWithString:(CPString URLString)
Definition: CPURL.j:78
void setFloat:forKey:(float aValue, [forKey] CPString aKey)
CPDictionary infoDictionary()
Definition: CPBundle.j:178
Definition: CPURL.h:2
CPRegistrationDomain
id alloc()
Definition: CPObject.j:130
CPData archivedDataWithRootObject:(id anObject)