LCOV - code coverage report
Current view: top level - Core - OOCacheManager.m (source / functions) Hit Total Coverage
Test: coverxygen.info Lines: 0 50 0.0 %
Date: 2025-05-28 07:50:54 Functions: 0 0 -

          Line data    Source code
       1           0 : /*
       2             : 
       3             : OOCacheManager.m
       4             : 
       5             : Oolite
       6             : Copyright (C) 2004-2013 Giles C Williams and contributors
       7             : 
       8             : This program is free software; you can redistribute it and/or
       9             : modify it under the terms of the GNU General Public License
      10             : as published by the Free Software Foundation; either version 2
      11             : of the License, or (at your option) any later version.
      12             : 
      13             : This program 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
      16             : GNU General Public License for more details.
      17             : 
      18             : You should have received a copy of the GNU General Public License
      19             : along with this program; if not, write to the Free Software
      20             : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
      21             : MA 02110-1301, USA.
      22             : 
      23             : */
      24             : 
      25             : #import "OOCacheManager.h"
      26             : #import "OOPListParsing.h"
      27             : #import "OODeepCopy.h"
      28             : #import "OOCollectionExtractors.h"
      29             : #import "OOJavaScriptEngine.h"
      30             : #import "NSFileManagerOOExtensions.h"
      31             : 
      32             : 
      33           0 : #define WRITE_ASYNC                             1
      34           0 : #define PROFILE_WRITES                  0
      35             : 
      36             : 
      37             : // Use the (presumed) most efficient plist format for each platform.
      38             : #if OOLITE_MAC_OS_X
      39           0 : #define CACHE_PLIST_FORMAT      NSPropertyListBinaryFormat_v1_0
      40             : #else
      41             : #define CACHE_PLIST_FORMAT      NSPropertyListGNUstepBinaryFormat
      42             : #endif
      43             : 
      44             : 
      45             : #if WRITE_ASYNC
      46             : #import "OOAsyncWorkManager.h"
      47             : #endif
      48             : #if PROFILE_WRITES
      49             : #import "OOProfilingStopwatch.h"
      50             : #endif
      51             : 
      52             : 
      53           0 : static NSString * const kOOLogDataCacheFound                            = @"dataCache.found";
      54           0 : static NSString * const kOOLogDataCacheNotFound                         = @"dataCache.notFound";
      55           0 : static NSString * const kOOLogDataCacheRebuild                          = @"dataCache.rebuild";
      56           0 : static NSString * const kOOLogDataCacheWriteSuccess                     = @"dataCache.write.success";
      57           0 : static NSString * const kOOLogDataCacheWriteFailed                      = @"dataCache.write.failed";
      58           0 : static NSString * const kOOLogDataCacheRetrieveSuccess          = @"dataCache.retrieve.success";
      59           0 : static NSString * const kOOLogDataCacheRetrieveFailed           = @"dataCache.retrieve.failed";
      60           0 : static NSString * const kOOLogDataCacheSetSuccess                       = @"dataCache.set.success";
      61           0 : static NSString * const kOOLogDataCacheSetFailed                        = @"dataCache.set.failed";
      62           0 : static NSString * const kOOLogDataCacheRemoveSuccess            = @"dataCache.remove.success";
      63           0 : static NSString * const kOOLogDataCacheClearSuccess                     = @"dataCache.clear.success";
      64           0 : static NSString * const kOOLogDataCacheBuildPathError           = @"dataCache.write.buildPath.failed";
      65           0 : static NSString * const kOOLogDataCacheSerializationError       = @"dataCache.write.serialize.failed";
      66             : 
      67           0 : static NSString * const kCacheKeyVersion                                        = @"version";
      68           0 : static NSString * const kCacheKeyEndianTag                                      = @"endian tag";
      69           0 : static NSString * const kCacheKeyFormatVersion                          = @"format version";
      70           0 : static NSString * const kCacheKeyCaches                                         = @"caches";
      71             : 
      72             : 
      73           0 : enum
      74             : {
      75             :         kEndianTagValue                 = 0x0123456789ABCDEFULL,
      76             :         kFormatVersionValue             = 219
      77             : };
      78             : 
      79             : 
      80           0 : static OOCacheManager *sSingleton = nil;
      81             : 
      82             : 
      83             : @interface OOCacheManager (Private)
      84             : 
      85           0 : - (void)loadCache;
      86           0 : - (void)write;
      87           0 : - (void)clear;
      88           0 : - (BOOL)dirty;
      89           0 : - (void)markClean;
      90             : 
      91           0 : - (NSDictionary *)loadDict;
      92           0 : - (BOOL)writeDict:(NSDictionary *)inDict;
      93             : 
      94           0 : - (void)buildCachesFromDictionary:(NSDictionary *)inDict;
      95           0 : - (NSDictionary *)dictionaryOfCaches;
      96             : 
      97           0 : - (BOOL)directoryExists:(NSString *)inPath create:(BOOL)inCreate;
      98             : 
      99             : @end
     100             : 
     101             : 
     102             : @interface OOCacheManager (PlatformSpecific)
     103             : 
     104           0 : - (NSString *)cachePathCreatingIfNecessary:(BOOL)inCreate;
     105             : 
     106             : @end
     107             : 
     108             : 
     109             : #if WRITE_ASYNC
     110           0 : @interface OOAsyncCacheWriter: NSObject <OOAsyncWorkTask>
     111             : {
     112             : @private
     113             :         NSDictionary                    *_cacheContents;
     114           0 : }
     115             : 
     116             : - (id) initWithCacheContents:(NSDictionary *)cacheContents;
     117           0 : 
     118             : @end
     119             : #endif
     120             : 
     121             : 
     122             : @implementation OOCacheManager
     123             : 
     124             : - (id)init
     125           0 : {
     126             :         self = [super init];
     127             :         if (self != nil)
     128             :         {
     129             :                 _permitWrites = YES;
     130             :                 [self loadCache];
     131             :         }
     132             :         return self;
     133             : }
     134             : 
     135             : 
     136             : - (void)dealloc
     137           0 : {
     138             :         [self clear];
     139             :         
     140             :         [super dealloc];
     141             : }
     142             : 
     143             : 
     144             : - (NSString *)description
     145           0 : {
     146             :         return [NSString stringWithFormat:@"<%@ %p>{dirty=%s}", [self class], self, [self dirty] ? "yes" : "no"];
     147             : }
     148             : 
     149             : 
     150             : + (OOCacheManager *) sharedCache
     151             : {
     152             :         // NOTE: assumes single-threaded access.
     153             :         if (sSingleton == nil)
     154             :         {
     155             :                 sSingleton = [[self alloc] init];
     156             :         }
     157             :         
     158             :         return sSingleton;
     159             : }
     160             : 
     161             : 
     162             : - (id)objectForKey:(NSString *)inKey inCache:(NSString *)inCacheKey
     163             : {
     164             :         NSMutableDictionary             *cache = nil;
     165             :         id                                              result = nil;
     166             :         
     167             :         NSParameterAssert(inKey != nil && inCacheKey != nil);
     168             :         
     169             :         cache = [_caches objectForKey:inCacheKey];
     170             :         if (cache != nil)
     171             :         {
     172             :                 result = [cache objectForKey:inKey];
     173             :                 if (result != nil)
     174             :                 {
     175             :                         OODebugLog(kOOLogDataCacheRetrieveSuccess, @"Retrieved \"%@\" cache object %@.", inCacheKey, inKey);
     176             :                 }
     177             :                 else
     178             :                 {
     179             :                         OODebugLog(kOOLogDataCacheRetrieveFailed, @"Failed to retrieve \"%@\" cache object %@ -- no such entry.", inCacheKey, inKey);
     180             :                 }
     181             :         }
     182             :         else
     183             :         {
     184             :                 OODebugLog(kOOLogDataCacheRetrieveFailed, @"Failed to retrieve \"%@\" cache object %@ -- no such cache.", inCacheKey, inKey);
     185             :         }
     186             :         
     187             :         return result;
     188             : }
     189             : 
     190             : 
     191             : 
     192             : - (void)setObject:(id)inObject forKey:(NSString *)inKey inCache:(NSString *)inCacheKey
     193             : {
     194             :         NSMutableDictionary             *cache = nil;
     195             :         
     196             :         NSParameterAssert(inObject != nil && inKey != nil && inCacheKey != nil);
     197             :         
     198             :         if (EXPECT_NOT(_caches == nil))  return;
     199             :         
     200             :         cache = [_caches objectForKey:inCacheKey];
     201             :         if (cache == nil)
     202             :         {
     203             :                 cache = [NSMutableDictionary dictionary];
     204             :                 if (cache == nil)
     205             :                 {
     206             :                         OODebugLog(kOOLogDataCacheSetFailed, @"Failed to create cache for key \"%@\".", inCacheKey);
     207             :                         return;
     208             :                 }
     209             :                 [_caches setObject:cache forKey:inCacheKey];
     210             :         }
     211             :         
     212             :         [cache setObject:inObject forKey:inKey];
     213             :         _dirty = YES;
     214             :         OODebugLog(kOOLogDataCacheSetSuccess, @"Updated entry %@ in cache \"%@\".", inKey, inCacheKey);
     215             : }
     216             : 
     217             : 
     218             : - (void)removeObjectForKey:(NSString *)inKey inCache:(NSString *)inCacheKey
     219             : {
     220             :         NSMutableDictionary             *cache = nil;
     221             :         
     222             :         NSParameterAssert(inKey != nil && inCacheKey != nil);
     223             :         
     224             :         cache = [_caches objectForKey:inCacheKey];
     225             :         if (cache != nil)
     226             :         {
     227             :                 if (nil != [cache objectForKey:inKey])
     228             :                 {
     229             :                         [cache removeObjectForKey:inKey];
     230             :                         _dirty = YES;
     231             :                         OODebugLog(kOOLogDataCacheRemoveSuccess, @"Removed entry keyed %@ from cache \"%@\".", inKey, inCacheKey);
     232             :                 }
     233             :                 else
     234             :                 {
     235             :                         OODebugLog(kOOLogDataCacheRemoveSuccess, @"No need to remove non-existent entry keyed %@ from cache \"%@\".", inKey, inCacheKey);
     236             :                 }
     237             :         }
     238             :         else
     239             :         {
     240             :                 OODebugLog(kOOLogDataCacheRemoveSuccess, @"No need to remove entry keyed %@ from non-existent cache \"%@\".", inKey, inCacheKey);
     241             :         }
     242             : }
     243             : 
     244             : 
     245             : - (void)clearCache:(NSString *)inCacheKey
     246             : {
     247             :         NSParameterAssert(inCacheKey != nil);
     248             :         
     249             :         if (nil != [_caches objectForKey:inCacheKey])
     250             :         {
     251             :                 [_caches removeObjectForKey:inCacheKey];
     252             :                 _dirty = YES;
     253             :                 OODebugLog(kOOLogDataCacheClearSuccess, @"Cleared cache \"%@\".", inCacheKey);
     254             :         }
     255             :         else
     256             :         {
     257             :                 OODebugLog(kOOLogDataCacheClearSuccess, @"No need to clear non-existent cache \"%@\".", inCacheKey);
     258             :         }
     259             : }
     260             : 
     261             : 
     262             : - (void)clearAllCaches
     263             : {
     264             :         [self clear];
     265             :         _caches = [[NSMutableDictionary alloc] init];
     266             :         _dirty = YES;
     267             : }
     268             : 
     269             : 
     270             : - (void) reloadAllCaches
     271             : {
     272             :         [self clear];
     273             :         [self loadCache];
     274             : }
     275             : 
     276             : 
     277             : - (void)flush
     278             : {
     279             :         if (_permitWrites && [self dirty] && _scheduledWrite == nil)
     280             :         {
     281             :                 [self write];
     282             :                 [self markClean];
     283             :         }
     284             : }
     285             : 
     286             : 
     287             : - (void)finishOngoingFlush
     288             : {
     289             : #if WRITE_ASYNC
     290             :         [[OOAsyncWorkManager sharedAsyncWorkManager] waitForTaskToComplete:_scheduledWrite];
     291             : #endif
     292             : }
     293             : 
     294             : 
     295             : - (void)setAllowCacheWrites:(BOOL)flag
     296             : {
     297             :         _permitWrites = (flag != NO);
     298             : }
     299             : 
     300             : 
     301             : - (NSString *)cacheDirectoryPathCreatingIfNecessary:(BOOL)create
     302             : {
     303             :         /*      Construct the path to the directory for cache files, which is:
     304             :                         ~/Library/Caches/org.aegidian.oolite/
     305             :                         or
     306             :                         ~/GNUStep/Library/Caches/org.aegidian.oolite/
     307             :                 In addition to generally being the right place to put caches,
     308             :                 ~/Library/Caches has the particular advantage of not being indexed by
     309             :                 Spotlight or backed up by Time Machine.
     310             :         */
     311             :         NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
     312             :         if (![self directoryExists:cachePath create:create]) return nil;
     313             : 
     314             : #if !OOLITE_MAC_OS_X
     315             :         // the old cache file on GNUstep was one level up, so remove it if it exists
     316             :         [[NSFileManager defaultManager] removeFileAtPath:[cachePath stringByAppendingPathComponent:@"Oolite-cache.plist"] handler:nil];
     317             : #endif
     318             : 
     319             :         cachePath = [cachePath stringByAppendingPathComponent:@"org.aegidian.oolite"];
     320             :         if (![self directoryExists:cachePath create:create]) return nil;
     321             :         return cachePath;
     322             : }
     323             : 
     324             : @end
     325             : 
     326             : 
     327             : @implementation OOCacheManager (Private)
     328             : 
     329             : - (void)loadCache
     330             : {
     331             :         NSDictionary                    *cache = nil;
     332             :         NSString                                *cacheVersion = nil;
     333             :         NSString                                *ooliteVersion = nil;
     334             :         NSData                                  *endianTag = nil;
     335             :         NSNumber                                *formatVersion = nil;
     336             :         BOOL                                    accept = YES;
     337             :         uint64_t                                endianTagValue = 0;
     338             :         
     339             :         ooliteVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
     340             :         
     341             :         [self clear];
     342             :         
     343             :         cache = [self loadDict];
     344             :         if (cache != nil)
     345             :         {
     346             :                 // We have a cache
     347             :                 OOLog(kOOLogDataCacheFound, @"%@", @"Found data cache.");
     348             :                 OOLogIndentIf(kOOLogDataCacheFound);
     349             :                 
     350             :                 cacheVersion = [cache objectForKey:kCacheKeyVersion];
     351             :                 if (![cacheVersion isEqual:ooliteVersion])
     352             :                 {
     353             :                         OOLog(kOOLogDataCacheRebuild, @"Data cache version (%@) does not match Oolite version (%@), rebuilding cache.", cacheVersion, ooliteVersion);
     354             :                         accept = NO;
     355             :                 }
     356             :                 
     357             :                 formatVersion = [cache objectForKey:kCacheKeyFormatVersion];
     358             :                 if (accept && [formatVersion unsignedIntValue] != kFormatVersionValue)
     359             :                 {
     360             :                         OOLog(kOOLogDataCacheRebuild, @"Data cache format (%@) is not supported format (%u), rebuilding cache.", formatVersion, kFormatVersionValue);
     361             :                         accept = NO;
     362             :                 }
     363             :                 
     364             :                 if (accept)
     365             :                 {
     366             :                         endianTag = [cache objectForKey:kCacheKeyEndianTag];
     367             :                         if (![endianTag isKindOfClass:[NSData class]] || [endianTag length] != sizeof endianTagValue)
     368             :                         {
     369             :                                 OOLog(kOOLogDataCacheRebuild, @"%@", @"Data cache endian tag is invalid, rebuilding cache.");
     370             :                                 accept = NO;
     371             :                         }
     372             :                         else
     373             :                         {
     374             :                                 endianTagValue = *(const uint64_t *)[endianTag bytes];
     375             :                                 if (endianTagValue != kEndianTagValue)
     376             :                                 {
     377             :                                         OOLog(kOOLogDataCacheRebuild, @"%@", @"Data cache endianness is inappropriate for this system, rebuilding cache.");
     378             :                                         accept = NO;
     379             :                                 }
     380             :                         }
     381             :                 }
     382             :                 
     383             :                 if (accept)
     384             :                 {
     385             :                         // We have a cache, and it's the right format.
     386             :                         [self buildCachesFromDictionary:[cache objectForKey:kCacheKeyCaches]];
     387             :                 }
     388             :                 
     389             :                 OOLogOutdentIf(kOOLogDataCacheFound);
     390             :         }
     391             :         else
     392             :         {
     393             :                 // No cache
     394             :                 OOLog(kOOLogDataCacheNotFound, @"%@", @"No data cache found, starting from scratch.");
     395             :         }
     396             :         
     397             :         // If loading failed, or there was a version or endianness conflict
     398             :         if (_caches == nil) _caches = [[NSMutableDictionary alloc] init];
     399             :         [self markClean];
     400             : }
     401             : 
     402             : 
     403             : - (void)write
     404             : {
     405             :         NSMutableDictionary             *newCache = nil;
     406             :         NSString                                *ooliteVersion = nil;
     407             :         NSData                                  *endianTag = nil;
     408             :         NSNumber                                *formatVersion = nil;
     409             :         NSDictionary                    *pListRep = nil;
     410             :         uint64_t                                endianTagValue = kEndianTagValue;
     411             :         
     412             :         if (_caches == nil) return;
     413             :         if (_scheduledWrite != nil)  return;
     414             :         
     415             : #if PROFILE_WRITES
     416             :         OOProfilingStopwatch *stopwatch = [OOProfilingStopwatch stopwatch];
     417             : #endif
     418             :         
     419             : #if WRITE_ASYNC
     420             :         OOLog(@"dataCache.willWrite", @"%@", @"Scheduling data cache write.");
     421             : #else
     422             :         OOLog(@"dataCache.willWrite", @"%@", @"About to write cache.");
     423             : #endif
     424             :         
     425             :         ooliteVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
     426             :         endianTag = [NSData dataWithBytes:&endianTagValue length:sizeof endianTagValue];
     427             :         formatVersion = [NSNumber numberWithUnsignedInt:kFormatVersionValue];
     428             :         
     429             :         pListRep = [self dictionaryOfCaches];
     430             :         if (ooliteVersion == nil || endianTag == nil || formatVersion == nil || pListRep == nil)
     431             :         {
     432             :                 OOLog(@"dataCache.cantWrite", @"%@", @"Failed to write data cache -- prerequisites not fulfilled. This is an internal error, please report it.");
     433             :                 return;
     434             :         }
     435             :         
     436             :         newCache = [NSMutableDictionary dictionaryWithCapacity:4];
     437             :         [newCache setObject:ooliteVersion forKey:kCacheKeyVersion];
     438             :         [newCache setObject:formatVersion forKey:kCacheKeyFormatVersion];
     439             :         [newCache setObject:endianTag forKey:kCacheKeyEndianTag];
     440             :         [newCache setObject:pListRep forKey:kCacheKeyCaches];
     441             :         
     442             : #if PROFILE_WRITES && !WRITE_ASYNC
     443             :         OOTimeDelta prepareT = [stopwatch reset];
     444             : #endif
     445             :         
     446             : #if WRITE_ASYNC
     447             :         NSDictionary *cacheData = newCache;
     448             :         _scheduledWrite = [[OOAsyncCacheWriter alloc] initWithCacheContents:cacheData];
     449             :         
     450             : #if PROFILE_WRITES
     451             :         OOTimeDelta endT = [stopwatch reset];
     452             :         OOLog(@"dataCache.profile", @"Time to prepare cache data: %g seconds.", endT);
     453             : #endif
     454             :         
     455             :         [[OOAsyncWorkManager sharedAsyncWorkManager] addTask:_scheduledWrite priority:kOOAsyncPriorityLow];
     456             : #else
     457             : #if PROFILE_WRITES
     458             :         OOLog(@"dataCache.profile", @"Time to prepare cache data: %g seconds.", prepareT);
     459             : #endif
     460             :         
     461             :         if ([self writeDict:newCache])
     462             :         {
     463             :                 [self markClean];
     464             :                 OOLog(kOOLogDataCacheWriteSuccess, @"%@", @"Wrote data cache.");
     465             :         }
     466             :         else
     467             :         {
     468             :                 OOLog(kOOLogDataCacheWriteFailed, @"%@", @"Failed to write data cache.");
     469             :         }
     470             : #endif
     471             : }
     472             : 
     473             : 
     474             : - (void)clear
     475             : {
     476             :         [_caches release];
     477             :         _caches = nil;
     478             : }
     479             : 
     480             : 
     481             : - (BOOL)dirty
     482             : {
     483             :         return _dirty;
     484             : }
     485             : 
     486             : 
     487             : - (void)markClean
     488             : {
     489             :         _dirty = NO;
     490             : }
     491             : 
     492             : 
     493             : - (NSDictionary *)loadDict
     494             : {
     495             :         NSString                        *path = nil;
     496             :         NSData                          *data = nil;
     497             :         NSString                        *errorString = nil;
     498             :         id                                      contents = nil;
     499             :         
     500             :         path = [self cachePathCreatingIfNecessary:NO];
     501             :         if (path == nil) return nil;
     502             :         
     503             :         @try
     504             :         {
     505             :                 data = [NSData dataWithContentsOfFile:path];
     506             :                 if (data == nil)  return nil;
     507             :                 
     508             :                 contents = [NSPropertyListSerialization propertyListFromData:data
     509             :                                                                                                         mutabilityOption:NSPropertyListImmutable
     510             :                                                                                                                           format:NULL
     511             :                                                                                                         errorDescription:&errorString];
     512             :         }
     513             :         @catch (NSException *exception)
     514             :         {
     515             :                 errorString = [exception reason];
     516             :                 contents = nil;
     517             :         }
     518             :         
     519             :         if (errorString != nil)
     520             :         {
     521             :                 OOLog(@"dataCache.badData", @"Could not read data cache: %@", errorString);
     522             : #if OOLITE_RELEASE_PLIST_ERROR_STRINGS
     523             :                 [errorString release];
     524             : #endif
     525             :                 return nil;
     526             :         }
     527             :         if (![contents isKindOfClass:[NSDictionary class]])  return nil;
     528             :         
     529             :         return contents;
     530             : }
     531             : 
     532             : 
     533             : - (BOOL)writeDict:(NSDictionary *)inDict
     534             : {
     535             :         NSString                        *path = nil;
     536             :         NSData                          *plist = nil;
     537             :         NSString                        *errorDesc = nil;
     538             :         
     539             :         path = [self cachePathCreatingIfNecessary:YES];
     540             :         if (path == nil) return NO;     
     541             :         
     542             : #if PROFILE_WRITES
     543             :         OOProfilingStopwatch *stopwatch = [OOProfilingStopwatch stopwatch];
     544             : #endif
     545             :         
     546             :         plist = [NSPropertyListSerialization dataFromPropertyList:inDict format:CACHE_PLIST_FORMAT errorDescription:&errorDesc];
     547             :         if (plist == nil)
     548             :         {
     549             : #if OOLITE_RELEASE_PLIST_ERROR_STRINGS
     550             :                 [errorDesc autorelease];
     551             : #endif
     552             :                 OOLog(kOOLogDataCacheSerializationError, @"Could not convert data cache to property list data: %@", errorDesc);
     553             :                 return NO;
     554             :         }
     555             :         
     556             : #if PROFILE_WRITES
     557             :         OOTimeDelta serializeT = [stopwatch reset];
     558             : #endif
     559             :         
     560             :         BOOL result = [plist writeToFile:path atomically:NO];
     561             :         
     562             : #if PROFILE_WRITES
     563             :         OOTimeDelta writeT = [stopwatch reset];
     564             :         
     565             :         OOLog(@"dataCache.profile", @"Time to serialize cache: %g seconds. Time to write data: %g seconds.", serializeT, writeT);
     566             : #endif
     567             :         
     568             : #if WRITE_ASYNC
     569             :         DESTROY(_scheduledWrite);
     570             : #endif
     571             :         return result;
     572             : }
     573             : 
     574             : 
     575             : - (void)buildCachesFromDictionary:(NSDictionary *)inDict
     576             : {
     577             :         NSEnumerator                            *keyEnum = nil;
     578             :         id                                                      key = nil;
     579             :         id                                                      value = nil;
     580             :         NSMutableDictionary                     *cache = nil;
     581             :         
     582             :         if (inDict == nil ) return;
     583             :         
     584             :         [_caches release];
     585             :         _caches = [[NSMutableDictionary alloc] initWithCapacity:[inDict count]];
     586             :         
     587             :         for (keyEnum = [inDict keyEnumerator]; (key = [keyEnum nextObject]); )
     588             :         {
     589             :                 value = [inDict oo_dictionaryForKey:key];
     590             :                 if (value != nil)
     591             :                 {
     592             :                         cache = [NSMutableDictionary dictionaryWithDictionary:value];
     593             :                         if (cache != nil)
     594             :                         {
     595             :                                 [_caches setObject:cache forKey:key];
     596             :                         }
     597             :                 }
     598             :         }
     599             : }
     600             : 
     601             : 
     602             : - (NSDictionary *)dictionaryOfCaches
     603             : {
     604             :         return [OODeepCopy(_caches) autorelease];
     605             : }
     606             : 
     607             : 
     608             : - (BOOL)directoryExists:(NSString *)inPath create:(BOOL)inCreate
     609             : {
     610             :         BOOL                            exists, directory;
     611             :         NSFileManager           *fmgr =  [NSFileManager defaultManager];
     612             :         
     613             :         exists = [fmgr fileExistsAtPath:inPath isDirectory:&directory];
     614             :         
     615             :         if (exists && !directory)
     616             :         {
     617             :                 OOLog(kOOLogDataCacheBuildPathError, @"Expected %@ to be a folder, but it is a file.", inPath);
     618             :                 return NO;
     619             :         }
     620             :         if (!exists)
     621             :         {
     622             :                 if (!inCreate) return NO;
     623             :                 if (![fmgr oo_createDirectoryAtPath:inPath attributes:nil])
     624             :                 {
     625             :                         OOLog(kOOLogDataCacheBuildPathError, @"Could not create folder %@.", inPath);
     626             :                         return NO;
     627             :                 }
     628             :         }
     629             :         
     630             :         return YES;
     631             : }
     632             : 
     633             : 
     634             : #if OOLITE_MAC_OS_X
     635             : 
     636             : - (NSString *)cachePathCreatingIfNecessary:(BOOL)create
     637           0 : {
     638             :         NSString *cachePath = [self cacheDirectoryPathCreatingIfNecessary:create];
     639             :         return [cachePath stringByAppendingPathComponent:@"Data Cache.plist"];
     640             : }
     641             : 
     642             : #else
     643             : 
     644             : - (NSString *)cachePathCreatingIfNecessary:(BOOL)create
     645             : {
     646             :         NSString *cachePath = [self cacheDirectoryPathCreatingIfNecessary:create];
     647             :         return [cachePath stringByAppendingPathComponent:@"Oolite-cache.plist"];
     648             : }
     649             : 
     650             : #endif
     651             : 
     652             : @end
     653             : 
     654             : 
     655             : @implementation OOCacheManager (Singleton)
     656             : 
     657             : /*      Canonical singleton boilerplate.
     658             :         See Cocoa Fundamentals Guide: Creating a Singleton Instance.
     659             :         See also +sharedCache above.
     660             :         
     661             :         NOTE: assumes single-threaded access.
     662             : */
     663             : 
     664             : + (id)allocWithZone:(NSZone *)inZone
     665           0 : {
     666             :         if (sSingleton == nil)
     667             :         {
     668             :                 sSingleton = [super allocWithZone:inZone];
     669             :                 return sSingleton;
     670             :         }
     671             :         return nil;
     672             : }
     673             : 
     674             : 
     675             : - (id)copyWithZone:(NSZone *)inZone
     676           0 : {
     677             :         return self;
     678             : }
     679             : 
     680             : 
     681             : - (id)retain
     682           0 : {
     683             :         return self;
     684             : }
     685             : 
     686             : 
     687             : - (NSUInteger)retainCount
     688           0 : {
     689             :         return UINT_MAX;
     690             : }
     691             : 
     692             : 
     693             : - (void)release
     694           0 : {}
     695             : 
     696             : 
     697             : - (id)autorelease
     698           0 : {
     699             :         return self;
     700             : }
     701             : 
     702             : @end
     703             : 
     704             : 
     705             : #if WRITE_ASYNC
     706             : @implementation OOAsyncCacheWriter
     707             : 
     708             : - (id) initWithCacheContents:(NSDictionary *)cacheContents
     709             : {
     710             :         if ((self = [super init]))
     711             :         {
     712             :                 _cacheContents = [cacheContents copy];
     713             :                 if (_cacheContents == nil)
     714             :                 {
     715             :                         [self release];
     716             :                         self = nil;
     717             :                 }
     718             :         }
     719             :         
     720             :         return self;
     721             : }
     722             : 
     723             : 
     724             : - (void) dealloc
     725           0 : {
     726             :         DESTROY(_cacheContents);
     727             :         
     728             :         [super dealloc];
     729             : }
     730             : 
     731             : 
     732             : - (void) performAsyncTask
     733           0 : {
     734             :         if ([[OOCacheManager sharedCache] writeDict:_cacheContents])
     735             :         {
     736             :                 OOLog(kOOLogDataCacheWriteSuccess, @"%@", @"Wrote data cache.");
     737             :         }
     738             :         else
     739             :         {
     740             :                 OOLog(kOOLogDataCacheWriteFailed, @"%@", @"Failed to write data cache.");
     741             :         }
     742             :         DESTROY(_cacheContents);
     743             : }
     744             : 
     745             : 
     746             : - (void) completeAsyncTask
     747           0 : {
     748             :         // Don't need to do anything, but this needs to be here so we can wait on it.
     749             : }
     750             : 
     751             : @end
     752             : #endif  // WRITE_ASYNC

Generated by: LCOV version 1.14