1/* 2 Copyright (C) 2002-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 "SoProduct.h" 23#include "SoProductClassInfo.h" 24#include "SoProductResourceManager.h" 25#include "SoClassRegistry.h" 26#include "SoClassSecurityInfo.h" 27#include "SoObject.h" 28#include "SoSecurityManager.h" 29#include <NGObjWeb/WOApplication.h> 30#include <NGObjWeb/WOResourceManager.h> 31#include <NGObjWeb/WOResponse.h> 32#include "common.h" 33 34@interface SoProduct(Privates) 35- (void)registerClassesFromDictionary:(NSDictionary *)_classToInfo; 36- (void)registerCategoriesFromDictionary:(NSDictionary *)_classToInfo; 37@end 38 39@implementation SoProduct 40 41static int debugOn = 1; 42static int regDebugOn = 0; 43static int loadDebugOn = 0; 44 45+ (void)initialize { 46 static BOOL didInit = NO; 47 if (!didInit) { 48 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; 49 didInit = YES; 50 regDebugOn = [ud boolForKey:@"SoDebugProductRegistry"] ? 1 : 0; 51 loadDebugOn = [ud boolForKey:@"SoDebugProductLoading"] ? 1 : 0; 52 } 53} 54 55- (id)initWithDictionary:(NSDictionary *)_dict { 56 if ((self = [super init])) { 57 self->requiredProducts = [[_dict objectForKey:@"requires"] copy]; 58 self->publicResources = [[_dict objectForKey:@"publicResources"] copy]; 59 60 [self registerClassesFromDictionary:[_dict objectForKey:@"classes"]]; 61 [self registerCategoriesFromDictionary:[_dict objectForKey:@"categories"]]; 62 } 63 return self; 64} 65 66- (id)initWithBundle:(NSBundle *)_bundle { 67 NSString *manifestPath; 68 NSDictionary *manifest; 69 70 if (_bundle == nil) { 71 [self release]; 72 return nil; 73 } 74 self->bundle = [_bundle retain]; 75 manifestPath = [self->bundle pathForResource:@"product" ofType:@"plist"]; 76 if ([manifestPath length] == 0) { 77 [self release]; 78 return nil; 79 } 80 81 manifest = [NSDictionary dictionaryWithContentsOfFile:manifestPath]; 82 if (manifest == nil) { 83 [self logWithFormat:@"could not parse manifest: %@", manifestPath]; 84 [self release]; 85 return nil; 86 } 87 88 self->resourceManager = 89 [[SoProductResourceManager alloc] initWithProduct:self]; 90 if (self->resourceManager == nil) 91 [self logWithFormat:@"failed to instantiate resourcemanager for bundle"]; 92 93 return [self initWithDictionary:manifest]; 94} 95 96- (void)dealloc { 97 [self->resourceManager detachFromContainer]; 98 99 [self->resourceManager release]; 100 [self->publicResources release]; 101 [self->requiredProducts release]; 102 [self->categories release]; 103 [self->classes release]; 104 [self->bundle release]; 105 [super dealloc]; 106} 107 108/* accessors */ 109 110- (NSArray *)requiredProducts { 111 return self->requiredProducts; 112} 113 114- (NSBundle *)bundle { 115 return self->bundle; 116} 117 118- (BOOL)isMainProduct { 119 if (self->bundle == nil) return YES; 120 if (self->bundle == [NSBundle mainBundle]) return YES; 121 return NO; 122} 123 124- (NSString *)productName { 125 if ([self isMainProduct]) 126 return @"MAIN"; 127 128 return [[[self->bundle bundlePath] 129 lastPathComponent] stringByDeletingPathExtension]; 130} 131 132- (BOOL)isPublicResource:(NSString *)_key { 133 return [self->publicResources containsObject:_key] ? YES : NO; 134} 135 136/* parsing manifest */ 137 138- (void)registerCategoryNamed:(NSString *)_name info:(NSDictionary *)_info { 139 SoProductCategoryInfo *catInfo; 140 141 if (regDebugOn) 142 [self logWithFormat:@" register category on '%@'", _name]; 143 144 catInfo = [[SoProductCategoryInfo alloc] 145 initWithName:_name manifest:_info product:self]; 146 if (catInfo == nil) { 147 [self logWithFormat:@" could not init category info for '%@'", _name]; 148 return; 149 } 150 if ([self->categories objectForKey:_name]) { 151 [self errorWithFormat: 152 @"duplicate declaration of category on '%@' in product.", 153 _name]; 154 [catInfo release]; 155 return; 156 } 157 158 if (self->categories == nil) 159 self->categories = [[NSMutableDictionary alloc] init]; 160 161 [self->categories setObject:catInfo forKey:_name]; 162 [catInfo autorelease]; 163} 164 165- (void)registerClassNamed:(NSString *)_name info:(NSDictionary *)_info { 166 SoProductClassInfo *classInfo; 167 168 if (regDebugOn) 169 [self logWithFormat:@" register class: %@", _name]; 170 171 classInfo = [[SoProductClassInfo alloc] 172 initWithName:_name manifest:_info product:self]; 173 if (classInfo == nil) { 174 [self debugWithFormat:@" could not init class info for '%@'", _name]; 175 return; 176 } 177 if ([self->classes objectForKey:_name]) { 178 [self errorWithFormat:@"duplicate declaration of class %@ in product " 179 @"(registering as category)", 180 _name]; 181 [classInfo release]; 182 [self registerCategoryNamed:_name info:_info]; 183 return; 184 } 185 186 if (self->classes == nil) 187 self->classes = [[NSMutableDictionary alloc] init]; 188 189 [self->classes setObject:classInfo forKey:_name]; 190 [classInfo autorelease]; 191} 192 193- (void)registerClassesFromDictionary:(NSDictionary *)_classToInfo { 194 NSEnumerator *names; 195 NSMutableSet *regClasses; 196 NSString *className; 197 198 regClasses = [NSMutableSet setWithCapacity:16]; 199 names = [_classToInfo keyEnumerator]; 200 while ((className = [names nextObject])) { 201 NSDictionary *info; 202 203 if ([regClasses containsObject:className]) 204 continue; 205 206 info = [_classToInfo objectForKey:className]; 207 [self registerClassNamed:className info:info]; 208 [regClasses addObject:className]; 209 } 210} 211- (void)registerCategoriesFromDictionary:(NSDictionary *)_classToInfo { 212 NSEnumerator *names; 213 NSMutableSet *regCats; 214 NSString *className; 215 216 regCats = [NSMutableSet setWithCapacity:16]; 217 names = [_classToInfo keyEnumerator]; 218 while ((className = [names nextObject])) { 219 NSDictionary *info; 220 221 if ([regCats containsObject:className]) 222 continue; 223 224 info = [_classToInfo objectForKey:className]; 225 [self registerCategoryNamed:className info:info]; 226 [regCats addObject:className]; 227 } 228} 229 230/* loading */ 231 232- (BOOL)load { 233 SoClassRegistry *registry; 234 235 if (self->flags.isLoaded) { 236 if (loadDebugOn) 237 [self logWithFormat:@"product already loaded: %@", self]; 238 return YES; 239 } 240 241 if (loadDebugOn) 242 [self logWithFormat:@"loading product: %@", self]; 243 self->flags.isLoaded = 1; 244 245 /* check whether bundle is binary ! */ 246 247 if ((self->bundle != nil) && (self->bundle != [NSBundle mainBundle])) { 248 if (loadDebugOn) { 249 [self logWithFormat:@" loading bundle of product: %@", 250 [self->bundle bundlePath]]; 251 } 252 253 if (![self->bundle load]) { 254 if (loadDebugOn) [self logWithFormat:@" failed to load bundle."]; 255 return NO; 256 } 257 self->flags.isCodeLoaded = 1; 258 } 259 260 registry = [SoClassRegistry sharedClassRegistry]; 261 262 if (loadDebugOn) { 263 [self logWithFormat:@" registering %i classes ...", 264 [self->classes count]]; 265 } 266 267 [[self->classes allValues] 268 makeObjectsPerformSelector:@selector(applyOnRegistry:) 269 withObject:registry]; 270 271 if (loadDebugOn) { 272 [self logWithFormat:@" registering %i categories ...", 273 [self->categories count]]; 274 } 275 276 [[self->categories allValues] 277 makeObjectsPerformSelector:@selector(applyOnRegistry:) 278 withObject:registry]; 279 280 if (loadDebugOn) 281 [self logWithFormat:@"done loading product."]; 282 return YES; 283} 284 285- (BOOL)reloadIfPossible { 286 /* only possible if no product ObjC code is loaded */ 287 if (self->flags.isCodeLoaded) return NO; 288 289 return NO; 290} 291 292/* product as a SoObject */ 293 294- (NSString *)baseURLInContext:(id)_ctx { 295 /* Note: cannot use -stringByAppendingPathComponent: on OSX ! */ 296 NSString *baseURL, *cname; 297 298 baseURL = [self rootURLInContext:_ctx]; 299 if (![baseURL hasSuffix:@"/"]) 300 baseURL = [baseURL stringByAppendingString:@"/"]; 301 302 baseURL = [baseURL stringByAppendingString:@"ControlPanel/Products/"]; 303 cname = [[self productName] stringByEscapingURL]; 304 baseURL = [baseURL stringByAppendingString:cname]; 305 return baseURL; 306} 307 308- (NSArray *)allKeys { 309 return [NSArray arrayWithObject:@"Resources"]; 310} 311 312- (BOOL)hasName:(NSString *)_key inContext:(id)_ctx { 313 if ([_key isEqualToString:@"Resources"]) 314 return YES; 315 return [super hasName:_key inContext:_ctx]; 316} 317 318- (id)lookupName:(NSString *)_key inContext:(id)_ctx acquire:(BOOL)_flag { 319 if ([_key isEqualToString:@"Resources"]) 320 return [self resourceManager]; 321 322 return [super lookupName:_key inContext:_ctx acquire:_flag]; 323} 324 325/* resource manager */ 326 327- (WOResourceManager *)resourceManager { 328 if ([self isMainProduct]) 329 return [[WOApplication application] resourceManager]; 330 331 if (self->resourceManager == nil) { 332 [self warnWithFormat:@"resource-manager was nil ..."]; 333 self->resourceManager = 334 [[SoProductResourceManager alloc] initWithProduct:self]; 335 } 336 return self->resourceManager; 337} 338 339/* HTML representation */ 340 341- (void)appendToResponse:(WOResponse *)_response inContext:(id)_ctx { 342 [_response appendContentString:@"<h3>SOPE Product: "]; 343 [_response appendContentHTMLString:[self productName]]; 344 [_response appendContentString:@"</h3>"]; 345 346 [_response appendContentString: 347 @"<li><a href=\"Resources/\">Resources</a></li>"]; 348} 349 350/* debugging */ 351 352- (NSString *)loggingPrefix { 353 return [NSString stringWithFormat:@"[so-product:%@]", 354 [[[self bundle] bundlePath] lastPathComponent]]; 355} 356- (BOOL)isDebuggingEnabled { 357 return debugOn ? YES : NO; 358} 359 360/* description */ 361 362- (NSString *)description { 363 NSMutableString *ms; 364 unsigned cnt; 365 366 ms = [NSMutableString stringWithCapacity:64]; 367 [ms appendFormat:@"<0x%p[%@]:", self, NSStringFromClass([self class])]; 368 369 if (self->flags.isLoaded) 370 [ms appendFormat:@" loaded"]; 371 if (self->flags.isCodeLoaded) 372 [ms appendFormat:@" code-loaded"]; 373 374 if (self->bundle) 375 [ms appendFormat:@" bundle=%@", [self->bundle bundlePath]]; 376 377 if ((cnt = [self->classes count]) > 0) 378 [ms appendFormat:@" #classes=%d", cnt]; 379 if ((cnt = [self->categories count]) > 0) 380 [ms appendFormat:@" #categories=%d", cnt]; 381 if ((cnt = [self->publicResources count]) > 0) 382 [ms appendFormat:@" #pubrsrc=%d", cnt]; 383 384 if (self->resourceManager) 385 [ms appendFormat:@" rm=0x%p", self->resourceManager]; 386 387 [ms appendString:@">"]; 388 return ms; 389} 390 391@end /* SoProduct */ 392