PageRenderTime 41ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/src/Three20UINavigator/Sources/TTURLMap.m

https://github.com/GetMoPix/three20
Objective C | 537 lines | 337 code | 115 blank | 85 comment | 61 complexity | c650a05cba1d58aa115c966cc421438f MD5 | raw file
  1. //
  2. // Copyright 2009-2011 Facebook
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. //
  16. #import "Three20UINavigator/TTURLMap.h"
  17. // UINavigator
  18. #import "Three20UINavigator/TTURLNavigatorPattern.h"
  19. #import "Three20UINavigator/TTURLGeneratorPattern.h"
  20. // UINavigator (private)
  21. #import "Three20UINavigator/private/UIViewController+TTNavigatorGarbageCollection.h"
  22. // Core
  23. #import "Three20Core/TTGlobalCore.h"
  24. #import "Three20Core/TTCorePreprocessorMacros.h"
  25. #import <objc/runtime.h>
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. ///////////////////////////////////////////////////////////////////////////////////////////////////
  28. ///////////////////////////////////////////////////////////////////////////////////////////////////
  29. @implementation TTURLMap
  30. ///////////////////////////////////////////////////////////////////////////////////////////////////
  31. - (void)dealloc {
  32. TT_RELEASE_SAFELY(_objectMappings);
  33. TT_RELEASE_SAFELY(_objectPatterns);
  34. TT_RELEASE_SAFELY(_fragmentPatterns);
  35. TT_RELEASE_SAFELY(_stringPatterns);
  36. TT_RELEASE_SAFELY(_schemes);
  37. TT_RELEASE_SAFELY(_defaultObjectPattern);
  38. [super dealloc];
  39. }
  40. ///////////////////////////////////////////////////////////////////////////////////////////////////
  41. ///////////////////////////////////////////////////////////////////////////////////////////////////
  42. #pragma mark -
  43. #pragma mark Private
  44. ///////////////////////////////////////////////////////////////////////////////////////////////////
  45. /**
  46. * @return a unique key for a class with a given name.
  47. * @private
  48. */
  49. - (NSString*)keyForClass:(Class)cls withName:(NSString*)name {
  50. const char* className = class_getName(cls);
  51. return [NSString stringWithFormat:@"%s_%@", className, (nil != name) ? name : @""];
  52. }
  53. ///////////////////////////////////////////////////////////////////////////////////////////////////
  54. /**
  55. * What's a scheme?
  56. * It's a specific URL that is registered with the URL map.
  57. * Example:
  58. * @"tt://some/path"
  59. *
  60. * This method registers them.
  61. *
  62. * @private
  63. */
  64. - (void)registerScheme:(NSString*)scheme {
  65. if (nil != scheme) {
  66. if (nil == _schemes) {
  67. _schemes = [[NSMutableDictionary alloc] init];
  68. }
  69. [_schemes setObject:[NSNull null] forKey:scheme];
  70. }
  71. }
  72. ///////////////////////////////////////////////////////////////////////////////////////////////////
  73. - (void)addObjectPattern: (TTURLNavigatorPattern*)pattern
  74. forURL: (NSString*)URL {
  75. pattern.URL = URL;
  76. [pattern compile];
  77. [self registerScheme:pattern.scheme];
  78. if (pattern.isUniversal) {
  79. [_defaultObjectPattern release];
  80. _defaultObjectPattern = [pattern retain];
  81. } else if (pattern.isFragment) {
  82. if (!_fragmentPatterns) {
  83. _fragmentPatterns = [[NSMutableArray alloc] init];
  84. }
  85. [_fragmentPatterns addObject:pattern];
  86. } else {
  87. _invalidPatterns = YES;
  88. if (!_objectPatterns) {
  89. _objectPatterns = [[NSMutableArray alloc] init];
  90. }
  91. [_objectPatterns addObject:pattern];
  92. }
  93. }
  94. ///////////////////////////////////////////////////////////////////////////////////////////////////
  95. - (void)addStringPattern: (TTURLGeneratorPattern*)pattern
  96. forURL: (NSString*)URL
  97. withName: (NSString*)name {
  98. pattern.URL = URL;
  99. [pattern compile];
  100. [self registerScheme:pattern.scheme];
  101. if (!_stringPatterns) {
  102. _stringPatterns = [[NSMutableDictionary alloc] init];
  103. }
  104. NSString* key = [self keyForClass:pattern.targetClass withName:name];
  105. [_stringPatterns setObject:pattern forKey:key];
  106. }
  107. ///////////////////////////////////////////////////////////////////////////////////////////////////
  108. - (TTURLNavigatorPattern*)matchObjectPattern:(NSURL*)URL {
  109. if (_invalidPatterns) {
  110. [_objectPatterns sortUsingSelector:@selector(compareSpecificity:)];
  111. _invalidPatterns = NO;
  112. }
  113. for (TTURLNavigatorPattern* pattern in _objectPatterns) {
  114. if ([pattern matchURL:URL]) {
  115. return pattern;
  116. }
  117. }
  118. return _defaultObjectPattern;
  119. }
  120. ///////////////////////////////////////////////////////////////////////////////////////////////////
  121. - (BOOL)isWebURL:(NSURL*)URL {
  122. return [URL.scheme caseInsensitiveCompare:@"http"] == NSOrderedSame
  123. || [URL.scheme caseInsensitiveCompare:@"https"] == NSOrderedSame
  124. || [URL.scheme caseInsensitiveCompare:@"ftp"] == NSOrderedSame
  125. || [URL.scheme caseInsensitiveCompare:@"ftps"] == NSOrderedSame
  126. || [URL.scheme caseInsensitiveCompare:@"data"] == NSOrderedSame
  127. || [URL.scheme caseInsensitiveCompare:@"file"] == NSOrderedSame;
  128. }
  129. ///////////////////////////////////////////////////////////////////////////////////////////////////
  130. - (BOOL)isExternalURL:(NSURL*)URL {
  131. if ([URL.host isEqualToString:@"maps.google.com"]
  132. || [URL.host isEqualToString:@"itunes.apple.com"]
  133. || [URL.host isEqualToString:@"phobos.apple.com"]) {
  134. return YES;
  135. } else {
  136. return NO;
  137. }
  138. }
  139. ///////////////////////////////////////////////////////////////////////////////////////////////////
  140. ///////////////////////////////////////////////////////////////////////////////////////////////////
  141. #pragma mark -
  142. #pragma mark Mapping
  143. ///////////////////////////////////////////////////////////////////////////////////////////////////
  144. - (void)from:(NSString*)URL toObject:(id)target {
  145. TTURLNavigatorPattern* pattern = [[TTURLNavigatorPattern alloc] initWithTarget:target];
  146. [self addObjectPattern:pattern forURL:URL];
  147. [pattern release];
  148. }
  149. ///////////////////////////////////////////////////////////////////////////////////////////////////
  150. - (void)from:(NSString*)URL toObject:(id)target selector:(SEL)selector {
  151. TTURLNavigatorPattern* pattern = [[TTURLNavigatorPattern alloc] initWithTarget:target];
  152. pattern.selector = selector;
  153. [self addObjectPattern:pattern forURL:URL];
  154. [pattern release];
  155. }
  156. ///////////////////////////////////////////////////////////////////////////////////////////////////
  157. - (void)from:(NSString*)URL toViewController:(id)target {
  158. TTURLNavigatorPattern* pattern = [[TTURLNavigatorPattern alloc] initWithTarget:target
  159. mode:TTNavigationModeCreate];
  160. [self addObjectPattern:pattern forURL:URL];
  161. [pattern release];
  162. }
  163. ///////////////////////////////////////////////////////////////////////////////////////////////////
  164. - (void)from:(NSString*)URL toViewController:(id)target selector:(SEL)selector {
  165. TTURLNavigatorPattern* pattern = [[TTURLNavigatorPattern alloc] initWithTarget:target
  166. mode:TTNavigationModeCreate];
  167. pattern.selector = selector;
  168. [self addObjectPattern:pattern forURL:URL];
  169. [pattern release];
  170. }
  171. ///////////////////////////////////////////////////////////////////////////////////////////////////
  172. - (void)from:(NSString*)URL toViewController:(id)target transition:(NSInteger)transition {
  173. TTURLNavigatorPattern* pattern = [[TTURLNavigatorPattern alloc] initWithTarget:target
  174. mode:TTNavigationModeCreate];
  175. pattern.transition = transition;
  176. [self addObjectPattern:pattern forURL:URL];
  177. [pattern release];
  178. }
  179. ///////////////////////////////////////////////////////////////////////////////////////////////////
  180. - (void)from:(NSString*)URL parent:(NSString*)parentURL
  181. toViewController:(id)target selector:(SEL)selector transition:(NSInteger)transition {
  182. TTURLNavigatorPattern* pattern = [[TTURLNavigatorPattern alloc] initWithTarget:target
  183. mode:TTNavigationModeCreate];
  184. pattern.parentURL = parentURL;
  185. pattern.selector = selector;
  186. pattern.transition = transition;
  187. [self addObjectPattern:pattern forURL:URL];
  188. [pattern release];
  189. }
  190. ///////////////////////////////////////////////////////////////////////////////////////////////////
  191. - (void)from:(NSString*)URL toSharedViewController:(id)target {
  192. TTURLNavigatorPattern* pattern = [[TTURLNavigatorPattern alloc] initWithTarget:target
  193. mode:TTNavigationModeShare];
  194. [self addObjectPattern:pattern forURL:URL];
  195. [pattern release];
  196. }
  197. ///////////////////////////////////////////////////////////////////////////////////////////////////
  198. - (void)from:(NSString*)URL toSharedViewController:(id)target selector:(SEL)selector {
  199. TTURLNavigatorPattern* pattern = [[TTURLNavigatorPattern alloc] initWithTarget:target
  200. mode:TTNavigationModeShare];
  201. pattern.selector = selector;
  202. [self addObjectPattern:pattern forURL:URL];
  203. [pattern release];
  204. }
  205. ///////////////////////////////////////////////////////////////////////////////////////////////////
  206. - (void)from:(NSString*)URL parent:(NSString*)parentURL
  207. toSharedViewController:(id)target {
  208. TTURLNavigatorPattern* pattern = [[TTURLNavigatorPattern alloc] initWithTarget:target
  209. mode:TTNavigationModeShare];
  210. pattern.parentURL = parentURL;
  211. [self addObjectPattern:pattern forURL:URL];
  212. [pattern release];
  213. }
  214. ///////////////////////////////////////////////////////////////////////////////////////////////////
  215. - (void)from:(NSString*)URL parent:(NSString*)parentURL
  216. toSharedViewController:(id)target selector:(SEL)selector {
  217. TTURLNavigatorPattern* pattern = [[TTURLNavigatorPattern alloc] initWithTarget:target
  218. mode:TTNavigationModeShare];
  219. pattern.parentURL = parentURL;
  220. pattern.selector = selector;
  221. [self addObjectPattern:pattern forURL:URL];
  222. [pattern release];
  223. }
  224. ///////////////////////////////////////////////////////////////////////////////////////////////////
  225. - (void)from:(NSString*)URL toModalViewController:(id)target {
  226. TTURLNavigatorPattern* pattern = [[TTURLNavigatorPattern alloc] initWithTarget:target
  227. mode:TTNavigationModeModal];
  228. [self addObjectPattern:pattern forURL:URL];
  229. [pattern release];
  230. }
  231. ///////////////////////////////////////////////////////////////////////////////////////////////////
  232. - (void)from:(NSString*)URL toModalViewController:(id)target selector:(SEL)selector {
  233. TTURLNavigatorPattern* pattern = [[TTURLNavigatorPattern alloc] initWithTarget:target
  234. mode:TTNavigationModeModal];
  235. pattern.selector = selector;
  236. [self addObjectPattern:pattern forURL:URL];
  237. [pattern release];
  238. }
  239. ///////////////////////////////////////////////////////////////////////////////////////////////////
  240. - (void)from:(NSString*)URL toModalViewController:(id)target transition:(NSInteger)transition {
  241. TTURLNavigatorPattern* pattern = [[TTURLNavigatorPattern alloc] initWithTarget:target
  242. mode:TTNavigationModeModal];
  243. pattern.transition = transition;
  244. [self addObjectPattern:pattern forURL:URL];
  245. [pattern release];
  246. }
  247. ///////////////////////////////////////////////////////////////////////////////////////////////////
  248. - (void)from:(NSString*)URL parent:(NSString*)parentURL
  249. toModalViewController:(id)target selector:(SEL)selector transition:(NSInteger)transition {
  250. TTURLNavigatorPattern* pattern = [[TTURLNavigatorPattern alloc] initWithTarget:target
  251. mode:TTNavigationModeModal];
  252. pattern.parentURL = parentURL;
  253. pattern.selector = selector;
  254. pattern.transition = transition;
  255. [self addObjectPattern:pattern forURL:URL];
  256. [pattern release];
  257. }
  258. ///////////////////////////////////////////////////////////////////////////////////////////////////
  259. - (void)from:(NSString*)URL toPopoverViewController:(id)target {
  260. TTURLNavigatorPattern* pattern =
  261. [[TTURLNavigatorPattern alloc] initWithTarget: target
  262. mode: TTNavigationModePopover];
  263. [self addObjectPattern:pattern forURL:URL];
  264. [pattern release];
  265. }
  266. ///////////////////////////////////////////////////////////////////////////////////////////////////
  267. - (void)from:(NSString*)URL toPopoverViewController:(id)target selector:(SEL)selector {
  268. TTURLNavigatorPattern* pattern =
  269. [[TTURLNavigatorPattern alloc] initWithTarget:target
  270. mode:TTNavigationModePopover];
  271. pattern.selector = selector;
  272. [self addObjectPattern:pattern forURL:URL];
  273. [pattern release];
  274. }
  275. ///////////////////////////////////////////////////////////////////////////////////////////////////
  276. - (void)from:(Class)cls toURL:(NSString*)URL {
  277. TTURLGeneratorPattern* pattern = [[TTURLGeneratorPattern alloc] initWithTargetClass:cls];
  278. [self addStringPattern:pattern forURL:URL withName:nil];
  279. [pattern release];
  280. }
  281. ///////////////////////////////////////////////////////////////////////////////////////////////////
  282. - (void)from:(Class)cls name:(NSString*)name toURL:(NSString*)URL {
  283. TTURLGeneratorPattern* pattern = [[TTURLGeneratorPattern alloc] initWithTargetClass:cls];
  284. [self addStringPattern:pattern forURL:URL withName:name];
  285. [pattern release];
  286. }
  287. ///////////////////////////////////////////////////////////////////////////////////////////////////
  288. ///////////////////////////////////////////////////////////////////////////////////////////////////
  289. #pragma mark -
  290. #pragma mark Public
  291. ///////////////////////////////////////////////////////////////////////////////////////////////////
  292. - (void)setObject:(id)object forURL:(NSString*)URL {
  293. if (nil == _objectMappings) {
  294. _objectMappings = TTCreateNonRetainingDictionary();
  295. }
  296. // XXXjoe Normalize the URL first
  297. [_objectMappings setObject:object forKey:URL];
  298. if ([object isKindOfClass:[UIViewController class]]) {
  299. [UIViewController ttAddNavigatorController:object];
  300. }
  301. }
  302. ///////////////////////////////////////////////////////////////////////////////////////////////////
  303. - (void)removeURL:(NSString*)URL {
  304. [_objectMappings removeObjectForKey:URL];
  305. for (TTURLNavigatorPattern* pattern in _objectPatterns) {
  306. if ([URL isEqualToString:pattern.URL]) {
  307. [_objectPatterns removeObject:pattern];
  308. break;
  309. }
  310. }
  311. }
  312. ///////////////////////////////////////////////////////////////////////////////////////////////////
  313. - (void)removeObject:(id)object {
  314. // XXXjoe IMPLEMENT ME
  315. }
  316. ///////////////////////////////////////////////////////////////////////////////////////////////////
  317. - (void)removeObjectForURL:(NSString*)URL {
  318. [_objectMappings removeObjectForKey:URL];
  319. }
  320. ///////////////////////////////////////////////////////////////////////////////////////////////////
  321. - (void)removeAllObjects {
  322. TT_RELEASE_SAFELY(_objectMappings);
  323. }
  324. ///////////////////////////////////////////////////////////////////////////////////////////////////
  325. - (id)objectForURL:(NSString*)URL {
  326. return [self objectForURL:URL query:nil pattern:nil];
  327. }
  328. ///////////////////////////////////////////////////////////////////////////////////////////////////
  329. - (id)objectForURL:(NSString*)URL query:(NSDictionary*)query {
  330. return [self objectForURL:URL query:query pattern:nil];
  331. }
  332. ///////////////////////////////////////////////////////////////////////////////////////////////////
  333. - (id)objectForURL: (NSString*)URL
  334. query: (NSDictionary*)query
  335. pattern: (TTURLNavigatorPattern**)outPattern {
  336. id object = nil;
  337. if (_objectMappings) {
  338. object = [_objectMappings objectForKey:URL];
  339. if (object && !outPattern) {
  340. return object;
  341. }
  342. }
  343. NSURL* theURL = [NSURL URLWithString:URL];
  344. TTURLNavigatorPattern* pattern = [self matchObjectPattern:theURL];
  345. if (pattern) {
  346. if (!object) {
  347. object = [pattern createObjectFromURL:theURL query:query];
  348. }
  349. if (pattern.navigationMode == TTNavigationModeShare && object) {
  350. [self setObject:object forURL:URL];
  351. }
  352. if (outPattern) {
  353. *outPattern = pattern;
  354. }
  355. return object;
  356. } else {
  357. return nil;
  358. }
  359. }
  360. ///////////////////////////////////////////////////////////////////////////////////////////////////
  361. - (id)dispatchURL:(NSString*)URL toTarget:(id)target query:(NSDictionary*)query {
  362. NSURL* theURL = [NSURL URLWithString:URL];
  363. for (TTURLNavigatorPattern* pattern in _fragmentPatterns) {
  364. if ([pattern matchURL:theURL]) {
  365. return [pattern invoke:target withURL:theURL query:query];
  366. }
  367. }
  368. // If there is no match, check if the fragment points to a method on the target
  369. if (theURL.fragment) {
  370. SEL selector = NSSelectorFromString(theURL.fragment);
  371. if (selector && [target respondsToSelector:selector]) {
  372. [target performSelector:selector];
  373. }
  374. }
  375. return nil;
  376. }
  377. ///////////////////////////////////////////////////////////////////////////////////////////////////
  378. - (TTNavigationMode)navigationModeForURL:(NSString*)URL {
  379. NSURL* theURL = [NSURL URLWithString:URL];
  380. if (![self isAppURL:theURL]) {
  381. TTURLNavigatorPattern* pattern = [self matchObjectPattern:theURL];
  382. if (pattern) {
  383. return pattern.navigationMode;
  384. }
  385. }
  386. return TTNavigationModeExternal;
  387. }
  388. ///////////////////////////////////////////////////////////////////////////////////////////////////
  389. - (NSInteger)transitionForURL:(NSString*)URL {
  390. TTURLNavigatorPattern* pattern = [self matchObjectPattern:[NSURL URLWithString:URL]];
  391. return pattern.transition;
  392. }
  393. ///////////////////////////////////////////////////////////////////////////////////////////////////
  394. - (BOOL)isSchemeSupported:(NSString*)scheme {
  395. return nil != scheme && !![_schemes objectForKey:scheme];
  396. }
  397. ///////////////////////////////////////////////////////////////////////////////////////////////////
  398. - (BOOL)isAppURL:(NSURL*)URL {
  399. return [self isExternalURL:URL]
  400. || ([[UIApplication sharedApplication] canOpenURL:URL]
  401. && ![self isSchemeSupported:URL.scheme]
  402. && ![self isWebURL:URL]);
  403. }
  404. ///////////////////////////////////////////////////////////////////////////////////////////////////
  405. - (NSString*)URLForObject:(id)object {
  406. return [self URLForObject:object withName:nil];
  407. }
  408. ///////////////////////////////////////////////////////////////////////////////////////////////////
  409. - (NSString*)URLForObject:(id)object withName:(NSString*)name {
  410. Class cls = [object class] == object ? object : [object class];
  411. while (cls) {
  412. NSString* key = [self keyForClass:cls withName:name];
  413. TTURLGeneratorPattern* pattern = [_stringPatterns objectForKey:key];
  414. if (pattern) {
  415. return [pattern generateURLFromObject:object];
  416. } else {
  417. cls = class_getSuperclass(cls);
  418. }
  419. }
  420. return nil;
  421. }
  422. @end