/*
- Copyright (C) 2000-2004 SKYRIX Software AG
+ Copyright (C) 2000-2005 SKYRIX Software AG
- This file is part of OpenGroupware.org.
+ This file is part of SOPE.
- OGo is free software; you can redistribute it and/or modify it under
+ SOPE is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
- OGo is distributed in the hope that it will be useful, but WITHOUT ANY
+ SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
- License along with OGo; see the file COPYING. If not, write to the
+ License along with SOPE; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
-// $Id: WOComponent.m 1 2004-08-20 10:08:27Z znek $
#include <NGObjWeb/WOComponent.h>
#include "WOComponent+private.h"
#include <NGObjWeb/WODynamicElement.h>
#include "WOContext+private.h"
#include "WOElement+private.h"
-#include "WOComponentDefinition.h"
+#include <NGObjWeb/WOComponentDefinition.h>
#include <NGObjWeb/WOResourceManager.h>
#include <NGObjWeb/WOApplication.h>
#include <NGObjWeb/WOResponse.h>
#include "WOComponentFault.h"
#include "common.h"
#include <NGExtensions/NGBundleManager.h>
-#include <EOControl/EOControl.h>
+#import <EOControl/EOControl.h>
+#ifdef MULLE_EO_CONTROL
+#import <EOControl/EOKeyValueUnarchiver.h>
+#endif
#include <NGExtensions/NSString+Ext.h>
@interface WOContext(ComponentStackCount)
static Class NSDateClass = Nil;
static Class WOComponentClass = Nil;
-static BOOL profElements = NO;
+
+static NGLogger *perfLogger = nil;
+
static BOOL debugOn = NO;
static BOOL debugComponentAwake = NO;
static BOOL debugTemplates = NO;
+static BOOL debugTakeValues = NO;
static BOOL abortOnAwakeComponentInCtxDealloc = NO;
static BOOL abortOnMissingCtx = NO;
static BOOL wakeupPageOnCreation = NO;
+ (int)version {
+ // TODO: is really v4 for baseURL/cycleContext ivar changes
return [super version] + 0 /* v2 */;
}
+ (void)initialize {
- NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
+ NSUserDefaults *ud;
+ NGLoggerManager *lm;
static BOOL didInit = NO;
+
if (didInit) return;
didInit = YES;
NSAssert2([super version] == 2,
@"invalid superclass (%@) version %i !",
NSStringFromClass([self superclass]), [super version]);
+
+ ud = [NSUserDefaults standardUserDefaults];
+ lm = [NGLoggerManager defaultLoggerManager];
- WOComponentClass = [WOComponent class];
- NSDateClass = [NSDate class];
-
- profElements = [ud boolForKey:@"WOProfileElements"];
+ WOComponentClass = [WOComponent class];
+ NSDateClass = [NSDate class];
+ perfLogger = [lm loggerForDefaultKey:@"WOProfileElements"];
debugOn = [WOApplication isDebuggingEnabled];
debugComponentAwake = [ud boolForKey:@"WODebugComponentAwake"];
+
+ if ((debugTakeValues = [ud boolForKey:@"WODebugTakeValues"]))
+ NSLog(@"WOComponent: WODebugTakeValues on.");
+
abortOnAwakeComponentInCtxDealloc =
[ud boolForKey:@"WOCoreOnAwakeComponentInCtxDealloc"];
}
- (id)initWithContext:(WOContext *)_ctx {
[self _setContext:_ctx];
if ((self = [self init])) {
- if (self->context)
+ if (self->context != nil)
[self ensureAwakeInContext:self->context];
else {
- [self logWithFormat:
- @"WARNING: no context given to -initWithContext: ..."];
+ [self warnWithFormat:
+ @"no context given to -initWithContext: ..."];
}
}
return self;
withObject:nil];
[self->subcomponents release];
+ [self->wocClientObject release];
[self->wocBindings release];
[self->wocVariables release];
[self->wocName release];
+ [self->wocBaseURL release];
[super dealloc];
}
#endif
if (self->componentFlags.isAwake) {
- [self logWithFormat:
- @"WARNING: session will dealloc, "
- @"but component 0x%08X is awake (ctx=%@) !", self, self->context];
+ [self warnWithFormat:
+ @"session will dealloc, but component 0x%08X is awake (ctx=%@) !",
+ self, self->context];
[self _sleepWithContext:self->context];
}
the event?!
*/
[self debugWithFormat:
- @"WARNING: context %@ will dealloc, "
- @"but component is awake in ctx %@!",
+ @"context %@ will dealloc, but component is awake in ctx %@!",
[_notification object], [self->context contextID]];
if (abortOnAwakeComponentInCtxDealloc)
abort();
- (void)sleep {
if (debugOn) {
if (self->componentFlags.isAwake) {
- [self debugWithFormat:
- @"WARNING: component should not be awake if sleep is called !"];
+ [self warnWithFormat:
+ @"component should not be awake if sleep is called !"];
}
if (self->context == nil) {
- [self debugWithFormat:
- @"WARNING: context should not be nil if sleep is called !"];
+ [self warnWithFormat:
+ @"context should not be nil if sleep is called !"];
}
}
#endif
if (debugComponentAwake)
- [self logWithFormat:@"0x%08X ensureAwakeInContext:0x%08X", self, _ctx];
+ [self debugWithFormat:@"0x%08X ensureAwakeInContext:0x%08X", self, _ctx];
+
+ /* sanity check */
+
+ if (self->componentFlags.isAwake) {
+ if (self->context == _ctx) {
+ if (debugComponentAwake)
+ [self debugWithFormat:@"0x%08X already awake:0x%08X", self, _ctx];
+ return;
+ }
+ }
- if (self->context == nil) [self _setContext:_ctx];
+ /* setup globals */
+
+ if (self->context == nil) [self _setContext:_ctx];
if (self->application == nil) self->application = [_ctx application];
if ((self->session == nil) && [_ctx hasSession])
WOComponent *child;
children = [self->subcomponents objectEnumerator];
- while ((child = [children nextObject]))
+ while ((child = [children nextObject]) != nil)
[child _awakeWithContext:_ctx];
}
}
- (void)_sleepWithContext:(WOContext *)_ctx {
if (debugComponentAwake)
- [self logWithFormat:@"0x%08X _sleepWithContext:0x%08X", self, _ctx];
+ [self debugWithFormat:@"0x%08X _sleepWithContext:0x%08X", self, _ctx];
if (_ctx != self->context) {
if ((self->context != nil) && (_ctx != nil)) {
/* component is active in different context ... */
- [self debugWithFormat:
- @"WARNING: sleep context mismatch (own=0x%08X vs given=0x%08X)",
+ [self warnWithFormat:
+ @"sleep context mismatch (own=0x%08X vs given=0x%08X)",
self->context, _ctx];
return;
}
return self->wocName;
}
- (NSString *)frameworkName {
- return [[NGBundle bundleForClass:[self class]] bundleName];
+ NSBundle *cbundle;
+
+ cbundle = [NGBundle bundleForClass:[self class]];
+ if (cbundle == [NSBundle mainBundle])
+ return nil;
+
+ return [cbundle bundleName];
}
- (NSString *)path {
NSArray *languages = nil;
#if 0 // the component might not yet be awake !
- languages = [self hasSession]
- ? [[self session] languages]
- : [[[self context] request] browserLanguages];
+ languages = [[self context] resourceLookupLanguages];
#endif
return [[self resourceManager]
languages:languages];
}
- (void)setBaseURL:(NSURL *)_url {
- _setExtraVar(self, @"__wobaseurl", _url);
+ ASSIGNCOPY(self->wocBaseURL, _url);
}
- (NSURL *)baseURL {
NSURL *url;
- if ((url = _getExtraVar(self, @"__wobaseurl")))
- return url;
+ if (self->wocBaseURL)
+ return self->wocBaseURL;
url = [(WOApplication *)[self application] baseURL];
- url = [NSURL URLWithString:@"WebServerResources" relativeToURL:url];
- return url;
+ self->wocBaseURL =
+ [[NSURL URLWithString:@"WebServerResources" relativeToURL:url] copy];
+ return self->wocBaseURL;
}
- (NSString *)componentActionURLForContext:(WOContext *)_ctx {
return [@"/" stringByAppendingString:[self name]];
}
-- (WOApplication *)application {
+- (id)application {
if (self->application == nil)
return (self->application = [WOApplication application]);
return self->application;
}
- (id)existingSession {
- if (self->session)
+ if (self->session != nil)
return self->session;
if ([[self context] hasSession])
return nil;
}
-- (WOSession *)session {
+- (id)session {
if (self->session == nil) {
if ((self->session = [[self context] session]) == nil) {
- [self logWithFormat:@"could not get session object from context %@",
+ [self debugWithFormat:@"could not get session object from context %@",
self->context];
}
}
if (self->session == nil)
- [self logWithFormat:@"WARNING: missing session for component!"];
+ [self warnWithFormat:@"missing session for component!"];
return self->session;
}
self->context = _ctx;
}
- (WOContext *)context {
- if (self->context)
+ if (self->context != nil)
return self->context;
[self debugWithFormat:
- @"WARNING: missing context in component 0x%08X (component%s)",
+ @"missing context in component 0x%08X (component%s)",
self,
self->componentFlags.isAwake ? " is awake" : " is not awake"];
if (abortOnMissingCtx) {
- [self logWithFormat:@"aborting, because ctx is missing !"];
+ [self errorWithFormat:@"aborting, because ctx is missing !"];
abort();
}
-
+
if (self->application == nil)
self->application = [WOApplication application];
[self _setContext:[self->application context]];
if (self->context == nil)
- [self logWithFormat:@"WARNING: could not determine context object!"];
+ [self warnWithFormat:@"could not determine context object!"];
return self->context;
}
}
- (void)setCachingEnabled:(BOOL)_flag {
- self->componentFlags.reloadTemplates = _flag ? NO : YES;
+ self->componentFlags.reloadTemplates = _flag ? 0 : 1;
}
- (BOOL)isCachingEnabled {
- return (self->componentFlags.reloadTemplates == NO) ? YES : NO;
+ return (!self->componentFlags.reloadTemplates) ? YES : NO;
}
-- (WOComponent *)pageWithName:(NSString *)_name {
- NSArray *languages = nil;
+- (id)pageWithName:(NSString *)_name {
+ NSArray *languages;
WOResourceManager *rm;
WOComponent *component;
- languages = [self hasSession]
- ? [(WOSession *)[self session] languages]
- : [[[self context] request] browserLanguages];
-
- rm = [self resourceManager];
-
+ languages = [[self context] resourceLookupLanguages];
+ rm = [self resourceManager];
+
/*
Note: this API is somewhat broken since the component expects the
-initWithContext: message for initialization yet we pass no
NSArray *langs;
IS_DEPRECATED;
- langs = [self hasSession]
- ? [(WOSession *)[self session] languages]
- : [[[self context] request] browserLanguages];
+ langs = [[self context] resourceLookupLanguages];
return [[[self application]
resourceManager]
- (void)setName:(NSString *)_name {
if (![_name isNotNull])
- [self logWithFormat:@"WARNING: setting 'nil' name on component!"];
+ [self warnWithFormat:@"setting 'nil' name on component!"];
ASSIGNCOPY(self->wocName, _name);
}
- (void)setParent:(WOComponent *)_parent {
self->parentComponent = _parent;
}
-- (WOComponent *)parent {
+- (id)parent {
return self->parentComponent;
}
}
- (NSString *)pathForResourceNamed:(NSString *)_name ofType:(NSString *)_ext {
- NSFileManager *fm = [NSFileManager defaultManager];
- NSEnumerator *languages = nil;
- NSString *language = nil;
+ NSFileManager *fm;
+ NSEnumerator *languages;
+ NSString *language;
BOOL isDirectory = NO;
- NSString *cpath = [self path];
+ NSString *cpath;
if (_ext) _name = [_name stringByAppendingPathExtension:_ext];
- if (cpath == nil) {
- NSLog(@"WARNING: no path set in component %@", [self name]);
+ if ((cpath = [self path]) == nil) {
+ [self warnWithFormat:@"no path set in component %@", [self name]];
return nil;
}
+
+ fm = [NSFileManager defaultManager];
if (![fm fileExistsAtPath:cpath isDirectory:&isDirectory]) {
- NSLog(@"WARNING: component directory %@ does not exist !", cpath);
+ [self warnWithFormat:@"component directory %@ does not exist !", cpath];
return nil;
}
if (!isDirectory) {
- NSLog(@"WARNING: component path %@ is not a directory !", cpath);
+ [self warnWithFormat:@"component path %@ is not a directory !", cpath];
return nil;
}
+
+ /* check in language projects */
- // check in language projects
-
- languages = [[(WOSession *)[self session] languages] objectEnumerator];
- while ((language = [languages nextObject])) {
- language = [[cpath stringByAppendingPathComponent:
- [language stringByAppendingPathExtension:@"lproj"]]
- stringByAppendingPathExtension:_name];
+ languages = [self hasSession]
+ ? [[(WOSession *)[self session] languages] objectEnumerator]
+ : [[[[self context] request] browserLanguages] objectEnumerator];
+
+ while ((language = [languages nextObject]) != nil) {
+ language = [language stringByAppendingPathExtension:@"lproj"];
+ language = [cpath stringByAppendingPathComponent:language];
+ language = [language stringByAppendingPathExtension:_name];
if ([fm fileExistsAtPath:language])
return language;
}
-
- // check in component
+
+ /* check in component */
cpath = [cpath stringByAppendingPathComponent:_name];
if ([fm fileExistsAtPath:cpath])
return cpath;
/* template */
-- (WOElement *)templateWithHTMLString:(NSString *)_html
++ (WOElement *)templateWithHTMLString:(NSString *)_html
declarationString:(NSString *)_wod
languages:(NSArray *)_languages
{
declarationString:(NSString *)_wod
{
IS_DEPRECATED;
- return [self templateWithHTMLString:_html
- declarationString:_wod
- languages:[(WOSession *)[self session] languages]];
+ return [[self class] templateWithHTMLString:_html
+ declarationString:_wod
+ languages:[(WOSession *)[self session] languages]];
}
- (WOElement *)templateWithName:(NSString *)_name {
WOElement *tmpl;
if ((resourceManager = [self resourceManager]) == nil) {
- [self logWithFormat:@"ERROR(%s): could not determine resource manager !",
- __PRETTY_FUNCTION__];
+ [self errorWithFormat:@"%s: could not determine resource manager !",
+ __PRETTY_FUNCTION__];
return nil;
}
- languages = [self hasSession]
- ? [(WOSession *)[self session] languages]
- : [[[self context] request] browserLanguages];
-
- tmpl = [resourceManager templateWithName:_name languages:languages];
- if (debugTemplates) [self logWithFormat:@"found template: %@", tmpl];
+ languages = [[self context] resourceLookupLanguages];
+ tmpl = [resourceManager templateWithName:_name languages:languages];
+
+ if (debugTemplates) [self debugWithFormat:@"found template: %@", tmpl];
return tmpl;
}
- (WOElement *)_woComponentTemplate {
WOElement *element;
- if ((element = _getExtraVar(self, @"__wotemplate")))
+ // TODO: move to ivar?
+ if ((element = _getExtraVar(self, @"__wotemplate")) != nil)
return element;
return [self templateWithName:[self name]];
child = [child resolveWithParent:self];
if (child == nil) {
- [self logWithFormat:@"Could not resolve component fault: %@", _name];
+ [self warnWithFormat:@"Could not resolve component fault: %@", _name];
return nil;
}
}
- (BOOL)synchronizesVariablesWithBindings {
- return YES;
+ return [self isStateless] ? NO : YES;
}
- (void)setValue:(id)_value forBinding:(NSString *)_name {
if (parent == nil) {
parent = [self parent];
- NSLog(@"WARNING: tried to set value of binding '%@' in component '%@' "
- @"without parent component (parent is '%@') !",
- _name, [self name], [parent name]);
+ [self warnWithFormat:@"tried to set value of binding '%@' in component "
+ @"'%@' without parent component (parent is '%@') !",
+ _name, [self name], [parent name]];
}
[[self retain] autorelease];
if (parent == nil) {
parent = [self parent];
- NSLog(@"WARNING: tried to retrieve value of binding '%@' in"
- @" component '%@' without parent component (parent is '%@') !",
- _name, [self name], [parent name]);
+ [self warnWithFormat:@"tried to retrieve value of binding '%@' in"
+ @" component '%@' without parent component (parent is '%@') !",
+ _name, [self name], [parent name]];
}
[[self retain] autorelease];
if (parent == nil) return nil;
if (action == NULL) return nil;
-
+
NSAssert(parent != self, @"parent component equals current component");
if (![parent respondsToSelector:action]) {
- [self logWithFormat:@"parent %@ doesn't respond to %@",
- [parent name], _name];
+ [self debugWithFormat:@"parent %@ doesn't respond to %@",
+ [parent name], _name];
return nil;
}
/* OWResponder */
-- (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c{
+- (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c {
+ if (debugTakeValues)
+ [self debugWithFormat:@"%s: default says no.", __PRETTY_FUNCTION__];
return NO;
}
- (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
WOElement *template = nil;
+ if (debugTakeValues)
+ [self debugWithFormat:@"take values from rq 0x%08X", _req];
+
NSAssert1(self->componentFlags.isAwake,
@"component %@ is not awake !", self);
[self _setContext:_ctx];
template = [self _woComponentTemplate];
- if (template == nil)
+ if (template == nil) {
+ if (debugTakeValues)
+ [self debugWithFormat:@"cannot take values, component has no template!"];
return;
-
+ }
+
if (template->takeValues) {
template->takeValues(template,
@selector(takeValuesFromRequest:inContext:),
@"component %@ is not awake !", self);
if (debugOn) {
if (self->context != _ctx) {
- [self debugWithFormat:@"WARNING(%s): component ctx != ctx (%@ vs %@)",
+ [self debugWithFormat:@"%s: component ctx != ctx (%@ vs %@)",
__PRETTY_FUNCTION__, self->context, _ctx];
}
}
return;
}
- if (profElements)
+ if (perfLogger)
st = [[NSDateClass date] timeIntervalSince1970];
if (template->appendResponse) {
else
[template appendToResponse:_response inContext:_ctx];
- if (profElements) {
+ if (perfLogger) {
NSTimeInterval diff;
int i;
diff = [[NSDateClass date] timeIntervalSince1970] - st;
for (i = [_ctx componentStackCount]; i >= 0; i--)
printf(" ");
#endif
- printf("Template %s (comp %s): %0.3fs\n",
- [[_ctx elementID] cString],
- [[self name] cString],
- diff);
+ [perfLogger logWithFormat:@"Template %@ (comp %@): %0.3fs\n",
+ [_ctx elementID],
+ [self name],
+ diff];
}
}
response = [WOResponse responseWithRequest:[ctx request]];
if (ctxID == nil) {
- [self logWithFormat:@"missing ctx-id for context %@", ctx];
+ [self debugWithFormat:@"missing ctx-id for context %@", ctx];
ctxID = @"noctx";
}
if ([self logComponentVariableCreations]) {
/* only if we have a subclass, we can store values in ivars ... */
if (![[self->wocVariables objectForKey:_key] isNotNull]) {
- [self logWithFormat:@"Created component variable (class=%@): '%@'.",
+ [self debugWithFormat:
+ @"Created component variable (class=%@): '%@'.",
NSStringFromClass(self->isa), _key];
}
}
return;
}
- [self logWithFormat:
+ [self debugWithFormat:
@"value %@ could not set via method or KVC "
@"(self responds to %@: %s).",
- _key, _key,
+ _key, _key,
[self respondsToSelector:NSSelectorFromString(_key)] ? "yes" : "no"];
#if 0
return NO;
return value;
#if DEBUG && 0
- [self logWithFormat:@"KVC: accessed the component variable %@", _key];
+ [self debugWithFormat:@"KVC: accessed the component variable %@", _key];
#endif
- if ((value = [self objectForKey:_key]))
+ if ((value = [self objectForKey:_key]) != nil)
return value;
return nil;
- (void)setValue:(id)_value forUndefinedKey:(NSString *)_key {
// Note: this is not used on libFoundation, insufficient KVC implementation
+
+#if DEBUG && 0
+ [self logWithFormat:@"KVC: set the component variable %@: %@",_key,_value];
+#endif
if (_value == nil) {
#if 0
@"storing <nil> value in component variable %@", _key];
#endif
- if ([self->wocVariables objectForKey:_key])
+ if ([self->wocVariables objectForKey:_key] != nil)
[self setObject:nil forKey:_key];
return;
if ([self logComponentVariableCreations]) {
/* only if we have a subclass, we can store values in ivars ... */
if (![[self->wocVariables objectForKey:_key] isNotNull]) {
- [self logWithFormat:@"Created component variable (class=%@): '%@'.",
- NSStringFromClass(self->isa), _key];
+ [self debugWithFormat:@"Created component variable (class=%@): '%@'.",
+ NSStringFromClass(self->isa), _key];
}
}
#endif
-
[self setObject:_value forKey:_key];
}
+
- (id)valueForUndefinedKey:(NSString *)_key {
// Note: this is not used on libFoundation, insufficient KVC implementation
#if DEBUG && 0
- [self logWithFormat:@"KVC: accessed the component variable %@", _key];
+ [self debugWithFormat:@"KVC: accessed the component variable %@", _key];
#endif
+
return [self objectForKey:_key];
}
- (void)unableToSetNilForKey:(NSString *)_key {
// TODO: should we call setValue:NSNull forKey?
- [self logWithFormat:@"ERROR: unable to set 'nil' for key: '%@'", _key];
+ [self errorWithFormat:@"unable to set 'nil' for key: '%@'", _key];
}
#endif /* KVC */
- (void)validationFailedWithException:(NSException *)_exception
value:(id)_value keyPath:(NSString *)_keyPath
{
- [self logWithFormat:
- @"WARNING: formatter failed for value %@ (keyPath=%@): %@",
+ [self warnWithFormat:
+ @"formatter failed for value %@ (keyPath=%@): %@",
_value, _keyPath, [_exception reason]];
}
- (id)unarchiver:(EOKeyValueUnarchiver *)_archiver
objectForReference:(id)_keyPath
{
+ /*
+ This is used when a .woo file is unarchived. Eg datasources contain
+ bindings in the archive:
+
+ editingContext = session.defaultEditingContext;
+
+ The binding will evaluate against the component during loading.
+ */
return [self valueForKeyPath:_keyPath];
}
return str;
}
-@end /* WOComponent */
-
-@implementation WOComponent(Statistics)
+/* Statistics */
- (NSString *)descriptionForResponse:(WOResponse *)_response
inContext:(WOContext *)_context
return [self name];
}
-@end /* WOComponent(Statistics) */
-
-@implementation WOComponent(AdvancedBindingAccessors)
+/* AdvancedBindingAccessors */
- (void)setUnsignedIntValue:(unsigned)_value forBinding:(NSString *)_name {
[self setValue:[NSNumber numberWithUnsignedInt:_value] forBinding:_name];
}
#endif
-@end /* WOComponent(AdvancedBindingAccessors) */
+@end /* WOComponent */