PageRenderTime 49ms CodeModel.GetById 14ms app.highlight 31ms RepoModel.GetById 1ms app.codeStats 0ms

/MapView/Map/RMDatabaseCache.m

http://github.com/route-me/route-me
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