API  0.9.6
 All Classes Files Functions Variables Macros Groups Pages
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 CPArgumentDomain = @"CPArgumentDomain";
26 CPApplicationDomain = [[[CPBundle mainBundle] infoDictionary] objectForKey:@"CPBundleIdentifier"] || @"CPApplicationDomain";
27 CPGlobalDomain = @"CPGlobalDomain";
28 CPLocaleDomain = @"CPLocaleDomain";
29 CPRegistrationDomain = @"CPRegistrationDomain";
30 
31 CPUserDefaultsDidChangeNotification = @"CPUserDefaultsDidChangeNotification";
32 
34 
51 @implementation CPUserDefaults : CPObject
52 {
53  CPDictionary _domains;
54  CPDictionary _stores;
55 
56  CPDictionary _searchList;
57  BOOL _searchListNeedsReload;
58 }
59 
63 + (id)standardUserDefaults
64 {
67 
68  return StandardUserDefaults;
69 }
70 
75 + (void)resetStandardUserDefaults
76 {
78  [StandardUserDefaults synchronize];
79 
81 }
82 
83 /*
84  @ignore
85 */
86 - (id)init
87 {
88  self = [super init];
89 
90  if (self)
91  {
92  _domains = [CPDictionary dictionary];
93  [self _setupArgumentsDomain];
94 
96 
97  _stores = [CPDictionary dictionary];
98  [self setPersistentStoreClass:defaultStore forDomain:CPGlobalDomain reloadData:YES];
99  [self setPersistentStoreClass:defaultStore forDomain:CPApplicationDomain reloadData:YES];
100  }
101 
102  return self;
103 }
104 
105 /*
106  @ignore
107 */
108 - (void)_setupArgumentsDomain
109 {
110  var args = [CPApp namedArguments],
111  keys = [args allKeys],
112  count = [keys count],
113  i = 0;
114 
115  for (; i < count; i++)
116  {
117  var key = keys[i];
118  [self setObject:[args objectForKey:key] forKey:key inDomain:CPArgumentDomain];
119  }
120 }
121 
126 - (id)objectForKey:(CPString)aKey
127 {
128  if (_searchListNeedsReload)
129  [self _reloadSearchList];
130 
131  return [_searchList objectForKey:aKey];
132 }
133 
137 - (void)setObject:(id)anObject forKey:(CPString)aKey
138 {
139  [self setObject:anObject forKey:aKey inDomain:CPApplicationDomain];
140 }
141 
147 - (id)objectForKey:(CPString)aKey inDomain:(CPString)aDomain
148 {
149  var domain = [_domains objectForKey:aDomain];
150 
151  if (!domain)
152  return nil;
153 
154  return [domain objectForKey:aKey];
155 }
156 
161 - (void)setObject:(id)anObject forKey:(CPString)aKey inDomain:(CPString)aDomain
162 {
163  if (!aKey || !aDomain)
164  return;
165 
166  var domain = [_domains objectForKey:aDomain];
167  if (!domain)
168  {
169  domain = [CPDictionary dictionary];
170  [_domains setObject:domain forKey:aDomain];
171  }
172 
173  [domain setObject:anObject forKey:aKey];
174  _searchListNeedsReload = YES;
175  [self domainDidChange:aDomain];
176 }
177 
182 - (void)removeObjectForKey:(CPString)aKey
183 {
184  [self removeObjectForKey:aKey inDomain:CPApplicationDomain];
185 }
186 
190 - (void)removeObjectForKey:(CPString)aKey inDomain:(CPString)aDomain
191 {
192  if (!aKey || !aDomain)
193  return;
194 
195  var domain = [_domains objectForKey:aDomain];
196  if (!domain)
197  return;
198 
199  [domain removeObjectForKey:aKey];
200  _searchListNeedsReload = YES;
201  [self domainDidChange:aDomain];
202 }
203 
212 - (void)registerDefaults:(CPDictionary)aDictionary
213 {
214  var keys = [aDictionary allKeys],
215  count = [keys count],
216  i = 0;
217 
218  for (; i < count; i++)
219  {
220  var key = keys[i];
221  [self setObject:[aDictionary objectForKey:key] forKey:key inDomain:CPRegistrationDomain];
222  }
223 }
224 
231 - (void)registerDefaultsFromContentsOfFile:(CPURL)aURL
232 {
234  data = [CPData dataWithRawString:[contents rawString]],
235  plist = [data plistObject];
236 
237  [self registerDefaults:plist];
238 }
239 
240 /*
241  @ignore
242 */
243 - (void)_reloadSearchList
244 {
245  _searchListNeedsReload = NO;
246 
247  var dicts = [CPRegistrationDomain, CPGlobalDomain, CPApplicationDomain, CPArgumentDomain],
248  count = [dicts count],
249  i = 0;
250 
251  _searchList = [CPDictionary dictionary];
252 
253  for (; i < count; i++)
254  {
255  var domain = [_domains objectForKey:dicts[i]];
256  if (!domain)
257  continue;
258 
259  var keys = [domain allKeys],
260  keysCount = [keys count],
261  j = 0;
262 
263  for (; j < keysCount; j++)
264  {
265  var key = keys[j];
266  [_searchList setObject:[domain objectForKey:key] forKey:key];
267  }
268  }
269 }
270 
271 // Synchronization
272 
276 - (CPArray)volatileDomainNames
277 {
278  return [CPArgumentDomain, CPLocaleDomain, CPRegistrationDomain];
279 }
280 
284 - (CPArray)persistentDomainNames
285 {
286  return [CPGlobalDomain, CPApplicationDomain];
287 }
288 
292 - (CPUserDefaultsStore)persistentStoreForDomain:(CPString)aDomain
293 {
294  return [_stores objectForKey:aDomain];
295 }
296 
305 - (CPUserDefaultsStore)setPersistentStoreClass:(Class)aStoreClass forDomain:(CPString)aDomain reloadData:(BOOL)aFlag
306 {
307  var currentStore = [_stores objectForKey:aDomain];
308  if (currentStore && [currentStore class] === aStoreClass)
309  return currentStore;
310 
311  var store = [[aStoreClass alloc] init];
312  [store setDomain:aDomain];
313  [_stores setObject:store forKey:aDomain];
314 
315  if (aFlag)
316  [self reloadDataFromStoreForDomain:aDomain];
317 
318  return store;
319 }
320 
324 - (void)reloadDataFromStoreForDomain:(CPString)aDomain
325 {
326  var data = [[self persistentStoreForDomain:aDomain] data],
327  domain = data ? [CPKeyedUnarchiver unarchiveObjectWithData:data] : nil;
328 
329  [_domains setObject:domain forKey:aDomain];
330 
331  _searchListNeedsReload = YES;
332 }
333 
337 - (void)domainDidChange:(CPString)aDomain
338 {
339  if (aDomain === CPGlobalDomain || aDomain === CPApplicationDomain)
340  [[CPRunLoop currentRunLoop] performSelector:@selector(synchronize) target:self argument:nil order:0 modes:[CPDefaultRunLoopMode]];
341 
342  [[CPNotificationCenter defaultCenter] postNotificationName:CPUserDefaultsDidChangeNotification object:self];
343 }
344 
348 - (void)synchronize
349 {
350  var globalDomain = [_domains objectForKey:CPGlobalDomain];
351  if (globalDomain)
352  {
353  var data = [CPKeyedArchiver archivedDataWithRootObject:globalDomain];
354  [[self persistentStoreForDomain:CPGlobalDomain] setData:data];
355  }
356 
357  var appDomain = [_domains objectForKey:CPApplicationDomain];
358  if (appDomain)
359  {
360  var data = [CPKeyedArchiver archivedDataWithRootObject:appDomain];
361  [[self persistentStoreForDomain:CPApplicationDomain] setData:data];
362  }
363 }
364 
365 #pragma mark Getting Default Values
366 
370 - (CPArray)arrayForKey:(CPString)aKey
371 {
372  var value = [self objectForKey:aKey];
373  if ([value isKindOfClass:CPArray])
374  return value;
375 
376  return nil;
377 }
378 
382 - (BOOL)boolForKey:(CPString)aKey
383 {
384  var value = [self objectForKey:aKey];
385  if ([value respondsToSelector:@selector(boolValue)])
386  return [value boolValue];
387 
388  return NO;
389 }
390 
391 
395 - (CPData)dataForKey:(CPString)aKey
396 {
397  var value = [self objectForKey:aKey];
398  if ([value isKindOfClass:CPData])
399  return value;
400 
401  return nil;
402 }
403 
407 - (CPDictionary)dictionaryForKey:(CPString)aKey
408 {
409  var value = [self objectForKey:aKey];
410  if ([value isKindOfClass:CPDictionary])
411  return value;
412 
413  return nil;
414 }
415 
419 - (float)floatForKey:(CPString)aKey
420 {
421  var value = [self objectForKey:aKey];
422  if (value === nil)
423  return 0;
424 
425  if ([value respondsToSelector:@selector(floatValue)])
426  value = [value floatValue];
427 
428  return parseFloat(value);
429 }
430 
434 - (int)integerForKey:(CPString)aKey
435 {
436  var value = [self objectForKey:aKey];
437  if (value === nil)
438  return 0;
439 
440  if ([value respondsToSelector:@selector(intValue)])
441  value = [value intValue];
442 
443  return parseInt(value);
444 }
445 
449 - (double)doubleForKey:(CPString)aKey
450 {
451  return [self floatForKey:aKey];
452 }
453 
457 - (CPString)stringForKey:(CPString)aKey
458 {
459  var value = [self objectForKey:aKey];
460 
461  if ([value isKindOfClass:CPString])
462  return value;
463 
464  else if ([value respondsToSelector:@selector(stringValue)])
465  return [value stringValue];
466 
467  return nil;
468 }
469 
473 - (CPArray)stringArrayForKey:(CPString)aKey
474 {
475  var value = [self objectForKey:aKey];
476  if (![value isKindOfClass:CPArray])
477  return nil;
478 
479  for (var i = 0, count = [value count]; i < count; i++)
480  if (![value[i] isKindOfClass:CPString])
481  return nil;
482 
483  return value;
484 }
485 
489 - (CPURL)URLForKey:(CPString)aKey
490 {
491  var value = [self objectForKey:aKey];
492  if ([value isKindOfClass:CPURL])
493  return value;
494 
495  if ([value isKindOfClass:CPString])
496  return [CPURL URLWithString:value];
497 
498  return nil;
499 }
500 
501 #pragma mark Setting Default Values
502 
507 - (void)setBool:(BOOL)aValue forKey:(CPString)aKey
508 {
509  if ([aValue respondsToSelector:@selector(boolValue)])
510  [self setObject:[aValue boolValue] forKey:aKey];
511 }
512 
517 - (void)setFloat:(float)aValue forKey:(CPString)aKey
518 {
519  if ([aValue respondsToSelector:@selector(aValue)])
520  aValue = [aValue floatValue];
521 
522  [self setObject:parseFloat(aValue) forKey:aKey];
523 }
524 
528 - (void)setDouble:(double)aValue forKey:(CPString)aKey
529 {
530  [self setFloat:aValue forKey:aKey];
531 }
532 
537 - (void)setInteger:(int)aValue forKey:(CPString)aKey
538 {
539  if ([aValue respondsToSelector:@selector(intValue)])
540  aValue = [aValue intValue];
541 
542  [self setObject:parseInt(aValue) forKey:aKey];
543 }
544 
549 - (void)setURL:(CPURL)aValue forKey:(CPString)aKey
550 {
551  if ([aValue isKindOfClass:CPString])
552  aValue = [CPURL URLWithString:aValue];
553 
554  [self setObject:aValue forKey:aKey];
555 }
556 
557 @end
558 
559 @implementation CPUserDefaultsStore : CPObject
560 {
561  CPString _domain;
562 }
563 
564 - (CPData)data
565 {
566  _CPRaiseInvalidAbstractInvocation(self, _cmd);
567  return nil;
568 }
569 
570 - (void)setData:(CPData)aData
571 {
572  _CPRaiseInvalidAbstractInvocation(self, _cmd);
573 }
574 
575 @end
576 
578 {
579  CPCookie _cookie;
580 }
581 
582 - (void)setDomain:(CPString)aDomain
583 {
584  if (_domain === aDomain)
585  return;
586 
587  _domain = aDomain;
588 
589  _cookie = [[CPCookie alloc] initWithName:_domain];
590 }
591 
592 - (CPData)data
593 {
594  var result = [_cookie value];
595  if (!result || [result length] < 1)
596  return nil;
597 
598  return [CPData dataWithRawString:decodeURIComponent(result)];
599 }
600 
601 - (void)setData:(CPData)aData
602 {
603  [_cookie setValue:encodeURIComponent([aData rawString]) expires:[CPDate distantFuture] domain:window.location.href.hostname];
604 }
605 
606 @end
607 
608 var CPUserDefaultsLocalStoreTestKey = "9961800812587769-Cappuccino-Storage-Test";
610 {
611  id __doxygen__;
612 }
613 
614 + (BOOL)supportsLocalStorage
615 {
616  if (!window.localStorage)
617  return NO;
618 
619  try
620  {
621  // Just because localStorage exists does not mean it works. In particular it might be disabled
622  // as it is when Safari's private browsing mode is active.
623  localStorage.setItem(CPUserDefaultsLocalStoreTestKey, "1");
624  if (localStorage.getItem(CPUserDefaultsLocalStoreTestKey) != "1")
625  return NO;
626  localStorage.removeItem(CPUserDefaultsLocalStoreTestKey);
627  }
628  catch (e)
629  {
630  return NO;
631  }
632  return YES;
633 }
634 
635 - (id)init
636 {
637  if (![[self class] supportsLocalStorage])
638  {
639  [CPException raise:@"UnsupportedFeature" reason:@"Browser does not support localStorage for CPUserDefaultsLocalStore"];
640  return self = nil;
641  }
642 
643  return self = [super init];
644 }
645 
646 - (CPData)data
647 {
648  var result = localStorage.getItem(_domain);
649  if (!result || [result length] < 1)
650  return nil;
651 
652  return [CPData dataWithRawString:decodeURIComponent(result)];
653 }
654 
655 - (void)setData:(CPData)aData
656 {
657  try
658  {
659  localStorage.setItem(_domain, encodeURIComponent([aData rawString]));
660  }
661  catch (e)
662  {
663  CPLog.warn("Unable to write to local storage: " + e);
664  }
665 }
666 
667 @end
668 
670 
674 - (CPString)domain
675 {
676  return _domain;
677 }
678 
682 - (void)setDomain:(CPString)aValue
683 {
684  _domain = aValue;
685 }
686 
687 @end