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