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 /* per default close tags in XML style */
108 self->wcFlags.xmlStyleEmptyElements = 1;
110 self->elementID = [[WOElementID alloc] init];
111 self->awakeComponents = [[NSMutableSet alloc] initWithCapacity:64];
113 self->request = [_request retain];
114 self->response = [[WOResponse responseWithRequest:_request] retain];
120 return [[[self alloc] init] autorelease];
123 return [self initWithRequest:nil];
128 - (void)_addAwakeComponent:(WOComponent *)_component {
129 if (_component == nil)
132 if ([self->awakeComponents containsObject:_component])
135 /* wake up component */
136 if (debugComponentAwake)
137 [self logWithFormat:@"mark component awake: %@", _component];
139 [self->awakeComponents addObject:_component];
142 - (void)_awakeComponent:(WOComponent *)_component {
143 if (_component == nil)
146 if ([self->awakeComponents containsObject:_component])
149 /* wake up component */
150 if (debugComponentAwake)
151 [self logWithFormat:@"awake component: %@", _component];
153 [_component _awakeWithContext:self];
155 [self _addAwakeComponent:_component];
157 if (debugComponentAwake)
158 [self logWithFormat:@"woke up component: %@", _component];
161 - (void)sleepComponents {
163 WOComponent *component;
164 BOOL sendSleepToPage;
166 if (debugComponentAwake) {
167 [self logWithFormat:@"sleep %d components ...",
168 [self->awakeComponents count]];
171 sendSleepToPage = YES;
172 e = [self->awakeComponents objectEnumerator];
173 while ((component = [e nextObject])) {
174 if (debugComponentAwake)
175 [self logWithFormat:@" sleep component: %@", component];
176 [component _sleepWithContext:self];
177 if (component == self->page) sendSleepToPage = NO;
179 if (sendSleepToPage && (self->page != nil)) {
180 if (debugComponentAwake)
181 [self logWithFormat:@" sleep page: %@", self->page];
182 [self->page _sleepWithContext:self];
185 if (debugComponentAwake) {
186 [self logWithFormat:@"done sleep %d components.",
187 [self->awakeComponents count]];
189 [self->awakeComponents removeAllObjects];
192 #if WITH_DEALLOC_OBSERVERS
193 - (void)addDeallocObserver:(id)_observer {
194 if (_observer == NULL) return;
197 if (self->deallocObservers == NULL) {
198 self->deallocObservers = calloc(8, sizeof(id));
199 self->deallocObserverCount = 0;
200 self->deallocObserverCapacity = 8;
204 if (self->deallocObserverCapacity == self->deallocObserverCount) {
205 /* need to increase array */
208 newa = calloc(self->deallocObserverCapacity * 2, sizeof(id));
209 memcpy(newa, self->deallocObservers,
210 sizeof(id) * self->deallocObserverCount);
211 free(self->deallocObservers);
212 self->deallocObservers = newa;
213 self->deallocObserverCapacity *= 2;
217 self->deallocObservers[self->deallocObserverCount] = _observer;
218 self->deallocObserverCount++;
220 - (void)removeDeallocObserver:(id)_observer {
221 /* the observer currently will only grow (this should be OK for WOContext) */
223 if (_observer == NULL) return;
225 for (i = self->deallocObserverCount - 1; i >= 0; i++) {
226 if ((self->deallocObservers[i]) == _observer)
227 self->deallocObservers[i] = NULL;
233 [self sleepComponents];
235 #if WITH_DEALLOC_OBSERVERS
236 if (self->deallocObservers) {
240 printf("%s: dealloc observer capacity: %i\n",
241 __PRETTY_FUNCTION__, self->deallocObserverCapacity);
244 /* GC!! process in reverse order ... */
245 for (i = self->deallocObserverCount - 1; i >= 0; i++)
246 [self->deallocObservers[i] _objectWillDealloc:self];
248 free(self->deallocObservers);
249 self->deallocObservers = NULL;
253 [self->activeUser release];
254 [self->rootURL release];
255 [self->objectPermissionCache release];
256 [self->traversalStack release];
257 [self->clientObject release];
258 [self->objectDispatcher release];
259 [self->soRequestType release];
260 [self->pathInfo release];
262 [[NSNotificationCenter defaultCenter]
263 postNotificationName:@"WOContextWillDeallocate"
266 { /* release component stack */
268 for (i = (self->componentStackCount - 1); i >= 0; i--) {
269 [self->componentStack[i] release]; self->componentStack[i] = nil;
270 [self->contentStack[i] release]; self->contentStack[i] = nil;
274 [self->urlPrefix release];
275 [self->elementID release];
276 [self->reqElementID release];
277 [self->activeFormElement release];
278 [self->page release];
279 [self->awakeComponents release];
280 [self->appURL release];
281 [self->baseURL release];
282 [self->session release];
283 [self->variables release];
284 [self->request release];
285 [self->response release];
286 [self->ctxId release];
290 - (void)setSession:(WOSession *)_session {
291 ASSIGN(self->session, _session);
294 - (WOSession *)session {
295 // in WO4 -session creates a new session if none is associated
297 if (self->session == nil) {
298 [[self application] _initializeSessionInContext:self];
300 if (self->session == nil)
301 [self logWithFormat:@"%s: missing session for context ..",
302 __PRETTY_FUNCTION__];
305 return self->session;
308 - (NSString *)contextID {
309 NSAssert(self->ctxId, @"context without id !");
311 // in WO4 -contextID returns nil if there is no associated session
312 return self->session ? self->ctxId : nil;
315 IMHO the above isn't true, otherwise session cannot be automagically
318 TODO: well, we might want to generate component URLs which work without
319 a session - at least in theory the ID tree should be stable even
320 without a session (and if proper uids are used for dynamic content).
321 eg this would be quite useful for SOPE.
327 - (WORequest *)request {
328 return self->request;
330 - (WOResponse *)response {
331 return self->response;
335 return (self->session != nil) ? YES : NO;
338 - (BOOL)savePageRequired {
339 return self->wcFlags.savePageRequired ? YES : NO;
344 - (void)pushCursor:(id)_obj {
345 if (debugCursor == -1) {
346 debugCursor = [[NSUserDefaults standardUserDefaults]
347 boolForKey:@"WODebugCursor"]
351 if (debugCursor) [self logWithFormat:@"enter cursor: %@", _obj];
352 [[self component] pushCursor:_obj];
356 if (debugCursor) [self logWithFormat:@"leave cursor ..."];
357 return [[self component] popCursor];
361 return [(id <WOPageGenerationContext>)[self component] cursor];
366 - (WOComponent *)component {
367 return (self->componentStackCount > 0)
368 ? self->componentStack[self->componentStackCount - 1]
372 - (void)setPage:(WOComponent *)_page {
373 [_page ensureAwakeInContext:self];
374 ASSIGN(self->page, _page);
376 - (WOComponent *)page {
380 void WOContext_enterComponent
381 (WOContext *self, WOComponent *_component, WOElement *_content)
383 WOComponent *parent = nil;
385 NSCAssert(_component, @"missing component to enter ...");
389 [self->application logWithFormat:@"enter component %@ (content=%@) ..",
390 [_component name], _content];
393 parent = self->componentStackCount > 0
394 ? self->componentStack[self->componentStackCount - 1]
397 NSCAssert2(self->componentStackCount < NGObjWeb_MAX_COMPONENT_NESTING_DEPTH,
398 @"exceeded maximum component nesting depth (%i):\n%@",
399 NGObjWeb_MAX_COMPONENT_NESTING_DEPTH,
400 [self _componentStack]);
401 self->componentStack[(int)self->componentStackCount] = [_component retain];
402 self->contentStack[(int)self->componentStackCount] = [_content retain];
403 self->componentStackCount++;
405 [self _awakeComponent:_component];
408 if ([_component synchronizesVariablesWithBindings])
409 WOComponent_syncFromParent(_component, parent);
412 void WOContext_leaveComponent(WOContext *self, WOComponent *_component) {
413 WOComponent *parent = nil;
417 parent = (self->componentStackCount > 1)
418 ? self->componentStack[self->componentStackCount - 2]
422 if ([_component synchronizesVariablesWithBindings])
423 WOComponent_syncToParent(_component, parent);
426 PROFILE_CHECKPOINT("after sync");
428 /* remove last object */
429 self->componentStackCount--;
430 NSCAssert(self->componentStackCount >= 0,
431 @"tried to pop component from empty component stack !");
432 [self->componentStack[(int)self->componentStackCount] release];
433 self->componentStack[(int)self->componentStackCount] = nil;
434 [self->contentStack[(int)self->componentStackCount] release];
435 self->contentStack[(int)self->componentStackCount] = nil;
438 [self->application logWithFormat:@"left component %@.", [_component name]];
443 - (void)enterComponent:(WOComponent *)_comp content:(WOElement *)_content {
444 WOContext_enterComponent(self, _comp, _content);
446 - (void)leaveComponent:(WOComponent *)_component {
448 WOContext_leaveComponent(self, _component);
452 - (WOComponent *)parentComponent {
453 return (self->componentStackCount > 1)
454 ? self->componentStack[(int)self->componentStackCount - 2]
458 - (WODynamicElement *)componentContent {
459 return (self->componentStackCount > 0)
460 ? self->contentStack[(int)self->componentStackCount - 1]
464 - (unsigned)componentStackCount {
465 return self->componentStackCount;
467 - (NSArray *)_componentStack {
468 return [NSArray arrayWithObjects:self->componentStack
469 count:self->componentStackCount];
474 - (NSURL *)serverURL {
480 if ((rq = [self request]) == nil) {
481 [self logWithFormat:@"missing request in -baseURL call .."];
485 if ((serverURL = [rq headerForKey:@"x-webobjects-server-url"]) == nil) {
486 if ((host = [rq headerForKey:@"host"]))
487 serverURL = [@"http://" stringByAppendingString:host];
490 // TODO: fix that (host is also broken for example with SOUP)
491 /* sometimes the port is broken in the server URL ... */
492 if ([serverURL hasSuffix:@":0"]) { // bad bad bad
493 if ((host = [rq headerForKey:@"host"])) {
495 scheme = [serverURL hasPrefix:@"https://"] ? @"https://" : @"http://";
496 serverURL = [scheme stringByAppendingString:host];
501 if ([serverURL length] == 0) {
502 [self errorWithFormat:@"could not find x-webobjects-server-url header !"];
506 if ((url = [NSURL URLWithString:serverURL]) == nil) {
507 [self logWithFormat:@"could not construct NSURL from string '%@'",
519 return self->baseURL;
521 if ((rq = [self request]) == nil) {
522 [self logWithFormat:@"missing request in -baseURL call .."];
526 serverURL = [self serverURL];
528 [[NSURL URLWithString:[rq uri] relativeToURL:serverURL] retain];
530 if (self->baseURL == nil) {
532 @"could not construct NSURL for uri '%@' and base '%@' ...",
533 [rq uri], serverURL];
535 return self->baseURL;
538 - (NSURL *)applicationURL {
541 if (self->appURL != nil)
544 // TODO: we should ensure that the suffix (.woa) is in the URL
546 s = [self->request adaptorPrefix];
547 if ([s length] > 0) {
548 s = [[[s stringByAppendingString:@"/"]
549 stringByAppendingString:[self->request applicationName]]
550 stringByAppendingString:@"/"];
553 s = [[self->request applicationName] stringByAppendingString:@"/"];
556 [[NSURL URLWithString:s relativeToURL:[self serverURL]] retain];
559 - (NSURL *)urlForKey:(NSString *)_key {
560 _key = [_key stringByAppendingString:@"/"];
561 return [NSURL URLWithString:_key relativeToURL:[self applicationURL]];
566 - (void)setInForm:(BOOL)_form {
567 self->wcFlags.inForm = _form ? 1 : 0;
570 return self->wcFlags.inForm ? YES : NO;
573 - (void)addActiveFormElement:(WOElement *)_formElement {
574 if (self->activeFormElement) {
575 [[self component] debugWithFormat:@"active form element already set !"];
579 ASSIGN(self->activeFormElement, _formElement);
580 [self setRequestSenderID:[self elementID]];
582 - (WOElement *)activeFormElement {
583 return self->activeFormElement;
586 /* context variables (transient) */
588 - (void)setObject:(id)_obj forKey:(NSString *)_key {
589 if (self->variables == nil) {
591 [[NSMutableDictionary allocWithZone:[self zone]]
592 initWithCapacity:16];
596 [self->variables setObject:_obj forKey:_key];
598 [self->variables removeObjectForKey:_key];
600 - (id)objectForKey:(NSString *)_key {
601 return [self->variables objectForKey:_key];
603 - (void)removeObjectForKey:(NSString *)_key {
604 [self->variables removeObjectForKey:_key];
607 - (NSDictionary *)variableDictionary {
608 return self->variables;
611 - (void)takeValue:(id)_value forKey:(NSString *)_key {
612 if (WOSetKVCValueUsingMethod(self, _key, _value))
615 else if (WOGetKVCGetMethod(self, _key) == NULL) {
617 _value = [EONull null];
619 if (self->variables == nil) {
621 [[NSMutableDictionary allocWithZone:[self zone]]
622 initWithCapacity:16];
624 [self->variables setObject:_value forKey:_key];
628 // only a 'get' method is defined for _key !
629 [self handleTakeValue:_value forUnboundKey:_key];
632 - (id)valueForKey:(NSString *)_key {
635 if ((value = WOGetKVCValueUsingMethod(self, _key)))
637 value = [self->variables objectForKey:_key];
643 - (id)copyWithZone:(NSZone *)_zone {
644 return [self retain];
649 - (NSString *)description {
651 WOApplication *app = [self application];
653 if ([self hasSession])
654 sid = [[self session] sessionID];
656 return [NSString stringWithFormat:
657 @"<0x%08X[%@]: %@ app=%@ sn=%@ eid=%@ rqeid=%@>",
658 (unsigned)self, NSStringFromClass([self class]),
668 - (NSString *)elementID {
669 return [self->elementID elementID];
671 - (void)appendElementIDComponent:(NSString *)_eid {
672 [self->elementID appendElementIDComponent:_eid];
674 - (void)appendZeroElementIDComponent {
675 [self->elementID appendZeroElementIDComponent];
677 - (void)deleteAllElementIDComponents {
678 [self->elementID deleteAllElementIDComponents];
680 - (void)deleteLastElementIDComponent {
681 [self->elementID deleteLastElementIDComponent];
683 - (void)incrementLastElementIDComponent {
684 [self->elementID incrementLastElementIDComponent];
686 - (void)appendIntElementIDComponent:(int)_eid {
687 [self->elementID appendIntElementIDComponent:_eid];
690 /* the following can be later moved to WOElementID */
692 - (id)currentElementID {
693 return [self->reqElementID currentElementID];
695 - (id)consumeElementID {
696 return [self->reqElementID consumeElementID];
701 - (void)_generateCompleteURLs {
702 /* described in Apple TIL article 70101 */
705 - (void)setQueryPathSeparator:(NSString *)_sp {
706 ASSIGNCOPY(self->qpJoin, _sp);
708 - (NSString *)queryPathSeparator {
712 - (void)setGenerateXMLStyleEmptyElements:(BOOL)_flag {
713 self->wcFlags.xmlStyleEmptyElements = _flag ? 1 : 0;
715 - (BOOL)generateXMLStyleEmptyElements {
716 return self->wcFlags.xmlStyleEmptyElements ? YES : NO;
719 - (NSString *)queryStringFromDictionary:(NSDictionary *)_queryDict {
725 qs = [MutableStrClass stringWithCapacity:256];
726 keys = [_queryDict keyEnumerator];
727 for (isFirst = YES; (key = [keys nextObject]); ) {
733 [qs appendString:self->qpJoin];
735 value = [[_queryDict objectForKey:key] stringValue];
737 key = [key stringByEscapingURL];
738 value = [value stringByEscapingURL];
740 [qs appendString:key];
742 [qs appendString:@"="];
743 [qs appendString:value];
750 - (NSString *)directActionURLForActionNamed:(NSString *)_actionName
751 queryDictionary:(NSDictionary *)_queryDict
753 NSMutableString *url;
756 url = [MutableStrClass stringWithCapacity:256];
759 [url appendString:@"/"];
761 [url appendString:_actionName];
763 /* add query parameters */
765 qs = [self queryStringFromDictionary:_queryDict];
767 return [self urlWithRequestHandlerKey:
768 [WOAppClass directActionRequestHandlerKey]
769 path:url queryString:qs];
772 - (NSString *)componentActionURL {
773 // TODO: add a -cComponentActionURL
774 // (without NSString for use with appendContentCString:)
776 // 26% -urlWithRequestHandler...
777 // 21% -elementID (was 40% !! :-)
778 // ~20% mutable string ops
783 self->wcFlags.savePageRequired = 1;
784 qs = [MutableStrClass stringWithCapacity:64];
786 [qs appendString:WORequestValueSenderID];
787 [qs appendString:@"="];
788 [qs appendString:[self elementID]];
789 [qs appendString:self->qpJoin];
790 [qs appendString:WORequestValueSessionID];
791 [qs appendString:@"="];
792 [qs appendString:[[self session] sessionID]];
793 [qs appendString:self->qpJoin];
794 [qs appendString:WORequestValueContextID];
795 [qs appendString:@"="];
796 [qs appendString:[self contextID]];
798 p = [[self page] componentActionURLForContext:self];
801 if ([p hasPrefix:@"/"]) p = [p substringFromIndex:1];
804 return [self urlWithRequestHandlerKey:
805 [WOAppClass componentRequestHandlerKey]
810 /* old style URLs ... */
811 static NSMutableString *url = nil; // THREAD
812 static IMP addStr = NULL;
815 self->wcFlags.savePageRequired = 1;
817 url = [[MutableStrClass alloc] initWithCapacity:256];
818 addStr = [url methodForSelector:@selector(appendString:)];
819 addStr(url, @selector(appendString:), @"/");
822 [url setString:@"/"];
825 Note: component actions *always* require sessions to be able to locate
826 the request component !
828 addStr(url, @selector(appendString:), [[self session] sessionID]);
829 addStr(url, @selector(appendString:), @"/");
830 addStr(url, @selector(appendString:), [self->elementID elementID]);
832 s = [self urlWithRequestHandlerKey:
833 [WOAppClass componentRequestHandlerKey]
834 path:url queryString:nil];
839 - (NSString *)urlWithRequestHandlerKey:(NSString *)_key
840 path:(NSString *)_path
841 queryString:(NSString *)_query
843 if (testNSURLs) { /* use NSURLs for processing */
846 if ([_path hasPrefix:@"/"]) {
848 [self warnWithFormat:@"got absolute path '%@'", _path];
850 _path = [_path substringFromIndex:1];
853 if (_key == nil) _key = [WOAppClass componentRequestHandlerKey];
854 rqUrl = [self urlForKey:_key];
856 if ([_query length] > 0) {
859 s = [_path mutableCopy];
860 [s appendString:@"?"];
861 [s appendString:_query];
862 rqUrl = [NSURL URLWithString:s relativeToURL:rqUrl];
866 rqUrl = [NSURL URLWithString:_path relativeToURL:rqUrl];
868 //[self logWithFormat:@"constructed component URL: %@", rqUrl];
870 return [rqUrl stringValueRelativeToURL:[self baseURL]];
873 NSMutableString *url;
877 if (_key == nil) _key = [WOAppClass componentRequestHandlerKey];
879 url = [MutableStrClass stringWithCapacity:256];
880 addStr = [url methodForSelector:@selector(appendString:)];
883 if (self->urlPrefix == nil) {
885 if ((tmp = [self->request headerForKey:@"x-webobjects-server-url"])) {
886 if ([tmp hasSuffix:@":0"] && [tmp length] > 2) // TODO: BAD BAD BAD
887 tmp = [tmp substringToIndex:([tmp length] - 2)];
888 addStr(url, @selector(appendString:), tmp);
890 else if ((tmp = [self->request headerForKey:@"host"])) {
891 addStr(url, @selector(appendString:), @"http://");
892 addStr(url, @selector(appendString:), tmp);
896 addStr(url, @selector(appendString:), [self->request adaptorPrefix]);
897 addStr(url, @selector(appendString:), @"/");
898 tmp = [[self request] applicationName];
899 if ([tmp length] == 0)
900 tmp = [(WOApplication *)[self application] name];
901 if ([tmp length] > 0) {
902 addStr(url, @selector(appendString:), tmp);
903 if (WOApplicationSuffix)
904 addStr(url, @selector(appendString:), WOApplicationSuffix);
905 addStr(url, @selector(appendString:), @"/");
909 self->urlPrefix = [url copy];
910 if (debugOn) [self debugWithFormat:@"URL prefix: '%@'", self->urlPrefix];
913 /* prefix is cached :-) */
914 addStr(url, @selector(appendString:), self->urlPrefix);
918 addStr(url, @selector(appendString:), _key);
920 addStr(url, @selector(appendString:), _path);
921 if ([_query length] > 0) {
922 addStr(url, @selector(appendString:), @"?");
923 addStr(url, @selector(appendString:), _query);
928 - (NSString *)completeURLWithRequestHandlerKey:(NSString *)_key
929 path:(NSString *)_path queryString:(NSString *)_query
930 isSecure:(BOOL)_isSecure port:(int)_port
932 NSMutableString *url = [MutableStrClass stringWithCapacity:256];
933 [url appendString:_isSecure ? @"https://" : @"http://"];
934 [url appendString:[[self request] headerForKey:@"host"]];
936 if (!(_isSecure && _port == 443) && !(!_isSecure && _port == 80))
937 [url appendFormat:@":%i", _port];
939 [url appendString:[self urlWithRequestHandlerKey:_key
941 queryString:_query]];
945 - (void)setRequestSenderID:(NSString *)_rid {
948 eid = [[WOElementID alloc] initWithString:_rid];
949 [self->reqElementID release];
950 self->reqElementID = eid;
952 - (NSString *)senderID {
954 return [self->reqElementID elementID];
956 NSMutableString *eid;
960 eid = [MutableStrClass stringWithCapacity:(self->reqElementIdCount * 4) + 1];
961 addStr = [eid methodForSelector:@selector(appendString:)];
962 for (i = 0; i < self->reqElementIdCount; i++) {
963 if (i != 0) addStr(eid, @selector(appendString:), @".");
964 addStr(eid, @selector(appendString:), [self->reqElementId[i] stringValue]);
970 /* languages for resource lookup (non-WO) */
972 - (NSArray *)resourceLookupLanguages {
973 return [self hasSession] ? [[self session] languages]
974 : [[self request] browserLanguages];
978 /* DeprecatedMethodsInWO4 */
980 - (WOApplication *)application {
981 if (self->application == nil)
982 self->application = [WOAppClass application];
984 if (self->application == nil)
985 NSLog(@"%s: missing application for context %@", __PRETTY_FUNCTION__, self);
987 return self->application;
990 - (void)setDistributionEnabled:(BOOL)_flag {
992 [[self session] setDistributionEnabled:_flag];
994 - (BOOL)isDistributionEnabled {
996 return [[self session] isDistributionEnabled];
1000 return [self componentActionURL];
1003 - (NSString *)urlSessionPrefix {
1004 NSMutableString *url;
1007 url = [MutableStrClass stringWithCapacity:128];
1009 [url appendString:[[self request] adaptorPrefix]];
1010 [url appendString:@"/"];
1011 tmp = [[self request] applicationName];
1013 tmp ? tmp : [(WOApplication *)[self application] name]];
1016 if ([url length] == 0) {
1017 [self warnWithFormat:@"(%s): could not determine session URL prefix !",
1018 __PRETTY_FUNCTION__];
1025 @end /* WOContext */
1028 @implementation WOComponent(Cursors)
1030 - (void)pushCursor:(id)_obj {
1032 [self logWithFormat:@"enter cursor: %@", _obj];
1034 if (!self->cycleContext)
1035 self->cycleContext = [[NSMutableArray alloc] initWithCapacity:8];
1037 /* add to cursor stack */
1038 [self->cycleContext addObject:(_obj ? _obj : [NSNull null])];
1040 /* set active cursor */
1041 [self setObject:_obj forKey:@"_"];
1045 NSMutableArray *ctxStack;
1048 /* retrieve last context */
1049 old = [[self objectForKey:@"_"] retain];
1050 [self setObject:nil forKey:@"_"];
1052 /* restore old ctx */
1053 if ((ctxStack = self->cycleContext) != nil) {
1056 if ((count = [ctxStack count]) > 0) {
1057 [ctxStack removeObjectAtIndex:(count - 1)];
1063 obj = [ctxStack objectAtIndex:(count - 1)];
1065 if (![obj isNotNull]) obj = nil;
1066 [self setObject:obj forKey:@"_"];
1072 [self warnWithFormat:@"-popCursor called without cycle ctx !"];
1077 [self logWithFormat:@"leave cursor: %@ (restored=%@)",
1078 old, [self cursor]];
1081 return [old autorelease];
1085 NSMutableArray *ctxStack;
1087 // TODO: why do we check for _ODCycleCtx, if we query '_' ?
1089 if ((ctxStack = self->cycleContext) == nil)
1090 /* no cycle context setup for component ... */
1092 if ([ctxStack count] == 0)
1093 /* nothing contained in cycle context ... */
1096 return [self objectForKey:@"_"];
1099 @end /* WOComponent(Cursors) */