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])) {
102 self->qpJoin = @"&";
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;
109 self->wcFlags.allowEmptyAttributes = 0;
111 self->elementID = [[WOElementID alloc] init];
112 self->awakeComponents = [[NSMutableSet alloc] initWithCapacity:64];
114 self->request = [_request retain];
115 self->response = [[WOResponse responseWithRequest:_request] retain];
121 return [[[self alloc] init] autorelease];
124 return [self initWithRequest:nil];
129 - (void)_addAwakeComponent:(WOComponent *)_component {
130 if (_component == nil)
133 if ([self->awakeComponents containsObject:_component])
136 /* wake up component */
137 if (debugComponentAwake)
138 [self logWithFormat:@"mark component awake: %@", _component];
140 [self->awakeComponents addObject:_component];
143 - (void)_awakeComponent:(WOComponent *)_component {
144 if (_component == nil)
147 if ([self->awakeComponents containsObject:_component])
150 /* wake up component */
151 if (debugComponentAwake)
152 [self logWithFormat:@"awake component: %@", _component];
154 [_component _awakeWithContext:self];
156 [self _addAwakeComponent:_component];
158 if (debugComponentAwake)
159 [self logWithFormat:@"woke up component: %@", _component];
162 - (void)sleepComponents {
164 WOComponent *component;
165 BOOL sendSleepToPage;
167 if (debugComponentAwake) {
168 [self logWithFormat:@"sleep %d components ...",
169 [self->awakeComponents count]];
172 sendSleepToPage = YES;
173 e = [self->awakeComponents objectEnumerator];
174 while ((component = [e nextObject])) {
175 if (debugComponentAwake)
176 [self logWithFormat:@" sleep component: %@", component];
177 [component _sleepWithContext:self];
178 if (component == self->page) sendSleepToPage = NO;
180 if (sendSleepToPage && (self->page != nil)) {
181 if (debugComponentAwake)
182 [self logWithFormat:@" sleep page: %@", self->page];
183 [self->page _sleepWithContext:self];
186 if (debugComponentAwake) {
187 [self logWithFormat:@"done sleep %d components.",
188 [self->awakeComponents count]];
190 [self->awakeComponents removeAllObjects];
193 #if WITH_DEALLOC_OBSERVERS
194 - (void)addDeallocObserver:(id)_observer {
195 if (_observer == NULL) return;
198 if (self->deallocObservers == NULL) {
199 self->deallocObservers = calloc(8, sizeof(id));
200 self->deallocObserverCount = 0;
201 self->deallocObserverCapacity = 8;
205 if (self->deallocObserverCapacity == self->deallocObserverCount) {
206 /* need to increase array */
209 newa = calloc(self->deallocObserverCapacity * 2, sizeof(id));
210 memcpy(newa, self->deallocObservers,
211 sizeof(id) * self->deallocObserverCount);
212 free(self->deallocObservers);
213 self->deallocObservers = newa;
214 self->deallocObserverCapacity *= 2;
218 self->deallocObservers[self->deallocObserverCount] = _observer;
219 self->deallocObserverCount++;
221 - (void)removeDeallocObserver:(id)_observer {
222 /* the observer currently will only grow (this should be OK for WOContext) */
224 if (_observer == NULL) return;
226 for (i = self->deallocObserverCount - 1; i >= 0; i++) {
227 if ((self->deallocObservers[i]) == _observer)
228 self->deallocObservers[i] = NULL;
234 [self sleepComponents];
236 #if WITH_DEALLOC_OBSERVERS
237 if (self->deallocObservers) {
241 printf("%s: dealloc observer capacity: %i\n",
242 __PRETTY_FUNCTION__, self->deallocObserverCapacity);
245 /* GC!! process in reverse order ... */
246 for (i = self->deallocObserverCount - 1; i >= 0; i++)
247 [self->deallocObservers[i] _objectWillDealloc:self];
249 free(self->deallocObservers);
250 self->deallocObservers = NULL;
254 [self->activeUser release];
255 [self->rootURL release];
256 [self->objectPermissionCache release];
257 [self->traversalStack release];
258 [self->clientObject release];
259 [self->objectDispatcher release];
260 [self->soRequestType release];
261 [self->pathInfo release];
263 [[NSNotificationCenter defaultCenter]
264 postNotificationName:@"WOContextWillDeallocate"
267 { /* release component stack */
269 for (i = (self->componentStackCount - 1); i >= 0; i--) {
270 [self->componentStack[i] release]; self->componentStack[i] = nil;
271 [self->contentStack[i] release]; self->contentStack[i] = nil;
275 [self->urlPrefix release];
276 [self->elementID release];
277 [self->reqElementID release];
278 [self->activeFormElement release];
279 [self->page release];
280 [self->awakeComponents release];
281 [self->appURL release];
282 [self->baseURL release];
283 [self->session release];
284 [self->variables release];
285 [self->request release];
286 [self->response release];
287 [self->ctxId release];
291 - (void)setSession:(WOSession *)_session {
292 ASSIGN(self->session, _session);
296 // in WO4 -session creates a new session if none is associated
298 if (self->session == nil) {
299 [[self application] _initializeSessionInContext:self];
301 if (self->session == nil)
302 [self logWithFormat:@"%s: missing session for context ..",
303 __PRETTY_FUNCTION__];
306 return self->session;
309 - (NSString *)contextID {
310 NSAssert(self->ctxId, @"context without id !");
312 // in WO4 -contextID returns nil if there is no associated session
313 return self->session ? self->ctxId : nil;
316 IMHO the above isn't true, otherwise session cannot be automagically
319 TODO: well, we might want to generate component URLs which work without
320 a session - at least in theory the ID tree should be stable even
321 without a session (and if proper uids are used for dynamic content).
322 eg this would be quite useful for SOPE.
328 - (WORequest *)request {
329 return self->request;
331 - (WOResponse *)response {
332 return self->response;
336 return (self->session != nil) ? YES : NO;
339 - (BOOL)savePageRequired {
340 return self->wcFlags.savePageRequired ? YES : NO;
345 - (void)pushCursor:(id)_obj {
346 if (debugCursor == -1) {
347 debugCursor = [[NSUserDefaults standardUserDefaults]
348 boolForKey:@"WODebugCursor"]
352 if (debugCursor) [self logWithFormat:@"enter cursor: %@", _obj];
353 [[self component] pushCursor:_obj];
357 if (debugCursor) [self logWithFormat:@"leave cursor ..."];
358 return [[self component] popCursor];
362 return [(id <WOPageGenerationContext>)[self component] cursor];
368 return (self->componentStackCount > 0)
369 ? self->componentStack[self->componentStackCount - 1]
373 - (void)setPage:(WOComponent *)_page {
374 [_page ensureAwakeInContext:self];
375 ASSIGN(self->page, _page);
381 void WOContext_enterComponent
382 (WOContext *self, WOComponent *_component, WOElement *_content)
384 WOComponent *parent = nil;
386 NSCAssert(_component, @"missing component to enter ...");
390 [self->application logWithFormat:@"enter component %@ (content=%@) ..",
391 [_component name], _content];
394 parent = self->componentStackCount > 0
395 ? self->componentStack[self->componentStackCount - 1]
398 NSCAssert2(self->componentStackCount < NGObjWeb_MAX_COMPONENT_NESTING_DEPTH,
399 @"exceeded maximum component nesting depth (%i):\n%@",
400 NGObjWeb_MAX_COMPONENT_NESTING_DEPTH,
401 [self _componentStack]);
402 self->componentStack[(int)self->componentStackCount] = [_component retain];
403 self->contentStack[(int)self->componentStackCount] = [_content retain];
404 self->componentStackCount++;
406 [self _awakeComponent:_component];
409 if ([_component synchronizesVariablesWithBindings])
410 WOComponent_syncFromParent(_component, parent);
413 void WOContext_leaveComponent(WOContext *self, WOComponent *_component) {
414 WOComponent *parent = nil;
418 parent = (self->componentStackCount > 1)
419 ? self->componentStack[self->componentStackCount - 2]
423 if ([_component synchronizesVariablesWithBindings])
424 WOComponent_syncToParent(_component, parent);
427 PROFILE_CHECKPOINT("after sync");
429 /* remove last object */
430 self->componentStackCount--;
431 NSCAssert(self->componentStackCount >= 0,
432 @"tried to pop component from empty component stack !");
433 [self->componentStack[(int)self->componentStackCount] release];
434 self->componentStack[(int)self->componentStackCount] = nil;
435 [self->contentStack[(int)self->componentStackCount] release];
436 self->contentStack[(int)self->componentStackCount] = nil;
439 [self->application logWithFormat:@"left component %@.", [_component name]];
444 - (void)enterComponent:(WOComponent *)_comp content:(WOElement *)_content {
445 WOContext_enterComponent(self, _comp, _content);
447 - (void)leaveComponent:(WOComponent *)_component {
449 WOContext_leaveComponent(self, _component);
453 - (WOComponent *)parentComponent {
454 return (self->componentStackCount > 1)
455 ? self->componentStack[(int)self->componentStackCount - 2]
459 - (WODynamicElement *)componentContent {
460 return (self->componentStackCount > 0)
461 ? self->contentStack[(int)self->componentStackCount - 1]
465 - (unsigned)componentStackCount {
466 return self->componentStackCount;
468 - (NSArray *)_componentStack {
469 return [NSArray arrayWithObjects:self->componentStack
470 count:self->componentStackCount];
475 - (NSURL *)serverURL {
481 if ((rq = [self request]) == nil) {
482 [self logWithFormat:@"missing request in -baseURL call .."];
486 if ((serverURL = [rq headerForKey:@"x-webobjects-server-url"]) == nil) {
487 if ((host = [rq headerForKey:@"host"]))
488 serverURL = [@"http://" stringByAppendingString:host];
491 // TODO: fix that (host is also broken for example with SOUP)
492 /* sometimes the port is broken in the server URL ... */
493 if ([serverURL hasSuffix:@":0"]) { // bad bad bad
494 if ((host = [rq headerForKey:@"host"])) {
496 scheme = [serverURL hasPrefix:@"https://"] ? @"https://" : @"http://";
497 serverURL = [scheme stringByAppendingString:host];
502 if ([serverURL length] == 0) {
503 [self errorWithFormat:@"could not find x-webobjects-server-url header !"];
507 if ((url = [NSURL URLWithString:serverURL]) == nil) {
508 [self logWithFormat:@"could not construct NSURL from string '%@'",
520 return self->baseURL;
522 if ((rq = [self request]) == nil) {
523 [self logWithFormat:@"missing request in -baseURL call .."];
527 serverURL = [self serverURL];
529 [[NSURL URLWithString:[rq uri] relativeToURL:serverURL] retain];
531 if (self->baseURL == nil) {
533 @"could not construct NSURL for uri '%@' and base '%@' ...",
534 [rq uri], serverURL];
536 return self->baseURL;
539 - (NSURL *)applicationURL {
542 if (self->appURL != nil)
545 // TODO: we should ensure that the suffix (.woa) is in the URL
547 s = [self->request adaptorPrefix];
548 if ([s length] > 0) {
549 s = [[[s stringByAppendingString:@"/"]
550 stringByAppendingString:[self->request applicationName]]
551 stringByAppendingString:@"/"];
554 s = [[self->request applicationName] stringByAppendingString:@"/"];
557 [[NSURL URLWithString:s relativeToURL:[self serverURL]] retain];
560 - (NSURL *)urlForKey:(NSString *)_key {
561 _key = [_key stringByAppendingString:@"/"];
562 return [NSURL URLWithString:_key relativeToURL:[self applicationURL]];
567 - (void)setInForm:(BOOL)_form {
568 self->wcFlags.inForm = _form ? 1 : 0;
571 return self->wcFlags.inForm ? YES : NO;
574 - (void)addActiveFormElement:(WOElement *)_formElement {
575 if (self->activeFormElement) {
576 [[self component] debugWithFormat:@"active form element already set !"];
580 ASSIGN(self->activeFormElement, _formElement);
581 [self setRequestSenderID:[self elementID]];
583 - (WOElement *)activeFormElement {
584 return self->activeFormElement;
587 /* context variables (transient) */
589 - (void)setObject:(id)_obj forKey:(NSString *)_key {
590 if (self->variables == nil) {
592 [[NSMutableDictionary allocWithZone:[self zone]]
593 initWithCapacity:16];
597 [self->variables setObject:_obj forKey:_key];
599 [self->variables removeObjectForKey:_key];
601 - (id)objectForKey:(NSString *)_key {
602 return [self->variables objectForKey:_key];
604 - (void)removeObjectForKey:(NSString *)_key {
605 [self->variables removeObjectForKey:_key];
608 - (NSDictionary *)variableDictionary {
609 return self->variables;
612 - (void)takeValue:(id)_value forKey:(NSString *)_key {
613 if (WOSetKVCValueUsingMethod(self, _key, _value))
616 else if (WOGetKVCGetMethod(self, _key) == NULL) {
618 _value = [EONull null];
620 if (self->variables == nil) {
622 [[NSMutableDictionary allocWithZone:[self zone]]
623 initWithCapacity:16];
625 [self->variables setObject:_value forKey:_key];
629 // only a 'get' method is defined for _key !
630 [self handleTakeValue:_value forUnboundKey:_key];
633 - (id)valueForKey:(NSString *)_key {
636 if ((value = WOGetKVCValueUsingMethod(self, _key)))
638 value = [self->variables objectForKey:_key];
644 - (id)copyWithZone:(NSZone *)_zone {
645 return [self retain];
650 - (NSString *)description {
652 WOApplication *app = [self application];
654 if ([self hasSession])
655 sid = [[self session] sessionID];
657 return [NSString stringWithFormat:
658 @"<0x%08X[%@]: %@ app=%@ sn=%@ eid=%@ rqeid=%@>",
659 (unsigned)self, NSStringFromClass([self class]),
669 - (NSString *)elementID {
670 return [self->elementID elementID];
672 - (void)appendElementIDComponent:(NSString *)_eid {
673 [self->elementID appendElementIDComponent:_eid];
675 - (void)appendZeroElementIDComponent {
676 [self->elementID appendZeroElementIDComponent];
678 - (void)deleteAllElementIDComponents {
679 [self->elementID deleteAllElementIDComponents];
681 - (void)deleteLastElementIDComponent {
682 [self->elementID deleteLastElementIDComponent];
684 - (void)incrementLastElementIDComponent {
685 [self->elementID incrementLastElementIDComponent];
687 - (void)appendIntElementIDComponent:(int)_eid {
688 [self->elementID appendIntElementIDComponent:_eid];
691 /* the following can be later moved to WOElementID */
693 - (id)currentElementID {
694 return [self->reqElementID currentElementID];
696 - (id)consumeElementID {
697 return [self->reqElementID consumeElementID];
702 - (void)_generateCompleteURLs {
703 /* described in Apple TIL article 70101 */
706 - (void)setQueryPathSeparator:(NSString *)_sp {
707 ASSIGNCOPY(self->qpJoin, _sp);
709 - (NSString *)queryPathSeparator {
713 - (void)setGenerateXMLStyleEmptyElements:(BOOL)_flag {
714 self->wcFlags.xmlStyleEmptyElements = _flag ? 1 : 0;
716 - (BOOL)generateXMLStyleEmptyElements {
717 return self->wcFlags.xmlStyleEmptyElements ? YES : NO;
720 - (void)setGenerateEmptyAttributes:(BOOL)_flag {
721 self->wcFlags.allowEmptyAttributes = _flag ? 1 : 0;
723 - (BOOL)generateEmptyAttributes {
724 return self->wcFlags.allowEmptyAttributes ? YES : NO;
727 - (NSString *)queryStringFromDictionary:(NSDictionary *)_queryDict {
733 qs = [MutableStrClass stringWithCapacity:256];
734 keys = [_queryDict keyEnumerator];
735 for (isFirst = YES; (key = [keys nextObject]) != nil; ) {
738 value = [_queryDict objectForKey:key];
740 /* check for multi-value parameter */
742 if ([value isKindOfClass:[NSArray class]]) {
746 for (i = 0, count = [a count]; i < count; i++) {
747 value = [a objectAtIndex:i];
749 if (isFirst) isFirst = NO;
750 else [qs appendString:self->qpJoin];
752 // TODO: code duplication ...
753 value = ![value isNotNull] ? nil : [value stringValue];
754 key = [key stringByEscapingURL];
755 value = [value stringByEscapingURL];
757 [qs appendString:key];
759 [qs appendString:@"="];
760 [qs appendString:value];
766 /* regular, single-value parameter */
768 if (isFirst) isFirst = NO;
769 else [qs appendString:self->qpJoin];
771 value = ![value isNotNull] ? nil : [value stringValue];
772 key = [key stringByEscapingURL];
773 value = [value stringByEscapingURL];
775 [qs appendString:key];
777 [qs appendString:@"="];
778 [qs appendString:value];
785 - (NSString *)directActionURLForActionNamed:(NSString *)_actionName
786 queryDictionary:(NSDictionary *)_queryDict
788 NSMutableString *url;
791 url = [MutableStrClass stringWithCapacity:256];
794 [url appendString:@"/"];
796 [url appendString:_actionName];
798 /* add query parameters */
800 qs = [self queryStringFromDictionary:_queryDict];
802 return [self urlWithRequestHandlerKey:
803 [WOAppClass directActionRequestHandlerKey]
804 path:url queryString:qs];
807 - (NSString *)componentActionURL {
808 // TODO: add a -cComponentActionURL
809 // (without NSString for use with appendContentCString:)
811 // 26% -urlWithRequestHandler...
812 // 21% -elementID (was 40% !! :-)
813 // ~20% mutable string ops
818 self->wcFlags.savePageRequired = 1;
819 qs = [MutableStrClass stringWithCapacity:64];
821 [qs appendString:WORequestValueSenderID];
822 [qs appendString:@"="];
823 [qs appendString:[self elementID]];
824 [qs appendString:self->qpJoin];
825 [qs appendString:WORequestValueSessionID];
826 [qs appendString:@"="];
827 [qs appendString:[[self session] sessionID]];
828 [qs appendString:self->qpJoin];
829 [qs appendString:WORequestValueContextID];
830 [qs appendString:@"="];
831 [qs appendString:[self contextID]];
833 p = [[self page] componentActionURLForContext:self];
836 if ([p hasPrefix:@"/"]) p = [p substringFromIndex:1];
839 return [self urlWithRequestHandlerKey:
840 [WOAppClass componentRequestHandlerKey]
845 /* old style URLs ... */
846 static NSMutableString *url = nil; // THREAD
847 static IMP addStr = NULL;
850 self->wcFlags.savePageRequired = 1;
852 url = [[MutableStrClass alloc] initWithCapacity:256];
853 addStr = [url methodForSelector:@selector(appendString:)];
854 addStr(url, @selector(appendString:), @"/");
857 [url setString:@"/"];
860 Note: component actions *always* require sessions to be able to locate
861 the request component !
863 addStr(url, @selector(appendString:), [[self session] sessionID]);
864 addStr(url, @selector(appendString:), @"/");
865 addStr(url, @selector(appendString:), [self->elementID elementID]);
867 s = [self urlWithRequestHandlerKey:
868 [WOAppClass componentRequestHandlerKey]
869 path:url queryString:nil];
874 - (NSString *)urlWithRequestHandlerKey:(NSString *)_key
875 path:(NSString *)_path
876 queryString:(NSString *)_query
878 if (testNSURLs) { /* use NSURLs for processing */
881 if ([_path hasPrefix:@"/"]) {
883 [self warnWithFormat:@"got absolute path '%@'", _path];
885 _path = [_path substringFromIndex:1];
888 if (_key == nil) _key = [WOAppClass componentRequestHandlerKey];
889 rqUrl = [self urlForKey:_key];
891 if ([_query length] > 0) {
894 s = [_path mutableCopy];
895 [s appendString:@"?"];
896 [s appendString:_query];
897 rqUrl = [NSURL URLWithString:s relativeToURL:rqUrl];
901 rqUrl = [NSURL URLWithString:_path relativeToURL:rqUrl];
903 //[self logWithFormat:@"constructed component URL: %@", rqUrl];
905 return [rqUrl stringValueRelativeToURL:[self baseURL]];
908 NSMutableString *url;
912 if (_key == nil) _key = [WOAppClass componentRequestHandlerKey];
914 url = [MutableStrClass stringWithCapacity:256];
915 addStr = [url methodForSelector:@selector(appendString:)];
918 if (self->urlPrefix == nil) {
920 if ((tmp = [self->request headerForKey:@"x-webobjects-server-url"])) {
921 if ([tmp hasSuffix:@":0"] && [tmp length] > 2) // TODO: BAD BAD BAD
922 tmp = [tmp substringToIndex:([tmp length] - 2)];
923 addStr(url, @selector(appendString:), tmp);
925 else if ((tmp = [self->request headerForKey:@"host"])) {
926 addStr(url, @selector(appendString:), @"http://");
927 addStr(url, @selector(appendString:), tmp);
931 addStr(url, @selector(appendString:), [self->request adaptorPrefix]);
932 addStr(url, @selector(appendString:), @"/");
933 tmp = [[self request] applicationName];
934 if ([tmp length] == 0)
935 tmp = [(WOApplication *)[self application] name];
936 if ([tmp length] > 0) {
937 addStr(url, @selector(appendString:), tmp);
938 if (WOApplicationSuffix)
939 addStr(url, @selector(appendString:), WOApplicationSuffix);
940 addStr(url, @selector(appendString:), @"/");
944 self->urlPrefix = [url copy];
945 if (debugOn) [self debugWithFormat:@"URL prefix: '%@'", self->urlPrefix];
948 /* prefix is cached :-) */
949 addStr(url, @selector(appendString:), self->urlPrefix);
953 addStr(url, @selector(appendString:), _key);
955 addStr(url, @selector(appendString:), _path);
956 if ([_query length] > 0) {
957 addStr(url, @selector(appendString:), @"?");
958 addStr(url, @selector(appendString:), _query);
963 - (NSString *)completeURLWithRequestHandlerKey:(NSString *)_key
964 path:(NSString *)_path queryString:(NSString *)_query
965 isSecure:(BOOL)_isSecure port:(int)_port
967 NSMutableString *url = [MutableStrClass stringWithCapacity:256];
968 [url appendString:_isSecure ? @"https://" : @"http://"];
969 [url appendString:[[self request] headerForKey:@"host"]];
971 if (!(_isSecure && _port == 443) && !(!_isSecure && _port == 80))
972 [url appendFormat:@":%i", _port];
974 [url appendString:[self urlWithRequestHandlerKey:_key
976 queryString:_query]];
980 - (void)setRequestSenderID:(NSString *)_rid {
983 eid = [[WOElementID alloc] initWithString:_rid];
984 [self->reqElementID release];
985 self->reqElementID = eid;
987 - (NSString *)senderID {
989 return [self->reqElementID elementID];
991 NSMutableString *eid;
995 eid = [MutableStrClass stringWithCapacity:(self->reqElementIdCount * 4) + 1];
996 addStr = [eid methodForSelector:@selector(appendString:)];
997 for (i = 0; i < self->reqElementIdCount; i++) {
998 if (i != 0) addStr(eid, @selector(appendString:), @".");
999 addStr(eid, @selector(appendString:), [self->reqElementId[i] stringValue]);
1005 /* languages for resource lookup (non-WO) */
1007 - (NSArray *)resourceLookupLanguages {
1008 return [self hasSession]
1009 ? [[self session] languages]
1010 : [[self request] browserLanguages];
1014 /* DeprecatedMethodsInWO4 */
1017 if (self->application == nil)
1018 self->application = [WOAppClass application];
1020 if (self->application == nil) {
1021 [self logWithFormat:
1022 @"%s: missing application for context %@",
1023 __PRETTY_FUNCTION__, self];
1026 return self->application;
1029 - (void)setDistributionEnabled:(BOOL)_flag {
1031 [[self session] setDistributionEnabled:_flag];
1033 - (BOOL)isDistributionEnabled {
1035 return [[self session] isDistributionEnabled];
1039 return [self componentActionURL];
1042 - (NSString *)urlSessionPrefix {
1043 NSMutableString *url;
1046 url = [MutableStrClass stringWithCapacity:128];
1048 [url appendString:[[self request] adaptorPrefix]];
1049 [url appendString:@"/"];
1050 tmp = [[self request] applicationName];
1052 tmp ? tmp : [(WOApplication *)[self application] name]];
1055 if ([url length] == 0) {
1056 [self warnWithFormat:@"(%s): could not determine session URL prefix !",
1057 __PRETTY_FUNCTION__];
1064 @end /* WOContext */
1067 @implementation WOComponent(Cursors)
1069 - (void)pushCursor:(id)_obj {
1071 [self logWithFormat:@"enter cursor: %@", _obj];
1073 if (!self->cycleContext)
1074 self->cycleContext = [[NSMutableArray alloc] initWithCapacity:8];
1076 /* add to cursor stack */
1077 [self->cycleContext addObject:(_obj ? _obj : [NSNull null])];
1079 /* set active cursor */
1080 [self setObject:_obj forKey:@"_"];
1084 NSMutableArray *ctxStack;
1087 /* retrieve last context */
1088 old = [[self objectForKey:@"_"] retain];
1089 [self setObject:nil forKey:@"_"];
1091 /* restore old ctx */
1092 if ((ctxStack = self->cycleContext) != nil) {
1095 if ((count = [ctxStack count]) > 0) {
1096 [ctxStack removeObjectAtIndex:(count - 1)];
1102 obj = [ctxStack objectAtIndex:(count - 1)];
1104 if (![obj isNotNull]) obj = nil;
1105 [self setObject:obj forKey:@"_"];
1111 [self warnWithFormat:@"-popCursor called without cycle ctx !"];
1116 [self logWithFormat:@"leave cursor: %@ (restored=%@)",
1117 old, [self cursor]];
1120 return [old autorelease];
1124 NSMutableArray *ctxStack;
1126 // TODO: why do we check for _ODCycleCtx, if we query '_' ?
1128 if ((ctxStack = self->cycleContext) == nil)
1129 /* no cycle context setup for component ... */
1131 if ([ctxStack count] == 0)
1132 /* nothing contained in cycle context ... */
1135 return [self objectForKey:@"_"];
1138 @end /* WOComponent(Cursors) */