2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
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
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.
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
22 #include <NGObjWeb/WOContext.h>
23 #include "NSObject+WO.h"
24 #include "WOComponent+private.h"
25 #include "WOContext+private.h"
26 #include "WOApplication+private.h"
27 #include <NGObjWeb/WOApplication.h>
28 #include <NGObjWeb/WORequest.h>
29 #include <NGObjWeb/WOResponse.h>
30 #include <NGObjWeb/WOSession.h>
31 #import <EOControl/EONull.h>
32 #include "WOElementID.h"
37 @interface WOContext(Privates5)
38 - (NSArray *)_componentStack;
41 @interface WOComponent(Cursors)
42 - (void)pushCursor:(id)_obj;
47 static Class WOAppClass = Nil;
49 @implementation WOContext
55 static Class WOContextClass = Nil;
56 static Class MutableStrClass = Nil;
57 static int contextCount = 0;
58 static int logComponents = -1;
59 static int relativeURLs = -1;
60 static BOOL debugOn = NO;
61 static int debugCursor = -1;
62 static BOOL debugComponentAwake = NO;
63 static BOOL testNSURLs = NO;
64 static BOOL newCURLStyle = NO;
65 static NSString *WOApplicationSuffix = nil;
68 static BOOL didInit = NO;
76 ud = [NSUserDefaults standardUserDefaults];
78 if (WOAppClass == Nil)
79 WOAppClass = [WOApplication class];
80 if (MutableStrClass == Nil)
81 MutableStrClass = [NSMutableString class];
83 cn = [ud stringForKey:@"WOContextClass"];
84 WOContextClass = NSClassFromString(cn);
85 NSAssert1(WOContextClass != Nil,
86 @"Couldn't instantiate WOContextClass (%@)!", cn);
88 logComponents = [[ud objectForKey:@"WOLogComponents"] boolValue] ? 1 : 0;
89 relativeURLs = [[ud objectForKey:@"WOUseRelativeURLs"] boolValue]? 1 : 0;
90 debugCursor = [ud boolForKey:@"WODebugCursor"] ? 1 : 0;
91 debugComponentAwake = [ud boolForKey:@"WODebugComponentAwake"];
92 WOApplicationSuffix = [[ud stringForKey:@"WOApplicationSuffix"] copy];
95 + (id)contextWithRequest:(WORequest *)_r {
96 return [[(WOContext *)[WOContextClass alloc] initWithRequest:_r] autorelease];
99 - (id)initWithRequest:(WORequest *)_request {
100 if ((self = [super init])) {
101 unsigned char buf[24];
104 sprintf(buf, "%03x%08x%08x", ++contextCount, (int)time(NULL), (int)self);
105 self->ctxId = [[NSString alloc] initWithCString:buf];
107 self->elementID = [[WOElementID alloc] init];
108 self->awakeComponents = [[NSMutableSet alloc] initWithCapacity:64];
110 self->request = [_request retain];
111 self->response = [[WOResponse responseWithRequest:_request] retain];
117 return [[[self alloc] init] autorelease];
120 return [self initWithRequest:nil];
125 - (void)_addAwakeComponent:(WOComponent *)_component {
126 if (_component == nil)
129 if ([self->awakeComponents containsObject:_component])
132 /* wake up component */
133 if (debugComponentAwake)
134 [self logWithFormat:@"mark component awake: %@", _component];
136 [self->awakeComponents addObject:_component];
139 - (void)_awakeComponent:(WOComponent *)_component {
140 if (_component == nil)
143 if ([self->awakeComponents containsObject:_component])
146 /* wake up component */
147 if (debugComponentAwake)
148 [self logWithFormat:@"awake component: %@", _component];
150 [_component _awakeWithContext:self];
152 [self _addAwakeComponent:_component];
154 if (debugComponentAwake)
155 [self logWithFormat:@"woke up component: %@", _component];
158 - (void)sleepComponents {
160 WOComponent *component;
161 BOOL sendSleepToPage;
163 if (debugComponentAwake) {
164 [self logWithFormat:@"sleep %d components ...",
165 [self->awakeComponents count]];
168 sendSleepToPage = YES;
169 e = [self->awakeComponents objectEnumerator];
170 while ((component = [e nextObject])) {
171 if (debugComponentAwake)
172 [self logWithFormat:@" sleep component: %@", component];
173 [component _sleepWithContext:self];
174 if (component == self->page) sendSleepToPage = NO;
176 if (sendSleepToPage && (self->page != nil)) {
177 if (debugComponentAwake)
178 [self logWithFormat:@" sleep page: %@", self->page];
179 [self->page _sleepWithContext:self];
182 if (debugComponentAwake) {
183 [self logWithFormat:@"done sleep %d components.",
184 [self->awakeComponents count]];
186 [self->awakeComponents removeAllObjects];
189 #if WITH_DEALLOC_OBSERVERS
190 - (void)addDeallocObserver:(id)_observer {
191 if (_observer == NULL) return;
194 if (self->deallocObservers == NULL) {
195 self->deallocObservers = calloc(8, sizeof(id));
196 self->deallocObserverCount = 0;
197 self->deallocObserverCapacity = 8;
201 if (self->deallocObserverCapacity == self->deallocObserverCount) {
202 /* need to increase array */
205 newa = calloc(self->deallocObserverCapacity * 2, sizeof(id));
206 memcpy(newa, self->deallocObservers,
207 sizeof(id) * self->deallocObserverCount);
208 free(self->deallocObservers);
209 self->deallocObservers = newa;
210 self->deallocObserverCapacity *= 2;
214 self->deallocObservers[self->deallocObserverCount] = _observer;
215 self->deallocObserverCount++;
217 - (void)removeDeallocObserver:(id)_observer {
218 /* the observer currently will only grow (this should be OK for WOContext) */
220 if (_observer == NULL) return;
222 for (i = self->deallocObserverCount - 1; i >= 0; i++) {
223 if ((self->deallocObservers[i]) == _observer)
224 self->deallocObservers[i] = NULL;
230 [self sleepComponents];
232 #if WITH_DEALLOC_OBSERVERS
233 if (self->deallocObservers) {
237 printf("%s: dealloc observer capacity: %i\n",
238 __PRETTY_FUNCTION__, self->deallocObserverCapacity);
241 /* GC!! process in reverse order ... */
242 for (i = self->deallocObserverCount - 1; i >= 0; i++)
243 [self->deallocObservers[i] _objectWillDealloc:self];
245 free(self->deallocObservers);
246 self->deallocObservers = NULL;
250 [self->activeUser release];
251 [self->rootURL release];
252 [self->objectPermissionCache release];
253 [self->traversalStack release];
254 [self->clientObject release];
255 [self->objectDispatcher release];
256 [self->soRequestType release];
257 [self->pathInfo release];
259 [[NSNotificationCenter defaultCenter]
260 postNotificationName:@"WOContextWillDeallocate"
263 { /* release component stack */
265 for (i = (self->componentStackCount - 1); i >= 0; i--) {
266 [self->componentStack[i] release]; self->componentStack[i] = nil;
267 [self->contentStack[i] release]; self->contentStack[i] = nil;
271 [self->urlPrefix release];
272 [self->elementID release];
273 [self->reqElementID release];
274 [self->activeFormElement release];
275 [self->page release];
276 [self->awakeComponents release];
277 [self->appURL release];
278 [self->baseURL release];
279 [self->session release];
280 [self->variables release];
281 [self->request release];
282 [self->response release];
283 [self->ctxId release];
287 - (void)setSession:(WOSession *)_session {
288 ASSIGN(self->session, _session);
291 - (WOSession *)session {
292 // in WO4 -session creates a new session if none is associated
294 if (self->session == nil) {
295 [[self application] _initializeSessionInContext:self];
297 if (self->session == nil)
298 [self logWithFormat:@"%s: missing session for context ..",
299 __PRETTY_FUNCTION__];
302 return self->session;
305 - (NSString *)contextID {
306 NSAssert(self->ctxId, @"context without id !");
308 // in WO4 -contextID returns nil if there is no associated session
309 return self->session ? self->ctxId : nil;
312 IMHO the above isn't true, otherwise session cannot be automagically
315 TODO: well, we might want to generate component URLs which work without
316 a session - at least in theory the ID tree should be stable even
317 without a session (and if proper uids are used for dynamic content).
318 eg this would be quite useful for SOPE.
324 - (WORequest *)request {
325 return self->request;
327 - (WOResponse *)response {
328 return self->response;
332 return (self->session != nil) ? YES : NO;
335 - (BOOL)savePageRequired {
336 return self->savePageRequired;
341 - (void)pushCursor:(id)_obj {
342 if (debugCursor == -1) {
343 debugCursor = [[NSUserDefaults standardUserDefaults]
344 boolForKey:@"WODebugCursor"]
348 if (debugCursor) [self logWithFormat:@"enter cursor: %@", _obj];
349 [[self component] pushCursor:_obj];
353 if (debugCursor) [self logWithFormat:@"leave cursor ..."];
354 return [[self component] popCursor];
358 return [(id <WOPageGenerationContext>)[self component] cursor];
363 - (WOComponent *)component {
364 return (self->componentStackCount > 0)
365 ? self->componentStack[self->componentStackCount - 1]
369 - (void)setPage:(WOComponent *)_page {
370 [_page ensureAwakeInContext:self];
371 ASSIGN(self->page, _page);
373 - (WOComponent *)page {
377 void WOContext_enterComponent
378 (WOContext *self, WOComponent *_component, WOElement *_content)
380 WOComponent *parent = nil;
382 NSCAssert(_component, @"missing component to enter ...");
386 [self->application logWithFormat:@"enter component %@ (content=%@) ..",
387 [_component name], _content];
390 parent = self->componentStackCount > 0
391 ? self->componentStack[self->componentStackCount - 1]
394 NSCAssert2(self->componentStackCount < NGObjWeb_MAX_COMPONENT_NESTING_DEPTH,
395 @"exceeded maximum component nesting depth (%i):\n%@",
396 NGObjWeb_MAX_COMPONENT_NESTING_DEPTH,
397 [self _componentStack]);
398 self->componentStack[(int)self->componentStackCount] = [_component retain];
399 self->contentStack[(int)self->componentStackCount] = [_content retain];
400 self->componentStackCount++;
402 [self _awakeComponent:_component];
405 if ([_component synchronizesVariablesWithBindings])
406 WOComponent_syncFromParent(_component, parent);
409 void WOContext_leaveComponent(WOContext *self, WOComponent *_component) {
410 WOComponent *parent = nil;
414 parent = (self->componentStackCount > 1)
415 ? self->componentStack[self->componentStackCount - 2]
419 if ([_component synchronizesVariablesWithBindings])
420 WOComponent_syncToParent(_component, parent);
423 PROFILE_CHECKPOINT("after sync");
425 /* remove last object */
426 self->componentStackCount--;
427 NSCAssert(self->componentStackCount >= 0,
428 @"tried to pop component from empty component stack !");
429 [self->componentStack[(int)self->componentStackCount] release];
430 self->componentStack[(int)self->componentStackCount] = nil;
431 [self->contentStack[(int)self->componentStackCount] release];
432 self->contentStack[(int)self->componentStackCount] = nil;
435 [self->application logWithFormat:@"left component %@.", [_component name]];
440 - (void)enterComponent:(WOComponent *)_comp content:(WOElement *)_content {
441 WOContext_enterComponent(self, _comp, _content);
443 - (void)leaveComponent:(WOComponent *)_component {
445 WOContext_leaveComponent(self, _component);
449 - (WOComponent *)parentComponent {
450 return (self->componentStackCount > 1)
451 ? self->componentStack[(int)self->componentStackCount - 2]
455 - (WODynamicElement *)componentContent {
456 return (self->componentStackCount > 0)
457 ? self->contentStack[(int)self->componentStackCount - 1]
461 - (unsigned)componentStackCount {
462 return self->componentStackCount;
464 - (NSArray *)_componentStack {
465 return [NSArray arrayWithObjects:self->componentStack
466 count:self->componentStackCount];
471 - (NSURL *)serverURL {
477 if ((rq = [self request]) == nil) {
478 [self logWithFormat:@"missing request in -baseURL call .."];
482 if ((serverURL = [rq headerForKey:@"x-webobjects-server-url"]) == nil) {
483 if ((host = [rq headerForKey:@"host"]))
484 serverURL = [@"http://" stringByAppendingString:host];
487 // TODO: fix that (host is also broken for example with SOUP)
488 /* sometimes the port is broken in the server URL ... */
489 if ([serverURL hasSuffix:@":0"]) { // bad bad bad
490 if ((host = [rq headerForKey:@"host"])) {
492 scheme = [serverURL hasPrefix:@"https://"] ? @"https://" : @"http://";
493 serverURL = [scheme stringByAppendingString:host];
498 if ([serverURL length] == 0) {
499 [self errorWithFormat:@"could not find x-webobjects-server-url header !"];
503 if ((url = [NSURL URLWithString:serverURL]) == nil) {
504 [self logWithFormat:@"could not construct NSURL from string '%@'",
516 return self->baseURL;
518 if ((rq = [self request]) == nil) {
519 [self logWithFormat:@"missing request in -baseURL call .."];
523 serverURL = [self serverURL];
525 [[NSURL URLWithString:[rq uri] relativeToURL:serverURL] retain];
527 if (self->baseURL == nil) {
529 @"could not construct NSURL for uri '%@' and base '%@' ...",
530 [rq uri], serverURL];
532 return self->baseURL;
535 - (NSURL *)applicationURL {
538 if (self->appURL != nil)
541 // TODO: we should ensure that the suffix (.woa) is in the URL
543 s = [self->request adaptorPrefix];
544 if ([s length] > 0) {
545 s = [[[s stringByAppendingString:@"/"]
546 stringByAppendingString:[self->request applicationName]]
547 stringByAppendingString:@"/"];
550 s = [[self->request applicationName] stringByAppendingString:@"/"];
553 [[NSURL URLWithString:s relativeToURL:[self serverURL]] retain];
556 - (NSURL *)urlForKey:(NSString *)_key {
557 _key = [_key stringByAppendingString:@"/"];
558 return [NSURL URLWithString:_key relativeToURL:[self applicationURL]];
563 - (void)setInForm:(BOOL)_form {
564 self->inForm = _form;
570 - (void)addActiveFormElement:(WOElement *)_formElement {
571 if (self->activeFormElement) {
572 [[self component] debugWithFormat:@"active form element already set !"];
576 ASSIGN(self->activeFormElement, _formElement);
577 [self setRequestSenderID:[self elementID]];
579 - (WOElement *)activeFormElement {
580 return self->activeFormElement;
583 /* context variables (transient) */
585 - (void)setObject:(id)_obj forKey:(NSString *)_key {
586 if (self->variables == nil) {
588 [[NSMutableDictionary allocWithZone:[self zone]]
589 initWithCapacity:16];
593 [self->variables setObject:_obj forKey:_key];
595 [self->variables removeObjectForKey:_key];
597 - (id)objectForKey:(NSString *)_key {
598 return [self->variables objectForKey:_key];
600 - (void)removeObjectForKey:(NSString *)_key {
601 [self->variables removeObjectForKey:_key];
604 - (NSDictionary *)variableDictionary {
605 return self->variables;
608 - (void)takeValue:(id)_value forKey:(NSString *)_key {
609 if (WOSetKVCValueUsingMethod(self, _key, _value))
612 else if (WOGetKVCGetMethod(self, _key) == NULL) {
614 _value = [EONull null];
616 if (self->variables == nil) {
618 [[NSMutableDictionary allocWithZone:[self zone]]
619 initWithCapacity:16];
621 [self->variables setObject:_value forKey:_key];
625 // only a 'get' method is defined for _key !
626 [self handleTakeValue:_value forUnboundKey:_key];
629 - (id)valueForKey:(NSString *)_key {
632 if ((value = WOGetKVCValueUsingMethod(self, _key)))
634 value = [self->variables objectForKey:_key];
640 - (id)copyWithZone:(NSZone *)_zone {
641 return [self retain];
646 - (NSString *)description {
648 WOApplication *app = [self application];
650 if ([self hasSession])
651 sid = [[self session] sessionID];
653 return [NSString stringWithFormat:
654 @"<0x%08X[%@]: %@ app=%@ sn=%@ eid=%@ rqeid=%@>",
655 (unsigned)self, NSStringFromClass([self class]),
665 - (NSString *)elementID {
666 return [self->elementID elementID];
668 - (void)appendElementIDComponent:(NSString *)_eid {
669 [self->elementID appendElementIDComponent:_eid];
671 - (void)appendZeroElementIDComponent {
672 [self->elementID appendZeroElementIDComponent];
674 - (void)deleteAllElementIDComponents {
675 [self->elementID deleteAllElementIDComponents];
677 - (void)deleteLastElementIDComponent {
678 [self->elementID deleteLastElementIDComponent];
680 - (void)incrementLastElementIDComponent {
681 [self->elementID incrementLastElementIDComponent];
683 - (void)appendIntElementIDComponent:(int)_eid {
684 [self->elementID appendIntElementIDComponent:_eid];
687 /* the following can be later moved to WOElementID */
689 - (id)currentElementID {
690 return [self->reqElementID currentElementID];
692 - (id)consumeElementID {
693 return [self->reqElementID consumeElementID];
698 - (void)_generateCompleteURLs {
699 /* described in Apple TIL article 70101 */
702 - (NSString *)queryStringFromDictionary:(NSDictionary *)_queryDict {
708 qs = [MutableStrClass stringWithCapacity:256];
709 keys = [_queryDict keyEnumerator];
710 for (isFirst = YES; (key = [keys nextObject]); ) {
716 [qs appendString:self->qpJoin];
718 value = [[_queryDict objectForKey:key] stringValue];
720 key = [key stringByEscapingURL];
721 value = [value stringByEscapingURL];
723 [qs appendString:key];
725 [qs appendString:@"="];
726 [qs appendString:value];
733 - (NSString *)directActionURLForActionNamed:(NSString *)_actionName
734 queryDictionary:(NSDictionary *)_queryDict
736 NSMutableString *url;
739 url = [MutableStrClass stringWithCapacity:256];
742 [url appendString:@"/"];
744 [url appendString:_actionName];
746 /* add query parameters */
748 qs = [self queryStringFromDictionary:_queryDict];
750 return [self urlWithRequestHandlerKey:
751 [WOAppClass directActionRequestHandlerKey]
752 path:url queryString:qs];
755 - (NSString *)componentActionURL {
756 // TODO: add a -cComponentActionURL
757 // (without NSString for use with appendContentCString:)
759 // 26% -urlWithRequestHandler...
760 // 21% -elementID (was 40% !! :-)
761 // ~20% mutable string ops
766 self->savePageRequired = YES;
767 qs = [MutableStrClass stringWithCapacity:64];
769 [qs appendString:WORequestValueSenderID];
770 [qs appendString:@"="];
771 [qs appendString:[self elementID]];
772 [qs appendString:self->qpJoin];
773 [qs appendString:WORequestValueSessionID];
774 [qs appendString:@"="];
775 [qs appendString:[[self session] sessionID]];
776 [qs appendString:self->qpJoin];
777 [qs appendString:WORequestValueContextID];
778 [qs appendString:@"="];
779 [qs appendString:[self contextID]];
781 p = [[self page] componentActionURLForContext:self];
784 if ([p hasPrefix:@"/"]) p = [p substringFromIndex:1];
787 return [self urlWithRequestHandlerKey:
788 [WOAppClass componentRequestHandlerKey]
793 /* old style URLs ... */
794 static NSMutableString *url = nil; // THREAD
795 static IMP addStr = NULL;
798 self->savePageRequired = YES;
800 url = [[MutableStrClass alloc] initWithCapacity:256];
801 addStr = [url methodForSelector:@selector(appendString:)];
802 addStr(url, @selector(appendString:), @"/");
805 [url setString:@"/"];
808 Note: component actions *always* require sessions to be able to locate
809 the request component !
811 addStr(url, @selector(appendString:), [[self session] sessionID]);
812 addStr(url, @selector(appendString:), @"/");
813 addStr(url, @selector(appendString:), [self->elementID elementID]);
815 s = [self urlWithRequestHandlerKey:
816 [WOAppClass componentRequestHandlerKey]
817 path:url queryString:nil];
822 - (NSString *)urlWithRequestHandlerKey:(NSString *)_key
823 path:(NSString *)_path
824 queryString:(NSString *)_query
826 if (testNSURLs) { /* use NSURLs for processing */
829 if ([_path hasPrefix:@"/"]) {
831 [self warnWithFormat:@"got absolute path '%@'", _path];
833 _path = [_path substringFromIndex:1];
836 if (_key == nil) _key = [WOAppClass componentRequestHandlerKey];
837 rqUrl = [self urlForKey:_key];
839 if ([_query length] > 0) {
842 s = [_path mutableCopy];
843 [s appendString:@"?"];
844 [s appendString:_query];
845 rqUrl = [NSURL URLWithString:s relativeToURL:rqUrl];
849 rqUrl = [NSURL URLWithString:_path relativeToURL:rqUrl];
851 //[self logWithFormat:@"constructed component URL: %@", rqUrl];
853 return [rqUrl stringValueRelativeToURL:[self baseURL]];
856 NSMutableString *url;
860 if (_key == nil) _key = [WOAppClass componentRequestHandlerKey];
862 url = [MutableStrClass stringWithCapacity:256];
863 addStr = [url methodForSelector:@selector(appendString:)];
866 if (self->urlPrefix == nil) {
868 if ((tmp = [self->request headerForKey:@"x-webobjects-server-url"])) {
869 if ([tmp hasSuffix:@":0"] && [tmp length] > 2) // TODO: BAD BAD BAD
870 tmp = [tmp substringToIndex:([tmp length] - 2)];
871 addStr(url, @selector(appendString:), tmp);
873 else if ((tmp = [self->request headerForKey:@"host"])) {
874 addStr(url, @selector(appendString:), @"http://");
875 addStr(url, @selector(appendString:), tmp);
879 addStr(url, @selector(appendString:), [self->request adaptorPrefix]);
880 addStr(url, @selector(appendString:), @"/");
881 tmp = [[self request] applicationName];
882 if ([tmp length] == 0)
883 tmp = [(WOApplication *)[self application] name];
884 if ([tmp length] > 0) {
885 addStr(url, @selector(appendString:), tmp);
886 if (WOApplicationSuffix)
887 addStr(url, @selector(appendString:), WOApplicationSuffix);
888 addStr(url, @selector(appendString:), @"/");
892 self->urlPrefix = [url copy];
893 if (debugOn) [self debugWithFormat:@"URL prefix: '%@'", self->urlPrefix];
896 /* prefix is cached :-) */
897 addStr(url, @selector(appendString:), self->urlPrefix);
901 addStr(url, @selector(appendString:), _key);
903 addStr(url, @selector(appendString:), _path);
904 if ([_query length] > 0) {
905 addStr(url, @selector(appendString:), @"?");
906 addStr(url, @selector(appendString:), _query);
911 - (NSString *)completeURLWithRequestHandlerKey:(NSString *)_key
912 path:(NSString *)_path queryString:(NSString *)_query
913 isSecure:(BOOL)_isSecure port:(int)_port
915 NSMutableString *url = [MutableStrClass stringWithCapacity:256];
916 [url appendString:_isSecure ? @"https://" : @"http://"];
917 [url appendString:[[self request] headerForKey:@"host"]];
919 if (!(_isSecure && _port == 443) && !(!_isSecure && _port == 80))
920 [url appendFormat:@":%i", _port];
922 [url appendString:[self urlWithRequestHandlerKey:_key
924 queryString:_query]];
928 - (void)setRequestSenderID:(NSString *)_rid {
931 eid = [[WOElementID alloc] initWithString:_rid];
932 [self->reqElementID release];
933 self->reqElementID = eid;
935 - (NSString *)senderID {
937 return [self->reqElementID elementID];
939 NSMutableString *eid;
943 eid = [MutableStrClass stringWithCapacity:(self->reqElementIdCount * 4) + 1];
944 addStr = [eid methodForSelector:@selector(appendString:)];
945 for (i = 0; i < self->reqElementIdCount; i++) {
946 if (i != 0) addStr(eid, @selector(appendString:), @".");
947 addStr(eid, @selector(appendString:), [self->reqElementId[i] stringValue]);
953 /* languages for resource lookup (non-WO) */
955 - (NSArray *)resourceLookupLanguages {
956 return [self hasSession] ? [[self session] languages]
957 : [[self request] browserLanguages];
961 /* DeprecatedMethodsInWO4 */
963 - (WOApplication *)application {
964 if (self->application == nil)
965 self->application = [WOAppClass application];
967 if (self->application == nil)
968 NSLog(@"%s: missing application for context %@", __PRETTY_FUNCTION__, self);
970 return self->application;
973 - (void)setDistributionEnabled:(BOOL)_flag {
975 [[self session] setDistributionEnabled:_flag];
977 - (BOOL)isDistributionEnabled {
979 return [[self session] isDistributionEnabled];
983 return [self componentActionURL];
986 - (NSString *)urlSessionPrefix {
987 NSMutableString *url;
990 url = [MutableStrClass stringWithCapacity:128];
992 [url appendString:[[self request] adaptorPrefix]];
993 [url appendString:@"/"];
994 tmp = [[self request] applicationName];
996 tmp ? tmp : [(WOApplication *)[self application] name]];
999 if ([url length] == 0) {
1000 [self warnWithFormat:@"(%s): could not determine session URL prefix !",
1001 __PRETTY_FUNCTION__];
1008 @end /* WOContext */
1011 @implementation WOComponent(Cursors)
1013 - (void)pushCursor:(id)_obj {
1015 [self logWithFormat:@"enter cursor: %@", _obj];
1017 if (!self->cycleContext)
1018 self->cycleContext = [[NSMutableArray alloc] initWithCapacity:8];
1020 /* add to cursor stack */
1021 [self->cycleContext addObject:(_obj ? _obj : [NSNull null])];
1023 /* set active cursor */
1024 [self setObject:_obj forKey:@"_"];
1028 NSMutableArray *ctxStack;
1031 /* retrieve last context */
1032 old = [[self objectForKey:@"_"] retain];
1033 [self setObject:nil forKey:@"_"];
1035 /* restore old ctx */
1036 if ((ctxStack = self->cycleContext) != nil) {
1039 if ((count = [ctxStack count]) > 0) {
1040 [ctxStack removeObjectAtIndex:(count - 1)];
1046 obj = [ctxStack objectAtIndex:(count - 1)];
1048 if (![obj isNotNull]) obj = nil;
1049 [self setObject:obj forKey:@"_"];
1055 [self warnWithFormat:@"-popCursor called without cycle ctx !"];
1060 [self logWithFormat:@"leave cursor: %@ (restored=%@)",
1061 old, [self cursor]];
1064 return [old autorelease];
1068 NSMutableArray *ctxStack;
1070 // TODO: why do we check for _ODCycleCtx, if we query '_' ?
1072 if ((ctxStack = self->cycleContext) == nil)
1073 /* no cycle context setup for component ... */
1075 if ([ctxStack count] == 0)
1076 /* nothing contained in cycle context ... */
1079 return [self objectForKey:@"_"];
1082 @end /* WOComponent(Cursors) */