]> err.no Git - sope/blob - sope-appserver/WEExtensions/WEResourceManager.m
added strict OSX bundle dependencies
[sope] / sope-appserver / WEExtensions / WEResourceManager.m
1 /*
2   Copyright (C) 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 #include "WEResourceManager.h"
23 #include "WEStringTableManager.h"
24 #include "WEResourceKey.h"
25 #include "common.h"
26
27 @implementation WEResourceManager
28
29 static BOOL          debugOn         = NO;
30 static BOOL          debugComponents = NO;
31 static NSArray       *wsPathes       = nil;
32 static NSArray       *templatePathes = nil;
33 static NSString      *suffix   = nil;
34 static NSString      *prefix   = nil;
35 static NSFileManager *fm   = nil;
36 static NSNull        *null = nil;
37 static NSString *themesDirName = @"Themes";
38
39 + (NSString *)shareSubpath {
40   static NSString *shareSubPath = nil;
41   NSString *p;
42   
43   if (shareSubPath != nil)
44     return shareSubPath;
45   
46   p = [[WOApplication application] shareDirectoryName];
47   p = [@"share/" stringByAppendingString:p];
48   p = [p stringByAppendingString:@"/"];
49   shareSubPath = [p copy];
50   return shareSubPath;
51 }
52
53 + (NSString *)gsTemplatesSubpath {
54   NSString *p;
55   
56   p = [[WOApplication application] gsTemplatesDirectoryName];
57   p = [@"Library/" stringByAppendingString:p];
58   return p;
59 }
60 + (NSString *)gsWebSubpath {
61   NSString *p;
62   
63   p = [[WOApplication application] gsWebDirectoryName];
64   p = [@"Library/" stringByAppendingString:p];
65   return p;
66 }
67
68 /* locate resource directories */
69
70 + (NSArray *)rootPathesInGNUstep {
71   NSDictionary *env;
72   id tmp;
73   
74   env = [[NSProcessInfo processInfo] environment];
75   if ((tmp = [env objectForKey:@"GNUSTEP_PATHPREFIX_LIST"]) == nil)
76     tmp = [env objectForKey:@"GNUSTEP_PATHLIST"];
77   
78   return [tmp componentsSeparatedByString:@":"];
79 }
80 + (NSArray *)rootPathesInFHS {
81   return [NSArray arrayWithObjects:@"/usr/local/", @"/usr/", nil];
82 }
83
84 + (NSArray *)findResourceDirectoryPathesWithName:(NSString *)_name
85   fhsName:(NSString *)_fhs
86 {
87   NSEnumerator   *e;
88   NSFileManager  *fm;
89   NSMutableArray *ma;
90   BOOL           isDir;
91   id tmp;
92
93   fm  = [NSFileManager defaultManager];
94   ma  = [NSMutableArray arrayWithCapacity:8];
95   
96   e = [[self rootPathesInGNUstep] objectEnumerator];
97   while ((tmp = [e nextObject]) != nil) {
98     if (![tmp hasSuffix:@"/"])
99       tmp = [tmp stringByAppendingString:@"/"];
100     
101     tmp = [tmp stringByAppendingString:_name];
102     if ([ma containsObject:tmp]) continue;
103     
104     if (debugOn) [self logWithFormat:@"CHECK: %@", tmp];
105     if (![fm fileExistsAtPath:tmp isDirectory:&isDir])
106       continue;
107       
108     if (!isDir) continue;
109     
110     [ma addObject:tmp];
111   }
112
113   /* hack in FHS pathes */
114   
115   e = [[self rootPathesInFHS] objectEnumerator];
116   while ((tmp = [e nextObject]) != nil) {
117     tmp = [tmp stringByAppendingString:[[self class] shareSubpath]];
118     tmp = [tmp stringByAppendingString:_fhs];
119     if ([ma containsObject:tmp]) continue;
120     
121     if (![fm fileExistsAtPath:tmp isDirectory:&isDir])
122       continue;
123     if (!isDir) {
124       [self logWithFormat:@"path is not a directory: %@", tmp];
125       continue;
126     }
127     
128     [ma addObject:tmp];
129   }
130   
131   return ma;
132 }
133
134 + (int)version {
135   return [super version] + 0 /* v4 */;
136 }
137 + (void)initialize {
138   static BOOL isInitialized = NO;
139   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
140   if (isInitialized) return;
141   isInitialized = YES;
142   
143   NSAssert2([super version] == 4,
144             @"invalid superclass (%@) version %i !",
145             NSStringFromClass([self superclass]), [super version]);
146
147   null = [[NSNull null] retain];
148   
149   if ((debugOn = [ud boolForKey:@"WEResourceManagerDebugEnabled"]))
150     NSLog(@"Note: WEResourceManager debugging is enabled.");
151   debugComponents = [ud boolForKey:@"WEResourceManagerComponentDebugEnabled"];
152   if (debugComponents)
153     NSLog(@"Note: WEResourceManager component debugging is enabled.");
154   
155   fm = [[NSFileManager defaultManager] retain];
156   
157   suffix = [[ud stringForKey:@"WOApplicationSuffix"] copy];
158   prefix = [[ud stringForKey:@"WOResourcePrefix"]    copy];
159   
160   wsPathes = [[self findResourceDirectoryPathesWithName:
161                       [self gsWebSubpath] fhsName:@"www/"] copy];
162   if (debugOn)
163     NSLog(@"WebServerResources pathes: %@", wsPathes);
164   
165   // TODO: use appname to enable different setups, maybe some var too
166   templatePathes = [[self findResourceDirectoryPathesWithName:
167                             [[self class] gsTemplatesSubpath]
168                           fhsName:@"templates/"] copy];
169   if (debugOn)
170     NSLog(@"template pathes: %@", templatePathes);
171   if ([templatePathes count] == 0)
172     NSLog(@"Note: found no directories containing flat templates.");
173 }
174
175 + (NSArray *)availableThemes {
176   static NSArray *lthemes = nil;
177   NSMutableSet  *themes;
178   NSEnumerator  *e;
179   NSFileManager *fm;
180   NSString      *path;
181
182   if (lthemes != nil)
183     return lthemes;
184   
185   themes = [NSMutableSet setWithCapacity:16];
186   fm     = [NSFileManager defaultManager];
187   
188   e = [templatePathes objectEnumerator];
189   while ((path = [e nextObject]) != nil) {
190     NSArray *dl;
191     
192     path = [path stringByAppendingPathComponent:themesDirName];
193     dl   = [fm directoryContentsAtPath:path];
194     
195     [themes addObjectsFromArray:dl];
196   }
197   
198   /* remove directories to be ignored */
199   [themes removeObject:@".svn"];
200   [themes removeObject:@"CVS"];
201   
202   lthemes = [[[themes allObjects] 
203                sortedArrayUsingSelector:@selector(compare:)] copy];
204   if ([lthemes count] > 0) {
205     NSLog(@"Note: located themes: %@", 
206           [lthemes componentsJoinedByString:@", "]);
207   }
208   else
209     NSLog(@"Note: located no additional themes.");
210   return lthemes;
211 }
212
213 - (id)initWithPath:(NSString *)_path {
214   if ((self = [super initWithPath:_path])) {
215     if ([WOApplication isCachingEnabled]) {
216       self->keyToComponentPath =
217         [[NSMutableDictionary alloc] initWithCapacity:128];
218       
219       self->keyToPath = [[NSMutableDictionary alloc] initWithCapacity:1024];
220       self->keyToURL  = [[NSMutableDictionary alloc] initWithCapacity:1024];
221     }
222     else
223       [self logWithFormat:@"Note: component path caching is disabled!"];
224     
225     self->labelManager = [[WEStringTableManager alloc] init];
226     self->cachedKey    = [[WEResourceKey alloc] initCachedKey];
227   }
228   return self;
229 }
230 - (id)init {
231   // TODO: maybe search and set some 'share/ogo' path?
232   return [self initWithPath:nil];
233 }
234
235 - (void)dealloc {
236   [self->cachedKey          release];
237   [self->labelManager       release];
238   [self->keyToURL           release];
239   [self->keyToPath          release];
240   [self->keyToComponentPath release];
241   [super dealloc];
242 }
243
244 /* accessors */
245
246 - (NGBundleManager *)bundleManager {
247   static NGBundleManager *bm = nil; // THREAD
248   if (bm == nil) bm = [[NGBundleManager defaultBundleManager] retain];
249   return bm;
250 }
251
252 /* resource cache */
253
254 static id
255 checkCache(NSDictionary *_cache, WEResourceKey *_key,
256            NSString *_n, NSString *_fw, NSString *_l)
257 {
258   if (_cache == nil) {
259     if (debugOn) NSLog(@"cache disabled.");
260     return nil; /* caching disabled */
261   }
262   
263   /* setup cache key (THREAD) */
264   _key->hashValue     = 0; /* reset, calculate on next access */
265   _key->name          = _n;
266   _key->frameworkName = _fw;
267   _key->language      = _l;
268   
269   return [_cache objectForKey:_key];
270 }
271
272 - (void)cacheValue:(id)_value inCache:(NSMutableDictionary *)_cache {
273   WEResourceKey *k;
274   
275   if (_cache == nil) return; /* caching disabled */
276   
277   /* we need to dup, because the cachedKey does not retain! */
278   k = [self->cachedKey duplicate];
279   
280   if (debugOn) {
281     [self debugWithFormat:@"cache key %@(#%d): %@", k, [self->keyToPath count],
282             _value];
283   }
284   
285   [_cache setObject:(_value ? _value : (id)null) forKey:k];
286   [k release]; k = nil;
287 }
288
289 /* locate Resources */
290
291 - (NSString *)_weCheckPath:(NSString *)_p forResourceNamed:(NSString *)_name
292   inFramework:(NSString *)_frameworkName
293   language:(NSString *)_language
294 {
295   NSString *path;
296   
297   path = [_frameworkName length] > 0
298     ? [_p stringByAppendingPathComponent:_frameworkName]
299     : _p;
300       
301   /* check language */
302   if (_language != nil) {
303     path = [path stringByAppendingPathComponent:_language];
304     path = [path stringByAppendingPathExtension:@"lproj"];
305   }
306   
307   path = [path stringByAppendingPathComponent:_name];
308   if (debugOn) [self debugWithFormat:@"  check path: '%@'", path];
309       
310   if (![fm fileExistsAtPath:path])
311     return nil;
312   
313   return path;
314 }
315
316 - (NSString *)_weCheckPathes:(NSArray *)_p forResourceNamed:(NSString *)_name
317   inFramework:(NSString *)_frameworkName
318   language:(NSString *)_language
319 {
320   NSEnumerator *e;
321   NSString     *path;
322   
323   e = [_p objectEnumerator];
324   while ((path = [e nextObject]) != nil) {
325     path = [self _weCheckPath:path forResourceNamed:_name
326                  inFramework:_frameworkName language:_language];
327     if (path != nil) {
328       if (debugOn) [self debugWithFormat:@"FOUND: '%@'", path];
329       return path;
330     }
331   }
332   return nil;
333 }
334
335 - (NSString *)_wePathForResourceNamed:(NSString *)_name
336   inFramework:(NSString *)_fwName
337   language:(NSString *)_lang
338   searchPathes:(NSArray *)_pathes
339 {
340   // TODO: a lot of DUP code with _urlForResourceNamed, needs some refacturing
341   NSString *path;
342   
343   if (debugOn) [self debugWithFormat:@"lookup resource '%@'", _name];
344   
345   /* check cache */
346   
347   path = checkCache(self->keyToPath, self->cachedKey, _name, _fwName, _lang);
348   if (path != nil) {
349     if (debugOn) [self debugWithFormat:@"  found in cache: %@", path];
350     return [path isNotNull] ? path : nil;
351   }
352   
353   /* check for framework resources (webserver resources + framework) */
354
355   if (debugOn) 
356     [self debugWithFormat:@"check framework resources ..."];
357   path = [self _weCheckPathes:_pathes forResourceNamed:_name
358                inFramework:_fwName language:_lang];
359   if (path != nil) {
360     [self cacheValue:path inCache:self->keyToPath];
361     return path;
362   }
363   
364   /* check in basepath of webserver resources */
365   
366   // TODO: where is the difference, same call like above?
367   if (debugOn) [self debugWithFormat:@"check global resources ..."];
368   path = [self _weCheckPathes:_pathes forResourceNamed:_name
369                inFramework:_fwName language:_lang];
370   if (path != nil) {
371     [self cacheValue:path inCache:self->keyToPath];
372     return path;
373   }
374   
375   /* finished processing */
376   if (debugOn) 
377     [self debugWithFormat:@"NOT FOUND: %@ (%@)", _name, self->cachedKey];
378   return nil;
379 }
380
381 - (BOOL)shouldLookupResourceInWebServerResources:(NSString *)_name {
382   if ([_name hasSuffix:@".wox"]) return NO;
383   if ([_name hasSuffix:@".wo"])  return NO;
384   return YES;
385 }
386
387 - (NSString *)_wePathForResourceNamed:(NSString *)_name
388   inFramework:(NSString *)_fwName
389   language:(NSString *)_lang
390 {
391   NSString *p;
392   
393   /* check in webserver resources */
394   
395   if ([self shouldLookupResourceInWebServerResources:_name]) {
396     p = [self _wePathForResourceNamed:_name inFramework:_fwName
397               language:_lang searchPathes:wsPathes];
398     if (p != nil) return p;
399   }
400   
401   return nil;
402 }
403
404 - (BOOL)isTemplateResourceName:(NSString *)_name {
405   // TODO: non-extensible
406   return [_name hasSuffix:@".wox"];
407 }
408
409 - (NSString *)pathForResourceNamed:(NSString *)_name
410   inFramework:(NSString *)_fwName
411   languages:(NSArray *)_langs
412 {
413   /* 
414      Note: this is also called by the superclass method 
415            -pathToComponentNamed:inFramework: for each registered component
416            extension.
417   */
418   NSEnumerator *e;
419   NSString     *language;
420   NSString     *rpath;
421   
422   if ([_name length] == 0) {
423     [self debugWithFormat:@"got no name for resource lookup?!"];
424     return nil;
425   }
426   
427   if (debugOn) {
428     [self debugWithFormat:@"pathForResourceNamed: %@/%@ (languages: %@)",
429             _name, _fwName, [_langs componentsJoinedByString:@","]];
430   }
431
432   if ([self isTemplateResourceName:_name]) {
433     if (debugOn) [self debugWithFormat:@"  is template resource .."];
434     return [self pathToComponentNamed:[_name stringByDeletingPathExtension]
435                  inFramework:_fwName
436                  languages:_langs];
437   }
438   
439   /* check languages */
440   
441   e = [_langs objectEnumerator];
442   while ((language = [e nextObject]) != nil) {
443       NSString *rpath;
444       
445       if (debugOn) 
446         [self logWithFormat:@"  check language (%@): '%@'", _name, language];
447       rpath = [self _wePathForResourceNamed:_name inFramework:_fwName
448                     language:language];
449       if (rpath != nil) {
450         if (debugOn) [self debugWithFormat:@"  FOUND: %@", rpath];
451         return rpath;
452       }
453   }
454   
455   /* check without language */
456   
457   rpath = [self _wePathForResourceNamed:_name inFramework:_fwName
458                 language:nil];
459   if (rpath != nil)
460     return rpath;
461   
462   if (debugOn) {
463     [self debugWithFormat:
464               @"did not find resource, try super lookup: '%@'", _name];
465   }
466     
467   /* look using WOResourceManager */
468   
469   rpath = [super pathForResourceNamed:_name inFramework:_fwName
470                  languages:_langs];
471   return rpath;
472 }
473
474 /* locate WebServerResources */
475
476 - (NSString *)_urlForResourceNamed:(NSString *)_name
477   inFramework:(NSString *)_fwName
478   language:(NSString *)_lang
479   applicationName:(NSString *)_appName
480 {
481   NSString     *url;
482   NSEnumerator *e;
483   NSString     *path;
484   
485   if (debugOn) {
486     [self logWithFormat:@"lookup URL of resource: '%@'/%@/%@", 
487             _name, _fwName, _lang];
488   }
489   
490   /* check cache */
491   
492   url = checkCache(self->keyToURL, self->cachedKey, _name, _fwName, _lang);
493   if (url != nil) {
494     if (debugOn) {
495       [self debugWithFormat:@"  found in cache: %@ (#%d)", url, 
496               [self->keyToURL count]];
497     }
498     return [url isNotNull] ? url : nil;
499   }
500   
501   if (debugOn) {
502     [self debugWithFormat:@"  not found in cache: %@ (%@,#%d)", 
503             url, self->cachedKey, [self->keyToURL count]];
504   }
505   
506   /* check for framework resources */
507   
508   if ([_fwName length] > 0) {
509     if (debugOn) 
510       [self debugWithFormat:@"check framework: '%@'", _fwName];
511     e = [wsPathes objectEnumerator];
512     while ((path = [e nextObject])) {
513       NSMutableString *ms;
514       
515       path = [path stringByAppendingPathComponent:_fwName];
516
517       /* check language */
518       if (_lang) {
519         path = [path stringByAppendingString:_lang];
520         path = [path stringByAppendingPathExtension:@"lproj"];
521       }
522       
523       path = [path stringByAppendingPathComponent:_name];
524       if (debugOn) [self debugWithFormat:@"  check path: '%@'", path];
525       
526       if (![fm fileExistsAtPath:path])
527         continue;
528         
529       ms = [[NSMutableString alloc] initWithCapacity:256];
530         
531       if (prefix) [ms appendString:prefix];
532       if (![ms hasSuffix:@"/"]) [ms appendString:@"/"];
533       [ms appendString:_appName];
534       if (suffix) [ms appendString:suffix];
535       [ms appendString:[ms hasSuffix:@"/"] 
536             ? @"WebServerResources/" : @"/WebServerResources/"];
537       [ms appendString:_fwName];
538       [ms appendString:@"/"];
539       if (_lang) {
540           [ms appendString:_lang];
541           [ms appendString:@".lproj/"];
542       }
543       [ms appendString:_name];
544       
545       url = [ms copy];
546       [ms release]; ms = nil;
547       if (debugOn) [self debugWithFormat:@"FOUND: '%@'", url];
548       goto done;
549     }
550   }
551   
552   /* check for global resources */
553   
554   if (debugOn) [self debugWithFormat:@"check global WebServerResources ..."];
555   e = [wsPathes objectEnumerator];
556   while ((path = [e nextObject])) {
557     NSMutableString *ms;
558     NSString *fpath, *basepath;
559     
560     /* check language */
561     if (_lang) {
562       basepath = [path stringByAppendingString:_lang];
563       basepath = [basepath stringByAppendingPathExtension:@"lproj"];
564     }
565     else
566       basepath = path;
567     
568     fpath = [basepath stringByAppendingPathComponent:_name];
569     if (debugOn) {
570       [self debugWithFormat:
571               @"  check path: '%@'\n base: %@\n name: %@\n "
572               @" path: %@\n lang: %@", 
573               fpath, basepath, _name, path, _lang];
574     }
575     
576     if (![fm fileExistsAtPath:fpath])
577       continue;
578       
579     ms = [[NSMutableString alloc] initWithCapacity:256];
580       
581     if (prefix) [ms appendString:prefix];
582     if (![ms hasSuffix:@"/"]) [ms appendString:@"/"];
583     [ms appendString:_appName];
584     if (suffix) [ms appendString:suffix];
585     [ms appendString:[ms hasSuffix:@"/"] 
586           ? @"WebServerResources/" : @"/WebServerResources/"];
587     if (_lang) {
588       [ms appendString:_lang];
589       [ms appendString:@".lproj/"];
590     }
591     [ms appendString:_name];
592       
593     url = [ms copy];
594     [ms release]; ms = nil;
595     if (debugOn) [self debugWithFormat:@"FOUND: '%@'", url];
596     goto done;
597   }
598   
599   /* finished processing */
600   if (debugOn) {
601     [self debugWithFormat:@"NOT FOUND: %@ (%@,#%d)", _name, self->cachedKey,
602             [self->keyToURL count]];
603   }
604   
605  done:
606   [self cacheValue:url inCache:self->keyToURL];
607   return url;
608 }
609
610 - (NSString *)urlForResourceNamed:(NSString *)_name
611   inFramework:(NSString *)_fwName
612   languages:(NSArray *)_langs
613   request:(WORequest *)_request
614 {
615   NSEnumerator *e;
616   NSString     *language;
617   NSString     *url;
618   NSString     *appName;
619   
620   if ([_name length] == 0) {
621     if (debugOn) [self logWithFormat:@"got no name for resource URL lookup?!"];
622     return nil;
623   }
624   
625   if (debugOn) [self debugWithFormat:@"urlForResourceNamed: %@", _name];
626   
627   if (_langs == nil) {
628     _langs = [_request browserLanguages];
629     if (debugOn) {
630       [self debugWithFormat:@"using browser languages: %@", 
631               [_langs componentsJoinedByString:@", "]];
632     }
633   }
634   else if (debugOn) {
635     [self debugWithFormat:@"using given languages: %@", 
636             [_langs componentsJoinedByString:@","]];
637   }
638   
639   appName = [_request applicationName];
640   if (appName == nil)
641     appName = [(WOApplication *)[WOApplication application] name];
642   
643   /* check languages */
644   
645   e = [_langs objectEnumerator];
646   while ((language = [e nextObject])) {
647     NSString *url;
648     
649     if (debugOn) [self logWithFormat:@"  check language: '%@'", language];
650     url = [self _urlForResourceNamed:_name
651                 inFramework:_fwName
652                 language:language
653                 applicationName:appName];
654     if (url != nil) {
655       if (debugOn) [self logWithFormat:@"  FOUND: %@", url];
656       return url;
657     }
658   }
659   
660   /* check without language */
661   
662   url = [self _urlForResourceNamed:_name
663               inFramework:_fwName
664               language:nil
665               applicationName:appName];
666   if (url != nil)
667     return url;
668
669   if (debugOn) {
670     [self debugWithFormat:
671             @"did not find resource in try super lookup: '%@'", _name];
672   }
673   
674   url = [super urlForResourceNamed:_name
675                inFramework:_fwName
676                languages:_langs
677                request:_request];
678
679   return url;
680 }
681
682 /* locate components */
683
684 - (NSString *)lookupComponentInStandardPathes:(NSString *)_name
685   inFramework:(NSString *)_framework theme:(NSString *)_theme
686 {
687   // TODO: what about languages/themes?!
688   NSEnumerator *e;
689   NSString *path;
690   
691   if (debugComponents) {
692     [self logWithFormat:@"lookup component in std pathes: %@|%@|%@",
693           _name, _framework, _theme];
694   }
695   
696   if ([_theme isNotNull] && [_theme length] == 0) 
697     _theme = nil;
698   if ([_framework isNotNull] && [_framework length] == 0) 
699     _framework = nil;
700   
701   e = [templatePathes objectEnumerator];
702   while ((path = [e nextObject]) != nil) {
703     NSString *pe;
704     
705     if (_theme != nil) {
706       // TODO: should be lower case for FHS? or use a different path?
707       path = [path stringByAppendingPathComponent:themesDirName];
708       path = [path stringByAppendingPathComponent:_theme];
709     }
710     
711     if (_framework != nil) {
712       NSString *pureName;
713       
714       pureName = [_framework lastPathComponent];
715       pureName = [pureName stringByDeletingPathExtension];
716       path = [path stringByAppendingPathComponent:pureName];
717     }
718     
719     path = [path stringByAppendingPathComponent:_name];
720     
721     pe = [path stringByAppendingPathExtension:@"wox"];
722     if (debugComponents) [self logWithFormat:@"CHECK %@", pe];
723     
724     if ([fm fileExistsAtPath:pe])
725       return pe;
726
727     pe = [path stringByAppendingPathExtension:@"html"];
728     if ([fm fileExistsAtPath:pe]) {
729       /* 
730          Note: we are passing in the path of the HTML template, this is some
731                kind of hack to make the wrapper template builder look for the
732                $name.html/$name.wod in there.
733       */
734       return pe;
735     }
736   }
737   
738   return nil;
739 }
740 - (NSString *)lookupComponentInStandardPathes:(NSString *)_name
741   inFramework:(NSString *)_framework languages:(NSArray *)_langs
742 {
743   NSString *path;
744   NSString *theme;
745   
746   if (debugComponents) {
747     [self logWithFormat:@"lookup component in std pathes(langs): %@|%@",
748           _name, _framework];
749   }
750   
751   /* extract theme from language array (we do not support nested themes ATM) */
752   
753   theme = nil;
754   if ([_langs count] > 1) {
755     NSRange r;
756     
757     theme = [_langs objectAtIndex:0];
758     r = [theme rangeOfString:@"_"];
759     theme = (r.length > 0)
760       ? [theme substringFromIndex:(r.location + r.length)]
761       : nil;
762   }
763   else
764     theme = nil;
765
766   /* check theme dirs */
767   
768   if (theme != nil) {
769     path = [self lookupComponentInStandardPathes:_name inFramework:_framework
770                  theme:theme];
771     if (path != nil)
772       return path;
773   }
774
775   /* check base dirs */
776   
777   path = [self lookupComponentInStandardPathes:_name inFramework:_framework
778                theme:nil];
779   if (path != nil)
780     return path;
781   
782   /* check without framework subdir */
783
784   if ([_framework length] > 0) {
785     path = [self lookupComponentInStandardPathes:_name inFramework:nil
786                  theme:nil];
787     if (path != nil)
788       return path;
789   }
790
791   return nil;
792 }
793
794 - (NSString *)lookupComponentPathUsingBundleManager:(NSString *)_name {
795   // TODO: is this ever invoked?
796   NSFileManager *fm = nil;
797   NSString *wrapper = nil;
798   NSBundle *bundle  = nil;
799   NSString *path;
800
801   if (debugComponents)
802     [self logWithFormat:@"lookup component using bundle manager: %@", _name];
803   
804   bundle = [[self bundleManager]
805                   bundleProvidingResource:_name
806                   ofType:@"WOComponents"];
807   if (bundle == nil) {
808     [self debugWithFormat:@"did not find a bundle providing component: %@", 
809             _name];
810     return nil;
811   }
812   
813   if (debugOn) {
814     [self debugWithFormat:@"bundle %@ for component %@",
815             [[bundle bundlePath] lastPathComponent], _name];
816   }
817   
818   [bundle load];
819   
820   fm      = [NSFileManager defaultManager];
821   wrapper = [_name stringByAppendingPathExtension:@"wo"];
822   
823   path = [[bundle bundlePath] stringByAppendingPathComponent:@"Resources"];
824   path = [path stringByAppendingPathComponent:wrapper];
825   if ([fm fileExistsAtPath:path])
826     return path;
827   
828   path = [[bundle bundlePath] stringByAppendingPathComponent:wrapper];
829   if ([fm fileExistsAtPath:path])
830     return path;
831   
832   return nil;
833 }
834
835 - (NSString *)pathToComponentNamed:(NSString *)_name
836   inFramework:(NSString *)_fw languages:(NSArray *)_langs
837 {
838   // TODO: what about languages in lookup?
839   NSString *path;
840
841   if (debugComponents) {
842     [self logWithFormat:@"%s: lookup component: %@|%@",
843           __PRETTY_FUNCTION__, _name, _fw];
844   }
845   
846   /* first check cache */
847   
848   path = checkCache(self->keyToComponentPath, self->cachedKey, _name, _fw,
849                     [_langs count] > 0 ? [_langs objectAtIndex:0]:@"English");
850   if (path != nil) {
851     if (debugComponents)
852       [self logWithFormat:@"  use cached location: %@", path];
853     return [path isNotNull] ? path : nil;
854   }
855   
856   /* look in FHS locations */
857
858   path = [self lookupComponentInStandardPathes:_name inFramework:_fw
859                languages:_langs];
860   if (path != nil) {
861     if (debugComponents)
862       [self logWithFormat:@"  found in standard pathes: %@", path];
863     goto done;
864   }
865   
866   /* try to find component by standard NGObjWeb method */
867   
868   if (debugComponents)
869     [self logWithFormat:@"lookup component using WOResourceManager ..."];
870   path = [super pathToComponentNamed:_name inFramework:_fw languages:_langs];
871   if (path != nil) {
872     if (debugComponents)
873       [self logWithFormat:@"  found using WOResourceManager: %@", path];
874     goto done;
875   }
876   
877   /* find component using NGBundleManager */
878   
879   if ((path = [self lookupComponentPathUsingBundleManager:_name]) != nil) {
880     if (debugComponents)
881       [self logWithFormat:@"  found using bundle manager: %@", path];
882     goto done;
883   }
884   
885   /* did not find component */
886  done:
887   [self cacheValue:path inCache:self->keyToComponentPath];
888   return path;
889 }
890
891 /* string tables */
892
893 - (NSString *)labelForKey:(NSString *)_key component:(WOComponent *)_component{
894   return [self->labelManager labelForKey:_key component:_component];
895 }
896
897 - (NSString *)stringForKey:(NSString *)_key
898   inTableNamed:(NSString *)_tableName
899   withDefaultValue:(NSString *)_default
900   languages:(NSArray *)_langs
901 {
902   NSString *s;
903   
904   s = [self->labelManager 
905            stringForKey:_key inTableNamed:_tableName
906            withDefaultValue:_default languages:_langs];
907   if (s != nil) return s;
908   
909   s = [super stringForKey:_key inTableNamed:_tableName
910              withDefaultValue:_default
911              languages:_langs];
912   if (s != nil) return s;
913   
914   return s != nil ? s : _default;
915 }
916
917 /* debugging */
918
919 - (BOOL)isDebuggingEnabled {
920   return debugOn;
921 }
922 - (NSString *)loggingPrefix {
923   return @"[we-rm]";
924 }
925
926 @end /* WEResourceManager */
927
928 @implementation WOApplication(WEResourceManager)
929
930 - (NSString *)shareDirectoryName {
931   return [[self name] lowercaseString];
932 }
933 - (NSString *)gsTemplatesDirectoryName {
934   return [[self name] stringByAppendingString:@"/Templates/"];
935 }
936 - (NSString *)gsWebDirectoryName {
937   return [[self name] stringByAppendingString:@"/WebServerResources/"];
938 }
939
940 @end /* WOApplication(WEResourceManager) */