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];
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);
295 - (WOSession *)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];
367 - (WOComponent *)component {
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);
377 - (WOComponent *)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]); ) {
741 [qs appendString:self->qpJoin];
743 value = [[_queryDict objectForKey:key] stringValue];
745 key = [key stringByEscapingURL];
746 value = [value stringByEscapingURL];
748 [qs appendString:key];
750 [qs appendString:@"="];
751 [qs appendString:value];
758 - (NSString *)directActionURLForActionNamed:(NSString *)_actionName
759 queryDictionary:(NSDictionary *)_queryDict
761 NSMutableString *url;
764 url = [MutableStrClass stringWithCapacity:256];
767 [url appendString:@"/"];
769 [url appendString:_actionName];
771 /* add query parameters */
773 qs = [self queryStringFromDictionary:_queryDict];
775 return [self urlWithRequestHandlerKey:
776 [WOAppClass directActionRequestHandlerKey]
777 path:url queryString:qs];
780 - (NSString *)componentActionURL {
781 // TODO: add a -cComponentActionURL
782 // (without NSString for use with appendContentCString:)
784 // 26% -urlWithRequestHandler...
785 // 21% -elementID (was 40% !! :-)
786 // ~20% mutable string ops
791 self->wcFlags.savePageRequired = 1;
792 qs = [MutableStrClass stringWithCapacity:64];
794 [qs appendString:WORequestValueSenderID];
795 [qs appendString:@"="];
796 [qs appendString:[self elementID]];
797 [qs appendString:self->qpJoin];
798 [qs appendString:WORequestValueSessionID];
799 [qs appendString:@"="];
800 [qs appendString:[[self session] sessionID]];
801 [qs appendString:self->qpJoin];
802 [qs appendString:WORequestValueContextID];
803 [qs appendString:@"="];
804 [qs appendString:[self contextID]];
806 p = [[self page] componentActionURLForContext:self];
809 if ([p hasPrefix:@"/"]) p = [p substringFromIndex:1];
812 return [self urlWithRequestHandlerKey:
813 [WOAppClass componentRequestHandlerKey]
818 /* old style URLs ... */
819 static NSMutableString *url = nil; // THREAD
820 static IMP addStr = NULL;
823 self->wcFlags.savePageRequired = 1;
825 url = [[MutableStrClass alloc] initWithCapacity:256];
826 addStr = [url methodForSelector:@selector(appendString:)];
827 addStr(url, @selector(appendString:), @"/");
830 [url setString:@"/"];
833 Note: component actions *always* require sessions to be able to locate
834 the request component !
836 addStr(url, @selector(appendString:), [[self session] sessionID]);
837 addStr(url, @selector(appendString:), @"/");
838 addStr(url, @selector(appendString:), [self->elementID elementID]);
840 s = [self urlWithRequestHandlerKey:
841 [WOAppClass componentRequestHandlerKey]
842 path:url queryString:nil];
847 - (NSString *)urlWithRequestHandlerKey:(NSString *)_key
848 path:(NSString *)_path
849 queryString:(NSString *)_query
851 if (testNSURLs) { /* use NSURLs for processing */
854 if ([_path hasPrefix:@"/"]) {
856 [self warnWithFormat:@"got absolute path '%@'", _path];
858 _path = [_path substringFromIndex:1];
861 if (_key == nil) _key = [WOAppClass componentRequestHandlerKey];
862 rqUrl = [self urlForKey:_key];
864 if ([_query length] > 0) {
867 s = [_path mutableCopy];
868 [s appendString:@"?"];
869 [s appendString:_query];
870 rqUrl = [NSURL URLWithString:s relativeToURL:rqUrl];
874 rqUrl = [NSURL URLWithString:_path relativeToURL:rqUrl];
876 //[self logWithFormat:@"constructed component URL: %@", rqUrl];
878 return [rqUrl stringValueRelativeToURL:[self baseURL]];
881 NSMutableString *url;
885 if (_key == nil) _key = [WOAppClass componentRequestHandlerKey];
887 url = [MutableStrClass stringWithCapacity:256];
888 addStr = [url methodForSelector:@selector(appendString:)];
891 if (self->urlPrefix == nil) {
893 if ((tmp = [self->request headerForKey:@"x-webobjects-server-url"])) {
894 if ([tmp hasSuffix:@":0"] && [tmp length] > 2) // TODO: BAD BAD BAD
895 tmp = [tmp substringToIndex:([tmp length] - 2)];
896 addStr(url, @selector(appendString:), tmp);
898 else if ((tmp = [self->request headerForKey:@"host"])) {
899 addStr(url, @selector(appendString:), @"http://");
900 addStr(url, @selector(appendString:), tmp);
904 addStr(url, @selector(appendString:), [self->request adaptorPrefix]);
905 addStr(url, @selector(appendString:), @"/");
906 tmp = [[self request] applicationName];
907 if ([tmp length] == 0)
908 tmp = [(WOApplication *)[self application] name];
909 if ([tmp length] > 0) {
910 addStr(url, @selector(appendString:), tmp);
911 if (WOApplicationSuffix)
912 addStr(url, @selector(appendString:), WOApplicationSuffix);
913 addStr(url, @selector(appendString:), @"/");
917 self->urlPrefix = [url copy];
918 if (debugOn) [self debugWithFormat:@"URL prefix: '%@'", self->urlPrefix];
921 /* prefix is cached :-) */
922 addStr(url, @selector(appendString:), self->urlPrefix);
926 addStr(url, @selector(appendString:), _key);
928 addStr(url, @selector(appendString:), _path);
929 if ([_query length] > 0) {
930 addStr(url, @selector(appendString:), @"?");
931 addStr(url, @selector(appendString:), _query);
936 - (NSString *)completeURLWithRequestHandlerKey:(NSString *)_key
937 path:(NSString *)_path queryString:(NSString *)_query
938 isSecure:(BOOL)_isSecure port:(int)_port
940 NSMutableString *url = [MutableStrClass stringWithCapacity:256];
941 [url appendString:_isSecure ? @"https://" : @"http://"];
942 [url appendString:[[self request] headerForKey:@"host"]];
944 if (!(_isSecure && _port == 443) && !(!_isSecure && _port == 80))
945 [url appendFormat:@":%i", _port];
947 [url appendString:[self urlWithRequestHandlerKey:_key
949 queryString:_query]];
953 - (void)setRequestSenderID:(NSString *)_rid {
956 eid = [[WOElementID alloc] initWithString:_rid];
957 [self->reqElementID release];
958 self->reqElementID = eid;
960 - (NSString *)senderID {
962 return [self->reqElementID elementID];
964 NSMutableString *eid;
968 eid = [MutableStrClass stringWithCapacity:(self->reqElementIdCount * 4) + 1];
969 addStr = [eid methodForSelector:@selector(appendString:)];
970 for (i = 0; i < self->reqElementIdCount; i++) {
971 if (i != 0) addStr(eid, @selector(appendString:), @".");
972 addStr(eid, @selector(appendString:), [self->reqElementId[i] stringValue]);
978 /* languages for resource lookup (non-WO) */
980 - (NSArray *)resourceLookupLanguages {
981 return [self hasSession] ? [[self session] languages]
982 : [[self request] browserLanguages];
986 /* DeprecatedMethodsInWO4 */
988 - (WOApplication *)application {
989 if (self->application == nil)
990 self->application = [WOAppClass application];
992 if (self->application == nil)
993 NSLog(@"%s: missing application for context %@", __PRETTY_FUNCTION__, self);
995 return self->application;
998 - (void)setDistributionEnabled:(BOOL)_flag {
1000 [[self session] setDistributionEnabled:_flag];
1002 - (BOOL)isDistributionEnabled {
1004 return [[self session] isDistributionEnabled];
1008 return [self componentActionURL];
1011 - (NSString *)urlSessionPrefix {
1012 NSMutableString *url;
1015 url = [MutableStrClass stringWithCapacity:128];
1017 [url appendString:[[self request] adaptorPrefix]];
1018 [url appendString:@"/"];
1019 tmp = [[self request] applicationName];
1021 tmp ? tmp : [(WOApplication *)[self application] name]];
1024 if ([url length] == 0) {
1025 [self warnWithFormat:@"(%s): could not determine session URL prefix !",
1026 __PRETTY_FUNCTION__];
1033 @end /* WOContext */
1036 @implementation WOComponent(Cursors)
1038 - (void)pushCursor:(id)_obj {
1040 [self logWithFormat:@"enter cursor: %@", _obj];
1042 if (!self->cycleContext)
1043 self->cycleContext = [[NSMutableArray alloc] initWithCapacity:8];
1045 /* add to cursor stack */
1046 [self->cycleContext addObject:(_obj ? _obj : [NSNull null])];
1048 /* set active cursor */
1049 [self setObject:_obj forKey:@"_"];
1053 NSMutableArray *ctxStack;
1056 /* retrieve last context */
1057 old = [[self objectForKey:@"_"] retain];
1058 [self setObject:nil forKey:@"_"];
1060 /* restore old ctx */
1061 if ((ctxStack = self->cycleContext) != nil) {
1064 if ((count = [ctxStack count]) > 0) {
1065 [ctxStack removeObjectAtIndex:(count - 1)];
1071 obj = [ctxStack objectAtIndex:(count - 1)];
1073 if (![obj isNotNull]) obj = nil;
1074 [self setObject:obj forKey:@"_"];
1080 [self warnWithFormat:@"-popCursor called without cycle ctx !"];
1085 [self logWithFormat:@"leave cursor: %@ (restored=%@)",
1086 old, [self cursor]];
1089 return [old autorelease];
1093 NSMutableArray *ctxStack;
1095 // TODO: why do we check for _ODCycleCtx, if we query '_' ?
1097 if ((ctxStack = self->cycleContext) == nil)
1098 /* no cycle context setup for component ... */
1100 if ([ctxStack count] == 0)
1101 /* nothing contained in cycle context ... */
1104 return [self objectForKey:@"_"];
1107 @end /* WOComponent(Cursors) */