/MapView/Map/RMDatabaseCache.m
Objective C | 190 lines | 118 code | 38 blank | 34 comment | 16 complexity | 2cb7331854a4efed86c4af32203d69b6 MD5 | raw file
1// 2// RMDatabaseCache.m 3// 4// Copyright (c) 2008-2009, Route-Me Contributors 5// All rights reserved. 6// 7// Redistribution and use in source and binary forms, with or without 8// modification, are permitted provided that the following conditions are met: 9// 10// * Redistributions of source code must retain the above copyright notice, this 11// list of conditions and the following disclaimer. 12// * Redistributions in binary form must reproduce the above copyright notice, 13// this list of conditions and the following disclaimer in the documentation 14// and/or other materials provided with the distribution. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26// POSSIBILITY OF SUCH DAMAGE. 27 28#import "RMDatabaseCache.h" 29#import "RMTileCacheDAO.h" 30#import "RMTileImage.h" 31#import "RMTile.h" 32 33@implementation RMDatabaseCache 34 35@synthesize databasePath; 36 37+ (NSString*)dbPathForTileSource: (id<RMTileSource>) source usingCacheDir: (BOOL) useCacheDir 38{ 39 NSArray *paths; 40 41 if (useCacheDir) { 42 paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); 43 } else { 44 paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 45 } 46 47 if ([paths count] > 0) // Should only be one... 48 { 49 NSString *cachePath = [paths objectAtIndex:0]; 50 51 // check for existence of cache directory 52 if ( ![[NSFileManager defaultManager] fileExistsAtPath: cachePath]) 53 { 54 // create a new cache directory 55 [[NSFileManager defaultManager] createDirectoryAtPath:cachePath withIntermediateDirectories:NO attributes:nil error:nil]; 56 } 57 58 /// \bug magic string literals 59 NSString *filename = [NSString stringWithFormat:@"Map%@.sqlite", [source uniqueTilecacheKey]]; 60 return [cachePath stringByAppendingPathComponent:filename]; 61 } 62 return nil; 63} 64 65-(id) initWithDatabase: (NSString*)path 66{ 67 if (![super init]) 68 return nil; 69 70 71 self.databasePath = path; 72 dao = [[RMTileCacheDAO alloc] initWithDatabase:path]; 73 74 if (dao == nil) 75 return nil; 76 77 return self; 78} 79 80-(id) initWithTileSource: (id<RMTileSource>) source usingCacheDir: (BOOL) useCacheDir 81{ 82 return [self initWithDatabase:[RMDatabaseCache dbPathForTileSource:source usingCacheDir: useCacheDir]]; 83} 84 85-(void) dealloc 86{ 87 [[NSNotificationCenter defaultCenter] removeObserver:self]; 88 [databasePath release]; 89 [dao release]; 90 91 [super dealloc]; 92} 93 94-(void) setPurgeStrategy: (RMCachePurgeStrategy) theStrategy 95{ 96 purgeStrategy = theStrategy; 97} 98 99-(void) setCapacity: (NSUInteger) theCapacity 100{ 101 capacity = theCapacity; 102} 103 104-(void) setMinimalPurge: (NSUInteger) theMinimalPurge 105{ 106 minimalPurge = theMinimalPurge; 107} 108 109-(void)addTile: (RMTile)tile WithImage: (RMTileImage*)image 110{ 111 // The tile probably hasn't loaded any data yet... we must be patient. 112 113 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(addImageData:) name:RMMapImageLoadedNotification object:image]; 114} 115 116-(void) addImageData: (NSNotification *)notification 117{ 118 NSData *data = [[notification userInfo] objectForKey:@"data"]; 119 /// \bug magic string literals 120 RMTileImage *image = (RMTileImage*)[notification object]; 121 122 @synchronized (self) { 123 124 if (capacity != 0) { 125 NSUInteger tilesInDb = [dao count]; 126 if (capacity <= tilesInDb) { 127 [dao purgeTiles: MAX(minimalPurge, 1+tilesInDb-capacity)]; 128 } 129 } 130 NSDate *lastUsedTime = [[image lastUsedTime] retain]; 131 [dao addData:data LastUsed: lastUsedTime ForTile:RMTileKey([image tile])]; 132 [lastUsedTime release]; lastUsedTime = nil; 133 } 134 135 [[NSNotificationCenter defaultCenter] removeObserver:self 136 name:RMMapImageLoadedNotification 137 object:image]; 138 139 140// RMLog(@"%d items in DB", [dao count]); 141} 142 143-(RMTileImage*) cachedImage:(RMTile)tile 144{ 145// RMLog(@"Looking for cached image in DB"); 146 147 NSData *data = nil; 148 149 @synchronized (self) { 150 151 data = [dao dataForTile:RMTileKey(tile)]; 152 if (data == nil) 153 return nil; 154 155 if (capacity != 0 && purgeStrategy == RMCachePurgeStrategyLRU) { 156 [dao touchTile: RMTileKey(tile) withDate: [NSDate date]]; 157 } 158 159 } 160 161 RMTileImage *image = [RMTileImage imageForTile:tile withData:data]; 162// RMLog(@"DB cache hit for tile %d %d %d", tile.x, tile.y, tile.zoom); 163 return image; 164} 165 166-(void) purgeTilesFromBefore: (NSDate*) date 167{ 168 @synchronized(self) 169 { 170 [dao purgeTilesFromBefore:date]; 171 } 172} 173 174-(void)didReceiveMemoryWarning 175{ 176 @synchronized(self) 177 { 178 [dao didReceiveMemoryWarning]; 179 } 180} 181 182-(void) removeAllCachedImages 183{ 184 @synchronized(self) 185 { 186 [dao removeAllCachedImages]; 187 } 188} 189 190@end