1/* 2 Copyright (C) 2000-2005 SKYRIX Software AG 3 4 This file is part of SOPE. 5 6 SOPE is free software; you can redistribute it and/or modify it under 7 the terms of the GNU Lesser General Public License as published by the 8 Free Software Foundation; either version 2, or (at your option) any 9 later version. 10 11 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY 12 WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 14 License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with SOPE; see the file COPYING. If not, write to the 18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 19 02111-1307, USA. 20*/ 21 22// ATTENTION: this class is for OGo legacy, so that WO compatibility changes 23// to WOResourceManager do not break OGo. 24// So: do not use that class, its DEPRECATED! 25 26#include <NGObjWeb/OWResourceManager.h> 27#include <NGObjWeb/WOComponentDefinition.h> 28#include "WOComponent+private.h" 29#include <NGObjWeb/WORequest.h> 30#include <NGObjWeb/WOApplication.h> 31#include "common.h" 32#import <Foundation/NSNull.h> 33#include "_WOStringTable.h" 34 35/* 36 Component Discovery and Page Creation 37 38 All WO code uses either directly or indirectly the OWResourceManager's 39 -pageWithName:languages: method to instantiate WO components. 40 41 This methods works in three steps: 42 43 1. discovery of files associated with the component 44 - (WOComponentDefinition *)definitionForComponent:(id)_name 45 inFramework:(NSString *)_framework 46 languages:(NSArray *)_languages 47 48 2. creation of a proper WOComponentDefinition, which is some kind 49 of 'blueprint' or 'class' for components 50 51 3. component instantiation using the definition 52 53 All the instantiation/setup work is done by a component definition, the 54 resource manager is only responsible for managing those 'blueprint' 55 resources. 56 57 If you want to customize component creation, you can supply your 58 own WOComponentDefinition in a subclass of OWResourceManager by 59 overriding: 60 - (WOComponentDefinition *)definitionForComponent:(id)_name 61 inFramework:(NSString *)_frameworkName 62 languages:(NSArray *)_languages 63 64 Notably in WO 5.3 the WOResourceManager doesn't seem to handle components 65 anymore. 66*/ 67 68/* 69 Note: this was #if !COMPILE_FOR_GSTEP_MAKE - but there is no difference 70 between Xcode and gstep-make?! 71 The only possible difference might be that .wo wrappers are directly 72 in the bundle/framework root - but this doesn't relate to Resources. 73 74 OK, this breaks gstep-make based template lookup which places .wo 75 wrappers in .woa/Resources/xxx.wo. 76 This is an issue because .wox are looked up in Contents/Resources 77 but .wo ones in just Resources. 78 79 This issue should be fixed in recent woapp-gs.make ... 80*/ 81#if COCOA_Foundation_LIBRARY || NeXT_Foundation_LIBRARY 82# define RSRCDIR_CONTENTS 1 83#endif 84 85@implementation OWResourceManager 86 87static NSFileManager *fm = nil; 88static Class UrlClass = Nil; 89static NSString *resourcePrefix = @""; 90static NSString *rapidTurnAroundPath = nil; 91static NSNull *null = nil; 92static BOOL debugOn = NO; 93static BOOL debugComponentLookup = NO; 94static BOOL debugResourceLookup = NO; 95static BOOL genMissingResourceLinks = NO; 96 97+ (void)initialize { 98 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; 99 static BOOL isInitialized = NO; 100 NSDictionary *defs; 101 if (isInitialized) return; 102 isInitialized = YES; 103 104 fm = [[NSFileManager defaultManager] retain]; 105 null = [[NSNull null] retain]; 106 UrlClass = [NSURL class]; 107 108 defs = [NSDictionary dictionaryWithObjectsAndKeys: 109 [NSArray arrayWithObject:@"wo"], 110 @"WOComponentExtensions", 111 nil]; 112 [ud registerDefaults:defs]; 113 debugOn = [WOApplication isDebuggingEnabled]; 114 debugComponentLookup = [ud boolForKey:@"WODebugComponentLookup"]; 115 debugResourceLookup = [ud boolForKey:@"WODebugResourceLookup"]; 116 genMissingResourceLinks = [ud boolForKey:@"WOGenerateMissingResourceLinks"]; 117 rapidTurnAroundPath = [[ud stringForKey:@"WOProjectDirectory"] copy]; 118} 119 120static inline BOOL 121_pathExists(OWResourceManager *self, NSFileManager *fm, NSString *path) 122{ 123 BOOL doesExist; 124 125 if (self->existingPathes && (path != nil)) { 126 int i; 127 128 i = (int)(long)NSMapGet(self->existingPathes, path); 129 if (i == 0) { 130 doesExist = [fm fileExistsAtPath:path]; 131 NSMapInsert(self->existingPathes, path, (void*)(doesExist ? 1L : 0xFFL)); 132 } 133 else 134 doesExist = i == 1 ? YES : NO; 135 } 136 else 137 doesExist = [fm fileExistsAtPath:path]; 138 return doesExist; 139} 140 141+ (void)setResourcePrefix:(NSString *)_prefix { 142 [resourcePrefix autorelease]; 143 resourcePrefix = [_prefix copy]; 144} 145 146- (id)initWithPath:(NSString *)_path { 147 if ((self = [super init])) { 148 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; 149 NSString *rprefix = nil; 150 NSString *tmp; 151 152 self->componentDefinitions = 153 NSCreateMapTable(NSObjectMapKeyCallBacks, 154 NSObjectMapValueCallBacks, 155 128); 156 self->stringTables = 157 NSCreateMapTable(NSObjectMapKeyCallBacks, 158 NSObjectMapValueCallBacks, 159 16); 160 161 tmp = [_path stringByStandardizingPath]; 162 if (tmp) _path = tmp; 163 164 self->base = [_path copy]; 165 166 if ([WOApplication isCachingEnabled]) { 167 self->existingPathes = NSCreateMapTable(NSObjectMapKeyCallBacks, 168 NSIntMapValueCallBacks, 169 256); 170 } 171 172 rprefix = [ud stringForKey:@"WOResourcePrefix"]; 173 if (rprefix) [[self class] setResourcePrefix:rprefix]; 174 } 175 return self; 176} 177- (id)init { 178 return [self initWithPath:[[NGBundle mainBundle] bundlePath]]; 179} 180 181- (void)dealloc { 182 if (self->existingPathes) NSFreeMapTable(self->existingPathes); 183 if (self->stringTables) NSFreeMapTable(self->stringTables); 184 if (self->componentDefinitions) NSFreeMapTable(self->componentDefinitions); 185 if (self->keyedResources) NSFreeMapTable(self->keyedResources); 186 [self->w3resources release]; 187 [self->resources release]; 188 [self->base release]; 189 [super dealloc]; 190} 191 192/* debugging */ 193 194- (BOOL)isDebuggingEnabled { 195 return debugOn; 196} 197 198/* path methods */ 199 200- (NSFileManager *)fileManager { 201 static NSFileManager *fm = nil; 202 if (fm == nil) 203 fm = [[NSFileManager defaultManager] retain]; 204 return fm; 205} 206 207- (NSString *)basePath { 208 return self->base; 209} 210 211- (NSString *)resourcesPath { 212 NSFileManager *fm; 213 214 if (self->resources) 215 return self->resources; 216 217 fm = [self fileManager]; 218 if ([self->base length] > 0) { 219 if (![fm fileExistsAtPath:self->base]) { 220 [self warnWithFormat:@"(%s): Resources base path '%@' does not exist !", 221 __PRETTY_FUNCTION__, self->base]; 222 return nil; 223 } 224 } 225 226#if RSRCDIR_CONTENTS 227 if ([rapidTurnAroundPath length] > 0) { 228 /* 229 In rapid turnaround mode, first check for a Resources subdir in the 230 project directory, then directly in the project dir. 231 Note: you cannot have both! Either put stuff in a Resources subdir *or* 232 in the project dir. 233 */ 234 NSString *tmp; 235 BOOL isDir; 236 237 tmp = [rapidTurnAroundPath stringByAppendingPathComponent:@"Resources"]; 238 if (![fm fileExistsAtPath:tmp isDirectory:&isDir]) 239 isDir = NO; 240 if (!isDir) 241 tmp = rapidTurnAroundPath; 242 243 self->resources = [tmp copy]; 244 } 245 else { 246 self->resources = 247 [[[self->base stringByAppendingPathComponent:@"Contents"] 248 stringByAppendingPathComponent:@"Resources"] 249 copy]; 250 } 251#else 252 self->resources = 253 [[self->base stringByAppendingPathComponent:@"Resources"] copy]; 254#endif 255 256 if ([self->resources length] > 0) { 257 if (![fm fileExistsAtPath:self->resources]) { 258 [self warnWithFormat: 259 @"(%s): Resources path %@ does not exist !", 260 __PRETTY_FUNCTION__, self->resources]; 261 [self->resources release]; self->resources = nil; 262 } 263 else if (self->existingPathes && (self->resources != nil)) 264 NSMapInsert(self->existingPathes, self->resources, (void*)1); 265 } 266 return self->resources; 267} 268 269- (NSString *)resourcesPathForFramework:(NSString *)_fw { 270 if (_fw == nil) 271 return [self resourcesPath]; 272 273#if RSRCDIR_CONTENTS 274 return [[_fw stringByAppendingPathComponent:@"Contents"] 275 stringByAppendingPathComponent:@"Resources"]; 276#else 277 return [_fw stringByAppendingPathComponent:@"Resources"]; 278#endif 279} 280 281- (NSString *)webServerResourcesPath { 282 NSFileManager *fm; 283 284 if (self->w3resources) 285 return self->w3resources; 286 287#if GNUSTEP_BASE_LIBRARY && 0 288 self->w3resources = 289 [[self->base stringByAppendingPathComponent:@"Resources/WebServer"] copy]; 290#else 291 self->w3resources = 292 [[self->base stringByAppendingPathComponent:@"WebServerResources"] copy]; 293#endif 294 295 fm = [self fileManager]; 296 if ([self->w3resources length] == 0) 297 return nil; 298 299 if (![fm fileExistsAtPath:self->w3resources]) { 300 static BOOL didLog = NO; 301 if (!didLog) { 302 didLog = YES; 303 [self warnWithFormat: 304 @"(%s): WebServerResources path '%@' does not exist !", 305 __PRETTY_FUNCTION__, self->w3resources]; 306 } 307 [self->w3resources release]; self->w3resources = nil; 308 } 309 else if (self->existingPathes && (self->w3resources != nil)) 310 NSMapInsert(self->existingPathes, self->w3resources, (void*)1); 311 312 if (debugResourceLookup) 313 [self logWithFormat:@"WebServerResources: '%@'", self->w3resources]; 314 return self->w3resources; 315} 316 317- (NSString *)_lookupResourceNamed:(NSString *)_name inPath:(NSString *)_path 318 inFramework:(NSString *)_frameworkName 319 languages:(NSArray *)_languages 320{ 321 unsigned i, langCount; 322 NSString *resource; 323 324 if (_path == nil) 325 return nil; 326 327 // first check Language.lproj in WebServerResources 328 for (i = 0, langCount = [_languages count]; i < langCount; i++) { 329 NSString *langPath; 330 331 langPath = [_languages objectAtIndex:i]; 332 langPath = [langPath stringByAppendingPathExtension:@"lproj"]; 333 langPath = [_path stringByAppendingPathComponent:langPath]; 334 335 if (!_pathExists(self, fm, langPath)) { 336 if (debugResourceLookup) { 337 [self logWithFormat: 338 @" no language lproj for '%@' in path: %@", 339 [_languages objectAtIndex:i], _path]; 340 } 341 continue; 342 } 343 344 resource = [langPath stringByAppendingPathComponent:_name]; 345 346 if (_pathExists(self, fm, resource)) { 347 if (debugResourceLookup) 348 [self logWithFormat:@" found path: %@", resource]; 349 return resource; 350 } 351 else if (debugResourceLookup) 352 [self logWithFormat:@" not found in path: %@", resource]; 353 } 354 355 /* next check in resources path (WebServerResources or Resources) itself */ 356 resource = [_path stringByAppendingPathComponent:_name]; 357 if (debugResourceLookup) 358 [self logWithFormat:@" check for flat path: %@", resource]; 359 if (_pathExists(self, fm, resource)) 360 return resource; 361 362 return nil; 363} 364 365- (BOOL)shouldLookupResourceInWebServerResources:(NSString *)_name { 366 if ([_name hasSuffix:@".wox"]) return NO; 367 if ([_name hasSuffix:@".wo"]) return NO; 368 return YES; 369} 370 371- (NSString *)pathForResourceNamed:(NSString *)_name 372 inFramework:(NSString *)_frameworkName 373 languages:(NSArray *)_languages 374{ 375 /* 376 Note: at least in the case of OGo component lookups the framework name is 377 properly filled with the OGo bundle path on lookup, so no 378 NGBundleManager query is necessary. 379 */ 380 NSFileManager *fm; 381 NSString *resource = nil; 382 383 if (debugResourceLookup) { 384 [self logWithFormat:@"lookup '%@' bundle=%@ languages=%@", 385 _name, _frameworkName, [_languages componentsJoinedByString:@","]]; 386 } 387 388 fm = [self fileManager]; 389 390 /* now check in webserver resources path */ 391 392 if ([self shouldLookupResourceInWebServerResources:_name]) { 393 resource = [self _lookupResourceNamed:_name 394 inPath:[self webServerResourcesPath] 395 inFramework:_frameworkName languages:_languages]; 396 if (resource != nil) return resource; 397 } 398 399 /* now check in regular resources path */ 400 401 resource = [self _lookupResourceNamed:_name 402 inPath:[self resourcesPathForFramework:_frameworkName] 403 inFramework:_frameworkName languages:_languages]; 404 if (resource != nil) return resource; 405 406 /* and last check in the application directory */ 407 if (_pathExists(self, fm, self->base)) { 408 resource = [self->base stringByAppendingPathComponent:_name]; 409 if (_pathExists(self, fm, resource)) 410 return resource; 411 } 412 return nil; 413} 414 415- (NSString *)pathForResourceNamed:(NSString *)_name { 416 IS_DEPRECATED; 417 return [self pathForResourceNamed:_name inFramework:nil languages:nil]; 418} 419 420- (NSString *)pathForResourceNamed:(NSString *)_name ofType:(NSString *)_type { 421 _name = [_name stringByAppendingPathExtension:_type]; 422 return [self pathForResourceNamed:_name]; 423} 424 425/* URL methods */ 426 427- (NSString *)urlForResourceNamed:(NSString *)_name 428 inFramework:(NSString *)_frameworkName 429 languages:(NSArray *)_languages 430 request:(WORequest *)_request 431{ 432 WOApplication *app; 433 NSString *resource = nil, *tmp; 434 435 app = [WOApplication application]; 436 437 if (_languages == nil) 438 _languages = [_request browserLanguages]; 439 440 resource = [self pathForResourceNamed:_name 441 inFramework:_frameworkName 442 languages:_languages]; 443#if RSRCDIR_CONTENTS 444 if ([resource rangeOfString:@"/Contents/"].length > 0) { 445 resource = [resource stringByReplacingString:@"/Contents" 446 withString:@""]; 447 } 448#endif 449#if 0 450 tmp = [resource stringByStandardizingPath]; 451 if (tmp) resource = tmp; 452#endif 453 454 if (resource) { 455 NSString *path = nil, *sbase; 456 unsigned len; 457 458 sbase = self->base; 459 tmp = [sbase commonPrefixWithString:resource options:0]; 460 461 len = [tmp length]; 462 path = [sbase substringFromIndex:len]; 463 tmp = [resource substringFromIndex:len]; 464 if (([path length] > 0) && ![tmp hasPrefix:@"/"] && ![tmp hasPrefix:@"\\"]) 465 path = [path stringByAppendingString:@"/"]; 466 path = [path stringByAppendingString:tmp]; 467 468#ifdef __WIN32__ 469 { 470 NSArray *cs; 471 cs = [path componentsSeparatedByString:@"\\"]; 472 path = [cs componentsJoinedByString:@"/"]; 473 } 474#endif 475 476 if (path) { 477 static NSString *suffix = nil; 478 NSMutableString *url = nil; 479 480 if (suffix == nil) { 481 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; 482 suffix = [ud stringForKey:@"WOApplicationSuffix"]; 483 } 484 485 url = [[NSMutableString alloc] initWithCapacity:256]; 486#if 0 487 [url appendString:[_request adaptorPrefix]]; 488#endif 489 if (resourcePrefix) 490 [url appendString:resourcePrefix]; 491 if (![url hasSuffix:@"/"]) [url appendString:@"/"]; 492 [url appendString:app ? [app name] : [_request applicationName]]; 493 [url appendString:suffix]; 494 if (![path hasPrefix:@"/"]) [url appendString:@"/"]; 495 [url appendString:path]; 496 497 path = [url copy]; 498 [url release]; 499 500 return [path autorelease]; 501 } 502 } 503 504 if (genMissingResourceLinks) { 505 return [NSString stringWithFormat: 506 @"/missingresource?name=%@&application=%@", 507 _name, app ? [app name] : [_request applicationName]]; 508 } 509 return nil; 510} 511 512- (NSString *)urlForResourceNamed:(NSString *)_name { 513 IS_DEPRECATED; 514 return [self urlForResourceNamed:_name 515 inFramework:nil 516 languages:nil 517 request:nil]; 518} 519- (NSString *)urlForResourceNamed:(NSString *)_name ofType:(NSString *)_type { 520 return [self urlForResourceNamed: 521 [_name stringByAppendingPathExtension:_type]]; 522} 523 524/* string tables */ 525 526- (NSString *)stringForKey:(NSString *)_key 527 inTableNamed:(NSString *)_tableName 528 withDefaultValue:(NSString *)_defaultValue 529 inFramework:(NSString *)_framework 530 languages:(NSArray *)_languages; 531{ 532 NSFileManager *fm; 533 _WOStringTable *table = nil; 534 NSString *path = nil; 535 536 fm = [self fileManager]; 537 538 if (_tableName == nil) 539 _tableName = @"Localizable"; 540 541 /* take a look whether a matching table is already loaded */ 542 543 path = [_tableName stringByAppendingPathExtension:@"strings"]; 544 path = [self pathForResourceNamed:path inFramework:_framework 545 languages:_languages]; 546 547 if (path != nil) { 548 if ((table = NSMapGet(self->stringTables, path)) == NULL) { 549 if ([fm fileExistsAtPath:path]) { 550 table = [_WOStringTable allocWithZone:[self zone]]; /* for gcc */ 551 table = [table initWithPath:path]; 552 NSMapInsert(self->stringTables, path, table); 553 [table release]; 554 } 555 } 556 if (table != nil) 557 return [table stringForKey:_key withDefaultValue:_defaultValue]; 558 } 559 /* didn't found table in cache */ 560 561 return _defaultValue; 562} 563 564- (NSString *)stringForKey:(NSString *)_key 565 inTableNamed:(NSString *)_tableName 566 withDefaultValue:(NSString *)_default 567 languages:(NSArray *)_languages 568{ 569 return [self stringForKey:_key inTableNamed:_tableName 570 withDefaultValue:_default 571 inFramework:nil 572 languages:_languages]; 573} 574 575 576/* NSLocking */ 577 578- (void)lock { 579} 580- (void)unlock { 581} 582 583/* component definitions */ 584 585- (NSString *)pathToComponentNamed:(NSString *)_name 586 inFramework:(NSString *)_framework 587 languages:(NSArray *)_langs 588{ 589 /* search for component wrapper .. */ 590 // TODO: shouldn't we use that for WOx as well? 591 NSEnumerator *e; 592 NSString *ext; 593 594 if (_name == nil) { 595#if DEBUG 596 [self warnWithFormat:@"(%s): tried to get path to component with " 597 @"<nil> name !", 598 __PRETTY_FUNCTION__]; 599#endif 600 return nil; 601 } 602 603 /* scan for name.$ext resource ... */ 604 e = [[[NSUserDefaults standardUserDefaults] 605 arrayForKey:@"WOComponentExtensions"] 606 objectEnumerator]; 607 608 while ((ext = [e nextObject])) { 609 NSString *specName; 610 NSString *path; 611 612 specName = [_name stringByAppendingPathExtension:ext]; 613 614 path = [self pathForResourceNamed:specName 615 inFramework:_framework 616 languages:_langs]; 617 if (path != nil) return path; 618 } 619 return nil; 620} 621 622- (NSString *)pathToComponentNamed:(NSString *)_name 623 inFramework:(NSString *)_fw 624{ 625 // TODO: is this still used somewhere? 626 return [self pathToComponentNamed:_name inFramework:_fw languages:nil]; 627} 628 629- (WOComponentDefinition *)_definitionForPathlessComponent:(NSString *)_name 630 languages:(NSArray *)_languages 631{ 632 /* definition factory */ 633 WOComponentDefinition *cdef; 634 635 cdef = [[WOComponentDefinition allocWithZone:[self zone]] 636 initWithName:_name 637 path:nil 638 baseURL:nil 639 frameworkName:nil]; 640 641 return [cdef autorelease]; 642} 643 644- (WOComponentDefinition *)_definitionWithName:(NSString *)_name 645 url:(NSURL *)_url 646 baseURL:(NSURL *)_baseURL 647 frameworkName:(NSString *)_fwname 648{ 649 /* definition factory */ 650 static Class DefClass; 651 id cdef; 652 653 if (DefClass == Nil) 654 DefClass = [WOComponentDefinition class]; 655 656 cdef = [[DefClass alloc] initWithName:_name 657 path:[_url path] 658 baseURL:_baseURL frameworkName:_fwname]; 659 return cdef; 660} 661- (WOComponentDefinition *)_definitionWithName:(NSString *)_name 662 path:(NSString *)_path 663 baseURL:(NSURL *)_baseURL 664 frameworkName:(NSString *)_fwname 665{ 666 NSURL *url; 667 668 url = ([_path length] > 0) 669 ? [[[NSURL alloc] initFileURLWithPath:_path] autorelease] 670 : nil; 671 672 return [self _definitionWithName:_name url:url 673 baseURL:_baseURL frameworkName:_fwname]; 674} 675 676- (WOComponentDefinition *)_cachedDefinitionForComponent:(id)_name 677 languages:(NSArray *)_languages 678{ 679 NSArray *cacheKey; 680 id cdef; 681 682 if (self->componentDefinitions == NULL) 683 return nil; 684 if (![[WOApplication application] isCachingEnabled]) 685 return nil; 686 687 cacheKey = [NSArray arrayWithObjects:_name, _languages, nil]; 688 cdef = NSMapGet(self->componentDefinitions, cacheKey); 689 690 return cdef; 691} 692- (WOComponentDefinition *)_cacheDefinition:(id)_cdef 693 forComponent:(id)_name 694 languages:(NSArray *)_languages 695{ 696 NSArray *cacheKey; 697 698 if (self->componentDefinitions == NULL) 699 return _cdef; 700 if (![[WOApplication application] isCachingEnabled]) 701 return _cdef; 702 703 cacheKey = [NSArray arrayWithObjects:_name, _languages, nil]; 704 NSMapInsert(self->componentDefinitions, cacheKey, _cdef ? _cdef : (id)null); 705 706 return _cdef; 707} 708 709- (NSString *)resourceNameForComponentNamed:(NSString *)_name { 710 return [_name stringByAppendingPathExtension:@"wox"]; 711} 712 713- (BOOL)_isValidWrapperDirectory:(NSString *)_path 714 containingTemplate:(NSString *)_name 715{ 716 /* 717 Check whether this actually does contain a template! 718 719 This is new and hopefully doesn't break anything, but as far as I can 720 see checking for Component.html inside should do the right thing (unless 721 there are template wrappers which are not .wo wrappers ;-) 722 */ 723 NSString *htmlPath; 724 725 htmlPath = [_name stringByAppendingPathExtension:@"html"]; 726 htmlPath = [_path stringByAppendingPathComponent:htmlPath]; 727 return [[self fileManager] fileExistsAtPath:htmlPath]; 728} 729 730- (WOComponentDefinition *)_processWrapperLanguageProjects:(NSString *)_name 731 componentPath:(NSString *)componentPath 732 languages:(NSArray *)_langs 733{ 734 /* 735 this looks for language projects contained in template wrapper 736 directories, eg "Main.wo/English.lproj/" 737 */ 738 WOComponentDefinition *cdef = nil; 739 NSFileManager *fm = nil; 740 NSEnumerator *languages; 741 NSString *language; 742 NSString *sname; 743 BOOL doesCache; 744 745 if ([_langs count] == 0) 746 return nil; 747 748 doesCache = [[WOApplication application] isCachingEnabled]; 749 fm = [self fileManager]; 750 sname = [_name stringByAppendingString:@"\t"]; 751 752 languages = [_langs objectEnumerator]; 753 while ((language = [languages nextObject])) { 754 NSString *compoundKey = nil; 755 NSString *languagePath = nil; 756 BOOL isDirectory = NO; 757 NSString *baseUrl = nil; 758 759 // [self logWithFormat:@"check %@ / %@", _name, language]; 760 761 compoundKey = [sname stringByAppendingString:language]; 762 if (doesCache) { 763 cdef = NSMapGet(self->componentDefinitions, compoundKey); 764 765 if (cdef == (id)null) 766 /* resource does not exist */ 767 continue; 768 769 [cdef touch]; 770 if (cdef) return cdef; // found definition in cache 771 } 772 773 /* take a look into the file system */ 774 languagePath = [language stringByAppendingPathExtension:@"lproj"]; 775 languagePath = [componentPath stringByAppendingPathComponent:languagePath]; 776 777 if (![fm fileExistsAtPath:languagePath isDirectory:&isDirectory]) { 778 if (doesCache) { 779 // register null in cache, so that we know it's non-existent 780 NSMapInsert(self->componentDefinitions, compoundKey, null); 781 } 782 continue; 783 } 784 785 if (!isDirectory) { 786 [self warnWithFormat:@"(%s): language entry %@ is not a directory !", 787 __PRETTY_FUNCTION__, languagePath]; 788 if (doesCache && (compoundKey != nil)) { 789 // register null in cache, so that we know it's non-existent 790 NSMapInsert(self->componentDefinitions, compoundKey, null); 791 } 792 continue; 793 } 794 795 /* 796 Now check whether this actually does contain a template! 797 798 This is new and hopefully doesn't break anything, but as far as I can 799 see checking for Component.html inside should do the right thing (unless 800 there are template wrappers which are not .wo wrappers ;-) 801 */ 802 if (![self _isValidWrapperDirectory:languagePath 803 containingTemplate:_name]){ 804 [self debugWithFormat:@"no HTML template for inside lproj '%@': '%@'", 805 _name, languagePath]; 806 if (doesCache && (compoundKey != nil)) { 807 // register null in cache, so that we know it's non-existent 808 NSMapInsert(self->componentDefinitions, compoundKey, null); 809 } 810 continue; 811 } 812 813 /* construct the base URL */ 814 815 baseUrl = [[[WOApplication application] baseURL] absoluteString]; 816 baseUrl = [NSString stringWithFormat:@"%@/%@.lproj/%@.wo", 817 baseUrl, language, _name]; 818 819 /* create WOComponentDefinition object */ 820 821 cdef = [self _definitionWithName:_name 822 path:languagePath 823 baseURL:[NSURL URLWithString:baseUrl] 824 frameworkName:nil]; 825 if (cdef == nil) { 826 [self warnWithFormat:@"(%s): could not load component definition of " 827 @"'%@' from language project: %@", 828 __PRETTY_FUNCTION__, _name, languagePath]; 829 if (doesCache && (compoundKey != nil)) { 830 // register null in cache, so that we know it's non-existent 831 NSMapInsert(self->componentDefinitions, compoundKey, null); 832 } 833 continue; 834 } 835 836 if (doesCache && (compoundKey != nil)) { 837 // register in cache 838 NSMapInsert(self->componentDefinitions, compoundKey, cdef); 839 [cdef release]; 840 } 841 else { 842 // don't register in cache 843 cdef = [cdef autorelease]; 844 } 845 846 return cdef; 847 } 848 849 return nil; /* no lproj containing templates was found */ 850} 851 852- (NSString *)defaultFrameworkForComponentNamed:(NSString *)_name { 853 Class clazz; 854 855 if (rapidTurnAroundPath != nil) 856 return rapidTurnAroundPath; 857 858 if ((clazz = NSClassFromString(_name)) == nil) 859 return nil; 860 861 return [[NSBundle bundleForClass:clazz] bundlePath]; 862} 863 864- (WOComponentDefinition *)definitionForComponent:(id)_name 865 inFramework:(NSString *)_framework 866 languages:(NSArray *)_languages 867{ 868 /* this is the primary method for finding a definition */ 869 // TODO: this method is too large 870 WOApplication *app; 871 NSFileManager *fm = nil; 872 WOComponentDefinition *cdef = nil; 873 NSURL *componentURL; 874 BOOL doesCache, isDir; 875 876 app = [WOApplication application]; 877 doesCache = [app isCachingEnabled]; 878 879 /* lookup component path */ 880 881 if ([_name isKindOfClass:UrlClass]) { 882 componentURL = _name; 883 _name = [componentURL path]; 884 if (debugComponentLookup) { 885 [self debugWithFormat:@"using URL %@ for component %@", 886 componentURL, _name]; 887 } 888 } 889 else { 890 NSString *path; 891 892 /* 893 Note: this is a bit of a hack ..., actually this method should never 894 be called without a framework and pages shouldn't be instantiated 895 without specifying their framework. 896 But for legacy reasons this needs to be done and seems to work 897 without problems. It is required for loading components from 898 bundles. 899 */ 900 if (_framework == nil && _name != nil) 901 _framework = [self defaultFrameworkForComponentNamed:_name]; 902 903 if (debugComponentLookup) { 904 [self logWithFormat:@"lookup: component '%@' in framework '%@'", 905 _name, _framework]; 906 } 907 908 /* look for .wox component */ 909 910 // TODO: why don't we use -pathForComponentNamed: here? 911 path = [self pathForResourceNamed: 912 [self resourceNameForComponentNamed:_name] 913 inFramework:_framework 914 languages:_languages]; 915 916 if (debugComponentLookup) 917 [self logWithFormat:@"lookup: path-to-resource: '%@'", path]; 918 919 /* look for .wo component */ 920 921 if ([path length] == 0) { 922 path = [self pathToComponentNamed:_name 923 inFramework:_framework 924 languages:_languages]; 925 if (debugComponentLookup) 926 [self logWithFormat:@"lookup: path-to-component: '%@'", path]; 927 } 928 929 /* make URL from path */ 930 931 componentURL = ([path length] > 0) 932 ? [[[UrlClass alloc] initFileURLWithPath:path] autorelease] 933 : nil; 934 } 935 936 if (debugComponentLookup) { 937 [self logWithFormat:@" component='%@' in framework='%@'", 938 _name, _framework]; 939 [self logWithFormat:@" => '%@'", [componentURL absoluteString]]; 940 } 941 942 /* check whether it's a 'template-less' component ... */ 943 944 if (componentURL == nil) { 945 /* did not find component wrapper ! */ 946 [app debugWithFormat:@" component '%@' has no template !", _name]; 947 948 cdef = [self _definitionForPathlessComponent:_name languages:_languages]; 949 return cdef; 950 } 951 952 fm = [self fileManager]; 953 954 /* ensure that the component exists */ 955 956 isDir = NO; 957 if ([componentURL isFileURL]) { 958 NSString *componentPath; 959 960 componentPath = [componentURL path]; 961 962 if (![fm fileExistsAtPath:componentPath isDirectory:&isDir]) { 963 [[WOApplication application] 964 debugWithFormat: 965 @"%s: did not find component '%@' at path '%@' !", 966 __PRETTY_FUNCTION__, 967 _name, componentPath]; 968 return nil; 969 } 970 971 /* if the component spec is a directory (eg a .wo), scan lproj's inside */ 972 if (isDir && [_languages count] > 0) { 973 if (debugComponentLookup) { 974 [self logWithFormat:@" check wrapper languages (%d)", 975 [_languages count]]; 976 } 977 cdef = [self _processWrapperLanguageProjects:_name 978 componentPath:componentPath 979 languages:_languages]; 980 if (cdef != nil) { 981 if (debugComponentLookup) 982 [self logWithFormat:@" => FOUND: %@", cdef]; 983 return cdef; 984 } 985 else if (debugComponentLookup) 986 [self logWithFormat:@" ... no language template found ..."]; 987 } 988 } 989 990 /* look flat */ 991 992 if (doesCache) { 993 cdef = NSMapGet(self->componentDefinitions, componentURL); 994 if (cdef == (id)null) 995 /* resource does not exist */ 996 return nil; 997 [cdef touch]; 998 999 if (cdef != nil) return cdef; // found definition in cache 1000 } 1001 1002 /* 1003 in case the "componentURL" is a directory, check whether it contains 1004 an HTML file 1005 */ 1006 if (isDir) { 1007 if (![self _isValidWrapperDirectory:[componentURL path] 1008 containingTemplate:_name]) { 1009 if (debugComponentLookup) 1010 [self logWithFormat:@" not a valid wrapper '%@': '%@'", 1011 _name, [componentURL absoluteString]]; 1012 if (doesCache) { 1013 /* register null in cache, so that we know it's non-existent */ 1014 NSMapInsert(self->componentDefinitions, componentURL, null); 1015 } 1016 return nil; 1017 } 1018 } 1019 1020 /* take a look into the file system */ 1021 { 1022 NSString *baseUrl = nil; 1023 1024 baseUrl = [NSString stringWithFormat:@"%@/%@", 1025 [[app baseURL] absoluteString], 1026 [_name lastPathComponent]]; 1027 1028 cdef = [self _definitionWithName:_name 1029 url:componentURL 1030 baseURL:[NSURL URLWithString:baseUrl] 1031 frameworkName:nil]; 1032 if (cdef == nil) { 1033 [self warnWithFormat:@"(%s): could not load component definition of " 1034 @"'%@' from component wrapper: '%@'", 1035 __PRETTY_FUNCTION__, _name, componentURL]; 1036 if (doesCache) { 1037 /* register null in cache, so that we know it's non-existent */ 1038 NSMapInsert(self->componentDefinitions, componentURL, null); 1039 } 1040 return nil; 1041 } 1042 1043 if (doesCache) { 1044 /* register in cache */ 1045 NSMapInsert(self->componentDefinitions, componentURL, cdef); 1046 [cdef release]; 1047 } 1048 else 1049 /* don't register in cache, does not cache */ 1050 cdef = [cdef autorelease]; 1051 1052 return cdef; 1053 } 1054 1055 /* did not find component */ 1056 return nil; 1057} 1058- (WOComponentDefinition *)definitionForComponent:(id)_name 1059 languages:(NSArray *)_langs 1060{ 1061 /* Note: the framework will be determined base on the class '_name' */ 1062 return [self definitionForComponent:_name inFramework:nil languages:_langs]; 1063} 1064 1065/* caching */ 1066 1067- (WOComponentDefinition *)__definitionForComponent:(id)_name 1068 languages:(NSArray *)_languages 1069{ 1070 // TODO: this should add the framework parameter and maybe a context 1071 WOComponentDefinition *cdef; 1072 1073 /* look into cache */ 1074 1075 cdef = [self _cachedDefinitionForComponent:_name languages:_languages]; 1076 if (cdef != nil) { 1077 if (cdef == (id)null) 1078 /* component does not exist */ 1079 return nil; 1080 1081 if ([cdef respondsToSelector:@selector(touch)]) 1082 [cdef touch]; 1083 return cdef; 1084 } 1085 1086 /* not cached, create a definition */ 1087 1088 cdef = [self definitionForComponent:_name languages:_languages]; 1089 1090 /* cache created definition */ 1091 1092 return [self _cacheDefinition:cdef forComponent:_name languages:_languages]; 1093} 1094 1095/* primary call-in's */ 1096 1097- (WOElement *)templateWithName:(NSString *)_name 1098 languages:(NSArray *)_languages 1099{ 1100 WOComponentDefinition *cdef; 1101 1102 cdef = [self __definitionForComponent:_name languages:_languages]; 1103 if (cdef == nil) return nil; 1104 1105 return (WOElement *)[cdef template]; 1106} 1107 1108- (id)pageWithName:(NSString *)_name languages:(NSArray *)_languages { 1109 /* 1110 TODO: this appears to be deprecated since the WOComponent initializer 1111 is now -initWithContext: and we have no context here ... 1112 Also misses the framework? 1113 */ 1114 NSAutoreleasePool *pool = nil; 1115 WOComponentDefinition *cdef = nil; 1116 WOComponent *component = nil; 1117 1118 pool = [[NSAutoreleasePool alloc] init]; 1119 { 1120 cdef = [self __definitionForComponent:_name languages:_languages]; 1121 if (cdef != nil) { 1122 component = 1123 [cdef instantiateWithResourceManager:(WOResourceManager *)self 1124 languages:_languages]; 1125 component = [component retain]; 1126 } 1127 } 1128 [pool release]; 1129 1130 return [component autorelease]; 1131} 1132 1133/* KeyedData */ 1134 1135- (void)setData:(NSData *)_data 1136 forKey:(NSString *)_key 1137 mimeType:(NSString *)_type 1138 session:(WOSession *)_session 1139{ 1140 if ((_key == nil) || (_data == nil)) 1141 return; 1142 if (_type == nil) 1143 _type = @"application/octet-stream"; 1144 1145 [self lock]; 1146 1147 if (self->keyedResources == NULL) { 1148 self->keyedResources = NSCreateMapTable(NSObjectMapKeyCallBacks, 1149 NSObjectMapValueCallBacks, 1150 128); 1151 } 1152 1153 NSMapInsert(self->keyedResources, 1154 _key, 1155 [NSDictionary dictionaryWithObjectsAndKeys: 1156 _type, @"mimeType", 1157 _key, @"key", 1158 _data, @"data", 1159 nil]); 1160 1161 [self unlock]; 1162} 1163 1164- (id)_dataForKey:(NSString *)_key sessionID:(NSString *)_sid { 1165 id tmp; 1166 1167 [self lock]; 1168 1169 if (self->keyedResources) 1170 tmp = NSMapGet(self->keyedResources, _key); 1171 else 1172 tmp = nil; 1173 1174 tmp = [[tmp retain] autorelease]; 1175 1176 [self unlock]; 1177 1178 return tmp; 1179} 1180 1181- (void)removeDataForKey:(NSString *)_key session:(WOSession *)_session { 1182 [self lock]; 1183 1184 if (self->keyedResources) 1185 NSMapRemove(self->keyedResources, _key); 1186 1187 [self unlock]; 1188} 1189 1190- (void)flushDataCache { 1191 [self lock]; 1192 1193 if (self->keyedResources) { 1194 NSFreeMapTable(self->keyedResources); 1195 self->keyedResources = NULL; 1196 } 1197 1198 [self unlock]; 1199} 1200 1201/* description */ 1202 1203- (NSString *)description { 1204 return [NSString stringWithFormat:@"<%@[0x%p]: path=%@>", 1205 [self class], self, self->base]; 1206 1207} 1208 1209@end /* OWResourceManager */ 1210