/* Copyright (C) 2000-2004 SKYRIX Software AG This file is part of OpenGroupware.org. OGo 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 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 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "NSObject+WO.h" #include "WOComponent+private.h" #include "WOContext+private.h" #include "WOApplication+private.h" #include #include #include #include #import #include "WOElementID.h" #include "common.h" #include @interface WOContext(Privates5) - (NSArray *)_componentStack; @end @interface WOComponent(Cursors) - (void)pushCursor:(id)_obj; - (id)popCursor; - (id)cursor; @end static Class WOAppClass = Nil; @implementation WOContext + (int)version { return 7; } static Class MutableStrClass = Nil; static int contextCount = 0; static int logComponents = -1; static int relativeURLs = -1; static BOOL debugOn = NO; static int debugCursor = -1; static BOOL debugComponentAwake = NO; static BOOL testNSURLs = NO; static BOOL newCURLStyle = NO; static NSString *WOApplicationSuffix = nil; + (void)initialize { NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; static BOOL didInitialize = NO; if (didInitialize) return; if (WOAppClass == Nil) WOAppClass = [WOApplication class]; if (MutableStrClass == Nil) MutableStrClass = [NSMutableString class]; didInitialize = YES; logComponents = [[ud objectForKey:@"WOLogComponents"] boolValue] ? 1 : 0; relativeURLs = [[ud objectForKey:@"WOUseRelativeURLs"] boolValue]? 1 : 0; debugCursor = [ud boolForKey:@"WODebugCursor"] ? 1 : 0; debugComponentAwake = [ud boolForKey:@"WODebugComponentAwake"]; WOApplicationSuffix = [[ud stringForKey:@"WOApplicationSuffix"] copy]; } + (id)contextWithRequest:(WORequest *)_request { return [[(WOContext *)[self alloc] initWithRequest:_request] autorelease]; } - (id)initWithRequest:(WORequest *)_request { if ((self = [super init])) { unsigned char buf[24]; self->qpJoin = @"&"; sprintf(buf, "%03x%08x%08x", ++contextCount, (int)time(NULL), (int)self); self->ctxId = [[NSString alloc] initWithCString:buf]; self->elementID = [[WOElementID alloc] init]; self->awakeComponents = [[NSMutableSet alloc] initWithCapacity:64]; self->request = [_request retain]; self->response = [[WOResponse responseWithRequest:_request] retain]; } return self; } + (id)context { return [[[self alloc] init] autorelease]; } - (id)init { return [self initWithRequest:nil]; } /* components */ - (void)_addAwakeComponent:(WOComponent *)_component { if (_component == nil) return; if ([self->awakeComponents containsObject:_component]) return; /* wake up component */ if (debugComponentAwake) [self logWithFormat:@"mark component awake: %@", _component]; [self->awakeComponents addObject:_component]; } - (void)_awakeComponent:(WOComponent *)_component { if (_component == nil) return; if ([self->awakeComponents containsObject:_component]) return; /* wake up component */ if (debugComponentAwake) [self logWithFormat:@"awake component: %@", _component]; [_component _awakeWithContext:self]; [self _addAwakeComponent:_component]; if (debugComponentAwake) [self logWithFormat:@"woke up component: %@", _component]; } - (void)sleepComponents { NSEnumerator *e; WOComponent *component; BOOL sendSleepToPage; if (debugComponentAwake) { [self logWithFormat:@"sleep %d components ...", [self->awakeComponents count]]; } sendSleepToPage = YES; e = [self->awakeComponents objectEnumerator]; while ((component = [e nextObject])) { if (debugComponentAwake) [self logWithFormat:@" sleep component: %@", component]; [component _sleepWithContext:self]; if (component == self->page) sendSleepToPage = NO; } if (sendSleepToPage && (self->page != nil)) { if (debugComponentAwake) [self logWithFormat:@" sleep page: %@", self->page]; [self->page _sleepWithContext:self]; } if (debugComponentAwake) { [self logWithFormat:@"done sleep %d components.", [self->awakeComponents count]]; } [self->awakeComponents removeAllObjects]; } #if WITH_DEALLOC_OBSERVERS - (void)addDeallocObserver:(id)_observer { if (_observer == NULL) return; /* check array */ if (self->deallocObservers == NULL) { self->deallocObservers = calloc(8, sizeof(id)); self->deallocObserverCount = 0; self->deallocObserverCapacity = 8; } /* check capacity */ if (self->deallocObserverCapacity == self->deallocObserverCount) { /* need to increase array */ id *newa; newa = calloc(self->deallocObserverCapacity * 2, sizeof(id)); memcpy(newa, self->deallocObservers, sizeof(id) * self->deallocObserverCount); free(self->deallocObservers); self->deallocObservers = newa; self->deallocObserverCapacity *= 2; } /* register */ self->deallocObservers[self->deallocObserverCount] = _observer; self->deallocObserverCount++; } - (void)removeDeallocObserver:(id)_observer { /* the observer currently will only grow (this should be OK for WOContext) */ register int i; if (_observer == NULL) return; for (i = self->deallocObserverCount - 1; i >= 0; i++) { if ((self->deallocObservers[i]) == _observer) self->deallocObservers[i] = NULL; } } #endif - (void)dealloc { [self sleepComponents]; #if WITH_DEALLOC_OBSERVERS if (self->deallocObservers) { register int i; #if DEBUG printf("%s: dealloc observer capacity: %i\n", __PRETTY_FUNCTION__, self->deallocObserverCapacity); #endif /* GC!! process in reverse order ... */ for (i = self->deallocObserverCount - 1; i >= 0; i++) [self->deallocObservers[i] _objectWillDealloc:self]; free(self->deallocObservers); self->deallocObservers = NULL; } #endif [self->activeUser release]; [self->rootURL release]; [self->objectPermissionCache release]; [self->traversalStack release]; [self->clientObject release]; [self->objectDispatcher release]; [self->soRequestType release]; [self->pathInfo release]; [[NSNotificationCenter defaultCenter] postNotificationName:@"WOContextWillDeallocate" object:self->ctxId]; { /* release component stack */ int i; for (i = (self->componentStackCount - 1); i >= 0; i--) { [self->componentStack[i] release]; self->componentStack[i] = nil; [self->contentStack[i] release]; self->contentStack[i] = nil; } } [self->urlPrefix release]; [self->elementID release]; [self->reqElementID release]; [self->activeFormElement release]; [self->page release]; [self->awakeComponents release]; [self->appURL release]; [self->baseURL release]; [self->session release]; [self->variables release]; [self->request release]; [self->response release]; [self->ctxId release]; [super dealloc]; } - (void)setSession:(WOSession *)_session { ASSIGN(self->session, _session); } - (WOSession *)session { // in WO4 -session creates a new session if none is associated if (self->session == nil) { [[self application] _initializeSessionInContext:self]; if (self->session == nil) [self logWithFormat:@"%s: missing session for context ..", __PRETTY_FUNCTION__]; } return self->session; } - (NSString *)contextID { NSAssert(self->ctxId, @"context without id !"); #if 0 // in WO4 -contextID returns nil if there is no associated session return self->session ? self->ctxId : nil; #else /* IMHO the above isn't true, otherwise session cannot be automagically generated! TODO: well, we might want to generate component URLs which work without a session - at least in theory the ID tree should be stable even without a session (and if proper uids are used for dynamic content). eg this would be quite useful for SOPE. */ return self->ctxId; #endif } - (WORequest *)request { return self->request; } - (WOResponse *)response { return self->response; } - (BOOL)hasSession { return (self->session != nil) ? YES : NO; } - (BOOL)savePageRequired { return self->savePageRequired; } /* cursors */ - (void)pushCursor:(id)_obj { if (debugCursor == -1) { debugCursor = [[NSUserDefaults standardUserDefaults] boolForKey:@"WODebugCursor"] ? 1 : 0; } if (debugCursor) [self logWithFormat:@"enter cursor: %@", _obj]; [[self component] pushCursor:_obj]; } - (id)popCursor { if (debugCursor) [self logWithFormat:@"leave cursor ..."]; return [[self component] popCursor]; } - (id)cursor { return [(id )[self component] cursor]; } /* components */ - (WOComponent *)component { return (self->componentStackCount > 0) ? self->componentStack[self->componentStackCount - 1] : nil; } - (void)setPage:(WOComponent *)_page { [_page ensureAwakeInContext:self]; ASSIGN(self->page, _page); } - (WOComponent *)page { return self->page; } void WOContext_enterComponent (WOContext *self, WOComponent *_component, WOElement *_content) { WOComponent *parent = nil; #if DEBUG NSCAssert(_component, @"missing component to enter ..."); #endif if (logComponents) { [self->application logWithFormat:@"enter component %@ (content=%@) ..", [_component name], _content]; } parent = self->componentStackCount > 0 ? self->componentStack[self->componentStackCount - 1] : nil; NSCAssert2(self->componentStackCount < NGObjWeb_MAX_COMPONENT_NESTING_DEPTH, @"exceeded maximum component nesting depth (%i):\n%@", NGObjWeb_MAX_COMPONENT_NESTING_DEPTH, [self _componentStack]); self->componentStack[(int)self->componentStackCount] = [_component retain]; self->contentStack[(int)self->componentStackCount] = [_content retain]; self->componentStackCount++; [self _awakeComponent:_component]; if (parent) { if ([_component synchronizesVariablesWithBindings]) WOComponent_syncFromParent(_component, parent); } } void WOContext_leaveComponent(WOContext *self, WOComponent *_component) { WOComponent *parent = nil; BEGIN_PROFILE; parent = (self->componentStackCount > 1) ? self->componentStack[self->componentStackCount - 2] : nil; if (parent) { if ([_component synchronizesVariablesWithBindings]) WOComponent_syncToParent(_component, parent); } PROFILE_CHECKPOINT("after sync"); /* remove last object */ self->componentStackCount--; NSCAssert(self->componentStackCount >= 0, @"tried to pop component from empty component stack !"); [self->componentStack[(int)self->componentStackCount] release]; self->componentStack[(int)self->componentStackCount] = nil; [self->contentStack[(int)self->componentStackCount] release]; self->contentStack[(int)self->componentStackCount] = nil; if (logComponents) [self->application logWithFormat:@"left component %@.", [_component name]]; END_PROFILE; } - (void)enterComponent:(WOComponent *)_comp content:(WOElement *)_content { WOContext_enterComponent(self, _comp, _content); } - (void)leaveComponent:(WOComponent *)_component { BEGIN_PROFILE; WOContext_leaveComponent(self, _component); END_PROFILE; } - (WOComponent *)parentComponent { return (self->componentStackCount > 1) ? self->componentStack[(int)self->componentStackCount - 2] : nil; } - (WODynamicElement *)componentContent { return (self->componentStackCount > 0) ? self->contentStack[(int)self->componentStackCount - 1] : nil; } - (unsigned)componentStackCount { return self->componentStackCount; } - (NSArray *)_componentStack { return [NSArray arrayWithObjects:self->componentStack count:self->componentStackCount]; } /* URLs */ - (NSURL *)serverURL { WORequest *rq; NSString *serverURL; NSURL *url; NSString *host; if ((rq = [self request]) == nil) { [self logWithFormat:@"missing request in -baseURL call .."]; return nil; } if ((serverURL = [rq headerForKey:@"x-webobjects-server-url"]) == nil) { if ((host = [rq headerForKey:@"host"])) serverURL = [@"http://" stringByAppendingString:host]; } else { // TODO: fix that (host is also broken for example with SOUP) /* sometimes the port is broken in the server URL ... */ if ([serverURL hasSuffix:@":0"]) { // bad bad bad if ((host = [rq headerForKey:@"host"])) { NSString *scheme; scheme = [serverURL hasPrefix:@"https://"] ? @"https://" : @"http://"; serverURL = [scheme stringByAppendingString:host]; } } } if ([serverURL length] == 0) { [self logWithFormat: @"ERROR: could not find x-webobjects-server-url header !"]; return nil; } if ((url = [NSURL URLWithString:serverURL]) == nil) { [self logWithFormat:@"could not construct NSURL from string '%@'", serverURL]; return nil; } return url; } - (NSURL *)baseURL { WORequest *rq; NSURL *serverURL; if (self->baseURL) return self->baseURL; if ((rq = [self request]) == nil) { [self logWithFormat:@"missing request in -baseURL call .."]; return nil; } serverURL = [self serverURL]; self->baseURL = [[NSURL URLWithString:[rq uri] relativeToURL:serverURL] retain]; if (self->baseURL == nil) { [self logWithFormat: @"could not construct NSURL for uri '%@' and base '%@' ...", [rq uri], serverURL]; } return self->baseURL; } - (NSURL *)applicationURL { if (self->appURL == nil) { NSString *s; s = [self->request adaptorPrefix]; if ([s length] > 0) { s = [NSString stringWithFormat:@"%@/%@/", s, [self->request applicationName]]; } else s = [[self->request applicationName] stringByAppendingString:@"/"]; self->appURL = [[NSURL URLWithString:s relativeToURL:[self serverURL]] retain]; } return self->appURL; } - (NSURL *)urlForKey:(NSString *)_key { _key = [_key stringByAppendingString:@"/"]; return [NSURL URLWithString:_key relativeToURL:[self applicationURL]]; } /* forms */ - (void)setInForm:(BOOL)_form { self->inForm = _form; } - (BOOL)isInForm { return self->inForm; } - (void)addActiveFormElement:(WOElement *)_formElement { if (self->activeFormElement) { [[self component] debugWithFormat:@"active form element already set !"]; return; } ASSIGN(self->activeFormElement, _formElement); [self setRequestSenderID:[self elementID]]; } - (WOElement *)activeFormElement { return self->activeFormElement; } /* context variables (transient) */ - (void)setObject:(id)_obj forKey:(NSString *)_key { if (self->variables == nil) { self->variables = [[NSMutableDictionary allocWithZone:[self zone]] initWithCapacity:16]; } if (_obj) [self->variables setObject:_obj forKey:_key]; else [self->variables removeObjectForKey:_key]; } - (id)objectForKey:(NSString *)_key { return [self->variables objectForKey:_key]; } - (void)removeObjectForKey:(NSString *)_key { [self->variables removeObjectForKey:_key]; } - (NSDictionary *)variableDictionary { return self->variables; } - (void)takeValue:(id)_value forKey:(NSString *)_key { if (WOSetKVCValueUsingMethod(self, _key, _value)) // method is used return; else if (WOGetKVCGetMethod(self, _key) == NULL) { if (_value == nil) _value = [EONull null]; if (self->variables == nil) { self->variables = [[NSMutableDictionary allocWithZone:[self zone]] initWithCapacity:16]; } [self->variables setObject:_value forKey:_key]; return; } else { // only a 'get' method is defined for _key ! [self handleTakeValue:_value forUnboundKey:_key]; } } - (id)valueForKey:(NSString *)_key { id value; if ((value = WOGetKVCValueUsingMethod(self, _key))) return value; value = [self->variables objectForKey:_key]; return value; } /* NSCopying */ - (id)copyWithZone:(NSZone *)_zone { return [self retain]; } /* description */ - (NSString *)description { NSString *sid = nil; WOApplication *app = [self application]; if ([self hasSession]) sid = [[self session] sessionID]; return [NSString stringWithFormat: @"<0x%08X[%@]: %@ app=%@ sn=%@ eid=%@ rqeid=%@>", (unsigned)self, NSStringFromClass([self class]), [self contextID], [app name], sid ? sid : @"none", [self elementID], [self senderID]]; } @end /* WOContext */ @implementation WOContext(ElementIDs) - (NSString *)elementID { return [self->elementID elementID]; } - (void)appendElementIDComponent:(NSString *)_eid { [self->elementID appendElementIDComponent:_eid]; } - (void)appendZeroElementIDComponent { [self->elementID appendZeroElementIDComponent]; } - (void)deleteAllElementIDComponents { [self->elementID deleteAllElementIDComponents]; } - (void)deleteLastElementIDComponent { [self->elementID deleteLastElementIDComponent]; } - (void)incrementLastElementIDComponent { [self->elementID incrementLastElementIDComponent]; } - (void)appendIntElementIDComponent:(int)_eid { [self->elementID appendIntElementIDComponent:_eid]; } /* the following can be later moved to WOElementID */ - (id)currentElementID { return [self->reqElementID currentElementID]; } - (id)consumeElementID { return [self->reqElementID consumeElementID]; } @end /* WOContext(ElementIDs) */ @implementation WOContext(URLs) - (void)_generateCompleteURLs { /* described in Apple TIL article 70101 */ } - (NSString *)queryStringFromDictionary:(NSDictionary *)_queryDict { NSEnumerator *keys; NSString *key; BOOL isFirst; NSMutableString *qs; qs = [MutableStrClass stringWithCapacity:256]; keys = [_queryDict keyEnumerator]; for (isFirst = YES; (key = [keys nextObject]); ) { NSString *value; if (isFirst) isFirst = NO; else [qs appendString:self->qpJoin]; value = [[_queryDict objectForKey:key] stringValue]; key = [key stringByEscapingURL]; value = [value stringByEscapingURL]; [qs appendString:key]; if (value) { [qs appendString:@"="]; [qs appendString:value]; } } return qs; } - (NSString *)directActionURLForActionNamed:(NSString *)_actionName queryDictionary:(NSDictionary *)_queryDict { NSMutableString *url; NSString *qs; url = [MutableStrClass stringWithCapacity:256]; if (!testNSURLs) [url appendString:@"/"]; [url appendString:_actionName]; /* add query parameters */ qs = [self queryStringFromDictionary:_queryDict]; return [self urlWithRequestHandlerKey: [WOAppClass directActionRequestHandlerKey] path:url queryString:qs]; } - (NSString *)componentActionURL { // TODO: add a -cComponentActionURL // (without NSString for use with appendContentCString:) // Profiling: // 26% -urlWithRequestHandler... // 21% -elementID (was 40% !! :-) // ~20% mutable string ops if (newCURLStyle) { NSMutableString *qs; NSString *p; self->savePageRequired = YES; qs = [MutableStrClass stringWithCapacity:64]; [qs appendString:WORequestValueSenderID]; [qs appendString:@"="]; [qs appendString:[self elementID]]; [qs appendString:self->qpJoin]; [qs appendString:WORequestValueSessionID]; [qs appendString:@"="]; [qs appendString:[[self session] sessionID]]; [qs appendString:self->qpJoin]; [qs appendString:WORequestValueContextID]; [qs appendString:@"="]; [qs appendString:[self contextID]]; p = [[self page] componentActionURLForContext:self]; if (testNSURLs) { if ([p hasPrefix:@"/"]) p = [p substringFromIndex:1]; } return [self urlWithRequestHandlerKey: [WOAppClass componentRequestHandlerKey] path:p queryString:qs]; } else { /* old style URLs ... */ static NSMutableString *url = nil; // THREAD static IMP addStr = NULL; NSString *s; self->savePageRequired = YES; if (url == nil) { url = [[MutableStrClass alloc] initWithCapacity:256]; addStr = [url methodForSelector:@selector(appendString:)]; addStr(url, @selector(appendString:), @"/"); } else [url setString:@"/"]; /* Note: component actions *always* require sessions to be able to locate the request component ! */ addStr(url, @selector(appendString:), [[self session] sessionID]); addStr(url, @selector(appendString:), @"/"); addStr(url, @selector(appendString:), [self->elementID elementID]); s = [self urlWithRequestHandlerKey: [WOAppClass componentRequestHandlerKey] path:url queryString:nil]; return s; } } - (NSString *)urlWithRequestHandlerKey:(NSString *)_key path:(NSString *)_path queryString:(NSString *)_query { if (testNSURLs) { /* use NSURLs for processing */ NSURL *rqUrl; if ([_path hasPrefix:@"/"]) { #if DEBUG [self logWithFormat:@"WARNING: got absolute path '%@'", _path]; #endif _path = [_path substringFromIndex:1]; } if (_key == nil) _key = [WOAppClass componentRequestHandlerKey]; rqUrl = [self urlForKey:_key]; if ([_query length] > 0) { NSMutableString *s; s = [_path mutableCopy]; [s appendString:@"?"]; [s appendString:_query]; rqUrl = [NSURL URLWithString:s relativeToURL:rqUrl]; [s release]; } else rqUrl = [NSURL URLWithString:_path relativeToURL:rqUrl]; //[self logWithFormat:@"constructed component URL: %@", rqUrl]; return [rqUrl stringValueRelativeToURL:[self baseURL]]; } else { NSMutableString *url; NSString *tmp; IMP addStr; if (_key == nil) _key = [WOAppClass componentRequestHandlerKey]; url = [MutableStrClass stringWithCapacity:256]; addStr = [url methodForSelector:@selector(appendString:)]; /* static part */ if (self->urlPrefix == nil) { if (!relativeURLs) { if ((tmp = [self->request headerForKey:@"x-webobjects-server-url"])) { if ([tmp hasSuffix:@":0"] && [tmp length] > 2) // TODO: BAD BAD BAD tmp = [tmp substringToIndex:([tmp length] - 2)]; addStr(url, @selector(appendString:), tmp); } else if ((tmp = [self->request headerForKey:@"host"])) { addStr(url, @selector(appendString:), @"http://"); addStr(url, @selector(appendString:), tmp); } } addStr(url, @selector(appendString:), [self->request adaptorPrefix]); addStr(url, @selector(appendString:), @"/"); tmp = [[self request] applicationName]; if ([tmp length] == 0) tmp = [(WOApplication *)[self application] name]; if ([tmp length] > 0) { addStr(url, @selector(appendString:), tmp); if (WOApplicationSuffix) addStr(url, @selector(appendString:), WOApplicationSuffix); addStr(url, @selector(appendString:), @"/"); } /* cache prefix */ self->urlPrefix = [url copy]; if (debugOn) [self debugWithFormat:@"URL prefix: '%@'", self->urlPrefix]; } else { /* prefix is cached :-) */ addStr(url, @selector(appendString:), self->urlPrefix); } /* variable part */ addStr(url, @selector(appendString:), _key); if (_path) addStr(url, @selector(appendString:), _path); if ([_query length] > 0) { addStr(url, @selector(appendString:), @"?"); addStr(url, @selector(appendString:), _query); } return url; } } - (NSString *)completeURLWithRequestHandlerKey:(NSString *)_key path:(NSString *)_path queryString:(NSString *)_query isSecure:(BOOL)_isSecure port:(int)_port { NSMutableString *url = [MutableStrClass stringWithCapacity:256]; [url appendString:_isSecure ? @"https://" : @"http://"]; [url appendString:[[self request] headerForKey:@"host"]]; if (_port > 0) { if (!(_isSecure && _port == 443) && !(!_isSecure && _port == 80)) [url appendFormat:@":%i", _port]; } [url appendString:[self urlWithRequestHandlerKey:_key path:_path queryString:_query]]; return url; } - (void)setRequestSenderID:(NSString *)_rid { WOElementID *eid; eid = [[WOElementID alloc] initWithString:_rid]; [self->reqElementID release]; self->reqElementID = eid; } - (NSString *)senderID { #if 1 return [self->reqElementID elementID]; #else NSMutableString *eid; IMP addStr; int i; eid = [MutableStrClass stringWithCapacity:(self->reqElementIdCount * 4) + 1]; addStr = [eid methodForSelector:@selector(appendString:)]; for (i = 0; i < self->reqElementIdCount; i++) { if (i != 0) addStr(eid, @selector(appendString:), @"."); addStr(eid, @selector(appendString:), [self->reqElementId[i] stringValue]); } return eid; #endif } @end /* WOContext(URLs) */ @implementation WOContext(DeprecatedMethodsInWO4) - (WOApplication *)application { if (self->application == nil) self->application = [WOAppClass application]; if (self->application == nil) NSLog(@"%s: missing application for context %@", __PRETTY_FUNCTION__, self); return self->application; } - (void)setDistributionEnabled:(BOOL)_flag { IS_DEPRECATED; [[self session] setDistributionEnabled:_flag]; } - (BOOL)isDistributionEnabled { IS_DEPRECATED; return [[self session] isDistributionEnabled]; } - (NSString *)url { return [self componentActionURL]; } - (NSString *)urlSessionPrefix { NSMutableString *url; NSString *tmp; url = [MutableStrClass stringWithCapacity:128]; [url appendString:[[self request] adaptorPrefix]]; [url appendString:@"/"]; tmp = [[self request] applicationName]; [url appendString: tmp ? tmp : [(WOApplication *)[self application] name]]; #if DEBUG if ([url length] == 0) { NSLog(@"WARNING(%s): could not determine session URL prefix !", __PRETTY_FUNCTION__); } #endif return url; } @end /* WOContext(DeprecatedMethodsInWO4) */ @implementation WOComponent(Cursors) - (void)pushCursor:(id)_obj { NSMutableArray *ctxStack; if (debugCursor) [self logWithFormat:@"enter cursor: %@", _obj]; if ((ctxStack = [self objectForKey:@"_ODCycleCtx"]) == nil) { ctxStack = [NSMutableArray arrayWithCapacity:8]; [self setObject:ctxStack forKey:@"_ODCycleCtx"]; } /* add to cursor stack */ [ctxStack addObject:(_obj ? _obj : [NSNull null])]; /* set active cursor */ if (![_obj isNotNull]) _obj = nil; [self setObject:_obj forKey:@"_"]; } - (id)popCursor { NSMutableArray *ctxStack; id old; /* retrieve last context */ old = [[self objectForKey:@"_"] retain]; [self setObject:nil forKey:@"_"]; /* restore old ctx */ if ((ctxStack = [self objectForKey:@"_ODCycleCtx"])) { unsigned count; if ((count = [ctxStack count]) > 0) { [ctxStack removeObjectAtIndex:(count - 1)]; count--; if (count > 0) { id obj; obj = [ctxStack objectAtIndex:(count - 1)]; if (![obj isNotNull]) obj = nil; [self setObject:obj forKey:@"_"]; } } } #if DEBUG else { [self debugWithFormat:@"WARNING: -popCursor called without cycle ctx !"]; } #endif if (debugCursor) { [self logWithFormat:@"leave cursor: %@ (restored=%@)", old, [self cursor]]; } return [old autorelease]; } - (id)cursor { NSMutableArray *ctxStack; // TODO: why do we check for _ODCycleCtx, if we query '_' ? if ((ctxStack = [self objectForKey:@"_ODCycleCtx"]) == nil) /* no cycle context setup for component ... */ return self; if ([ctxStack count] == 0) /* nothing contained in cycle context ... */ return self; return [self objectForKey:@"_"]; } @end /* WOComponent(Cursors) */