PageRenderTime 50ms CodeModel.GetById 24ms app.highlight 23ms RepoModel.GetById 1ms app.codeStats 0ms

/MapView/Map/RMTileCache.m

http://github.com/route-me/route-me
Objective C | 217 lines | 143 code | 40 blank | 34 comment | 33 complexity | 1a0ecc356806087fc1fd92fefc67cad7 MD5 | raw file
  1//
  2//  RMTileCache.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 "RMTileCache.h"
 29
 30#import "RMMemoryCache.h"
 31#import "RMDatabaseCache.h"
 32
 33#import "RMConfiguration.h"
 34
 35#import "RMTileSource.h"
 36
 37
 38@interface RMTileCache ( Configuration ) 
 39
 40- (id<RMTileCache>) newMemoryCacheWithConfig: (NSDictionary*) cfg;
 41- (id<RMTileCache>) newDatabaseCacheWithConfig: (NSDictionary*) cfg tileSource: (id<RMTileSource>) tileSource;
 42
 43@end
 44
 45
 46@implementation RMTileCache
 47
 48-(id)initWithTileSource: (id<RMTileSource>) tileSource
 49{
 50	if (![super init])
 51		return nil;
 52	
 53	caches = [[NSMutableArray alloc] init];
 54
 55	id cacheCfg = [[RMConfiguration configuration] cacheConfiguration];
 56	
 57	if (cacheCfg==nil)
 58	{
 59		cacheCfg = [NSArray arrayWithObjects:
 60					/// \bug magic string literals
 61			[NSDictionary dictionaryWithObject: @"memory-cache" forKey: @"type"],
 62			[NSDictionary dictionaryWithObject: @"db-cache"     forKey: @"type"],
 63			nil
 64		];
 65	}
 66
 67	for (id cfg in cacheCfg) 
 68	{
 69		id<RMTileCache> newCache = nil;
 70				
 71		@try {
 72			NSString* type = [cfg valueForKey:@"type"];
 73			
 74			/// \bug magic string literals
 75			if ([@"memory-cache" isEqualToString: type]) 
 76				newCache = [self newMemoryCacheWithConfig: cfg];
 77
 78			if ([@"db-cache" isEqualToString: type]) 
 79				newCache = [self newDatabaseCacheWithConfig: cfg tileSource: tileSource];				
 80
 81			if (newCache) {
 82				[caches addObject: newCache];
 83				[newCache release];
 84			} else {
 85				RMLog(@"failed to create cache of type %@", type);
 86			}
 87		}
 88		@catch (NSException * e) {
 89			RMLog(@"*** configuration error: %@", [e reason]);
 90		}
 91				
 92	}
 93	return self;
 94}
 95
 96-(void) dealloc
 97{
 98	[caches release];
 99	[super dealloc];
100}
101
102-(void)addCache: (id<RMTileCache>)cache
103{
104	[caches addObject:cache];
105}
106
107+(NSNumber*) tileHash: (RMTile)tile
108{
109	return [NSNumber numberWithUnsignedLongLong: RMTileKey(tile)];
110}
111
112// Returns the cached image if it exists. nil otherwise.
113-(RMTileImage*) cachedImage:(RMTile)tile
114{
115	for (id<RMTileCache> cache in caches)
116	{
117		RMTileImage *image = [cache cachedImage:tile];
118		if (image != nil)
119			return image;
120	}
121	
122	return nil;
123}
124
125-(void)addTile: (RMTile)tile WithImage: (RMTileImage*)image
126{
127	for (id<RMTileCache> cache in caches)
128	{	
129		if ([cache respondsToSelector:@selector(addTile:WithImage:)])
130		{
131			[cache addTile:tile WithImage:image];
132		}
133	}
134}
135
136-(void)didReceiveMemoryWarning
137{
138	LogMethod();		
139	for (id<RMTileCache> cache in caches)
140	{
141		[cache didReceiveMemoryWarning];
142	}
143}
144
145-(void) removeAllCachedImages
146{
147	for (id<RMTileCache> cache in caches)
148	{
149		[cache removeAllCachedImages];
150	}
151}
152@end
153
154@implementation RMTileCache ( Configuration )
155
156/// \bug magic numbers and strings
157- (id<RMTileCache>) newMemoryCacheWithConfig: (NSDictionary*) cfg
158{
159	NSNumber* capacity = [cfg objectForKey:@"capacity"];
160	if (capacity == nil) capacity = [NSNumber numberWithInt: 32];
161	return [[RMMemoryCache alloc] initWithCapacity: [capacity intValue]];	
162}
163
164/// \bug magic numbers and strings
165- (id<RMTileCache>) newDatabaseCacheWithConfig: (NSDictionary*) cfg tileSource: (id<RMTileSource>) theTileSource
166{
167	BOOL useCacheDir = NO;
168	RMCachePurgeStrategy strategy = RMCachePurgeStrategyFIFO;
169	/// \bug magic numbers
170	NSUInteger capacity = 1000;
171	NSUInteger minimalPurge = capacity / 10;
172	
173	NSNumber* capacityNumber = [cfg objectForKey:@"capacity"];
174	if (capacityNumber!=nil) {
175		NSInteger value = [capacityNumber intValue];
176		
177		// 0 is valid: it means no capacity limit
178		if (value >= 0) {
179			capacity =  value;
180			minimalPurge = MAX(1,capacity / 10);
181		} else 
182			RMLog(@"illegal value for capacity: %d", value);
183	}
184	
185	NSString* strategyStr = [cfg objectForKey:@"strategy"];
186	if (strategyStr != nil) {
187		if ([strategyStr caseInsensitiveCompare:@"FIFO"] == NSOrderedSame) strategy = RMCachePurgeStrategyFIFO;
188		if ([strategyStr caseInsensitiveCompare:@"LRU"] == NSOrderedSame) strategy = RMCachePurgeStrategyLRU;
189	}
190	
191	/// \bug magic string literals
192	NSNumber* useCacheDirNumber = [cfg objectForKey:@"useCachesDirectory"];
193	if (useCacheDirNumber!=nil) useCacheDir =  [useCacheDirNumber boolValue];
194	
195	NSNumber* minimalPurgeNumber = [cfg objectForKey:@"minimalPurge"];
196	if (minimalPurgeNumber != nil && capacity != 0) {
197		NSUInteger value = [minimalPurgeNumber unsignedIntValue];
198		if (value > 0 && value<=capacity) 
199			minimalPurge = value;
200		else {
201			RMLog(@"minimalPurge must be at least one and at most the cache capacity");
202		}
203	}
204	
205	RMDatabaseCache* dbCache = [[RMDatabaseCache alloc] 
206								initWithTileSource: theTileSource 
207								usingCacheDir: useCacheDir
208								];
209	
210	[dbCache setCapacity: capacity];
211	[dbCache setPurgeStrategy: strategy];
212	[dbCache setMinimalPurge: minimalPurge];
213	
214	return dbCache;
215}
216
217@end