2 Copyright (C) 2000-2006 SKYRIX Software AG
3 Copyright (C) 2006 Helge Hess
5 This file is part of SOPE.
7 SOPE is free software; you can redistribute it and/or modify it under
8 the terms of the GNU Lesser General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
12 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with SOPE; see the file COPYING. If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23 #include <NGObjWeb/WOContext.h>
24 #include "NSObject+WO.h"
25 #include "WOComponent+private.h"
26 #include "WOContext+private.h"
27 #include "WOApplication+private.h"
28 #include <NGObjWeb/WOApplication.h>
29 #include <NGObjWeb/WORequest.h>
30 #include <NGObjWeb/WOResponse.h>
31 #include <NGObjWeb/WOSession.h>
32 #include <Foundation/NSNull.h>
33 #include "WOElementID.h"
38 @interface WOContext(Privates5)
39 - (NSArray *)_componentStack;
42 @interface WOComponent(Cursors)
43 - (void)pushCursor:(id)_obj;
48 static Class WOAppClass = Nil;
50 @implementation WOContext
56 static Class WOContextClass = Nil;
57 static Class MutableStrClass = Nil;
58 static int contextCount = 0;
59 static int logComponents = -1;
60 static int relativeURLs = -1;
61 static BOOL debugOn = NO;
62 static int debugCursor = -1;
63 static BOOL debugComponentAwake = NO;
64 static BOOL testNSURLs = NO;
65 static BOOL newCURLStyle = NO;
66 static NSString *WOApplicationSuffix = nil;
69 static BOOL didInit = NO;
77 ud = [NSUserDefaults standardUserDefaults];
79 if (WOAppClass == Nil)
80 WOAppClass = [WOApplication class];
81 if (MutableStrClass == Nil)
82 MutableStrClass = [NSMutableString class];
84 cn = [ud stringForKey:@"WOContextClass"];
85 WOContextClass = NSClassFromString(cn);
86 NSAssert1(WOContextClass != Nil,
87 @"Couldn't instantiate WOContextClass (%@)!", cn);
89 logComponents = [[ud objectForKey:@"WOLogComponents"] boolValue] ? 1 : 0;
90 relativeURLs = [[ud objectForKey:@"WOUseRelativeURLs"] boolValue]? 1 : 0;
91 debugCursor = [ud boolForKey:@"WODebugCursor"] ? 1 : 0;
92 debugComponentAwake = [ud boolForKey:@"WODebugComponentAwake"];
93 WOApplicationSuffix = [[ud stringForKey:@"WOApplicationSuffix"] copy];
96 + (id)contextWithRequest:(WORequest *)_r {
97 return [[(WOContext *)[WOContextClass alloc] initWithRequest:_r] autorelease];
100 - (id)initWithRequest:(WORequest *)_request {
101 if ((self = [super init])) {
103 self->qpJoin = @"&";
105 sprintf(buf, "%03x%08x%08x", ++contextCount, (int)time(NULL),
106 (unsigned int)(unsigned long)self);
107 self->ctxId = [[NSString alloc] initWithCString:buf];
109 /* per default close tags in XML style */
110 self->wcFlags.xmlStyleEmptyElements = 1;
111 self->wcFlags.allowEmptyAttributes = 0;
113 self->elementID = [[WOElementID alloc] init];
114 self->awakeComponents = [[NSMutableSet alloc] initWithCapacity:64];
116 self->request = [_request retain];
117 self->response = [[WOResponse responseWithRequest:_request] retain];
123 return [[[self alloc] init] autorelease];
126 return [self initWithRequest:nil];
131 - (void)_addAwakeComponent:(WOComponent *)_component {
132 if (_component == nil)
135 if ([self->awakeComponents containsObject:_component])
138 /* wake up component */
139 if (debugComponentAwake)
140 [self logWithFormat:@"mark component awake: %@", _component];
142 [self->awakeComponents addObject:_component];
145 - (void)_awakeComponent:(WOComponent *)_component {
146 if (_component == nil)
149 if ([self->awakeComponents containsObject:_component])
152 /* wake up component */
153 if (debugComponentAwake)
154 [self logWithFormat:@"awake component: %@", _component];
156 [_component _awakeWithContext:self];
158 [self _addAwakeComponent:_component];
160 if (debugComponentAwake)
161 [self logWithFormat:@"woke up component: %@", _component];
164 - (void)sleepComponents {
166 WOComponent *component;
167 BOOL sendSleepToPage;
169 if (debugComponentAwake) {
170 [self logWithFormat:@"sleep %d components ...",
171 [self->awakeComponents count]];
174 sendSleepToPage = YES;
175 e = [self->awakeComponents objectEnumerator];
176 while ((component = [e nextObject])) {
177 if (debugComponentAwake)
178 [self logWithFormat:@" sleep component: %@", component];
179 [component _sleepWithContext:self];
180 if (component == self->page) sendSleepToPage = NO;
182 if (sendSleepToPage && (self->page != nil)) {
183 if (debugComponentAwake)
184 [self logWithFormat:@" sleep page: %@", self->page];
185 [self->page _sleepWithContext:self];
188 if (debugComponentAwake) {
189 [self logWithFormat:@"done sleep %d components.",
190 [self->awakeComponents count]];
192 [self->awakeComponents removeAllObjects];
195 #if WITH_DEALLOC_OBSERVERS
196 - (void)addDeallocObserver:(id)_observer {
197 if (_observer == NULL) return;
200 if (self->deallocObservers == NULL) {
201 self->deallocObservers = calloc(8, sizeof(id));
202 self->deallocObserverCount = 0;
203 self->deallocObserverCapacity = 8;
207 if (self->deallocObserverCapacity == self->deallocObserverCount) {
208 /* need to increase array */
211 newa = calloc(self->deallocObserverCapacity * 2, sizeof(id));
212 memcpy(newa, self->deallocObservers,
213 sizeof(id) * self->deallocObserverCount);
214 free(self->deallocObservers);
215 self->deallocObservers = newa;
216 self->deallocObserverCapacity *= 2;
220 self->deallocObservers[self->deallocObserverCount] = _observer;
221 self->deallocObserverCount++;
223 - (void)removeDeallocObserver:(id)_observer {
224 /* the observer currently will only grow (this should be OK for WOContext) */
226 if (_observer == NULL) return;
228 for (i = self->deallocObserverCount - 1; i >= 0; i++) {
229 if ((self->deallocObservers[i]) == _observer)
230 self->deallocObservers[i] = NULL;
236 [self sleepComponents];
238 #if WITH_DEALLOC_OBSERVERS
239 if (self->deallocObservers) {
243 printf("%s: dealloc observer capacity: %i\n",
244 __PRETTY_FUNCTION__, self->deallocObserverCapacity);
247 /* GC!! process in reverse order ... */
248 for (i = self->deallocObserverCount - 1; i >= 0; i++)
249 [self->deallocObservers[i] _objectWillDealloc:self];
251 free(self->deallocObservers);
252 self->deallocObservers = NULL;
256 [self->activeUser release];
257 [self->rootURL release];
258 [self->objectPermissionCache release];
259 [self->traversalStack release];
260 [self->clientObject release];
261 [self->objectDispatcher release];
262 [self->soRequestType release];
263 [self->pathInfo release];
265 [[NSNotificationCenter defaultCenter]
266 postNotificationName:@"WOContextWillDeallocate"
269 { /* release component stack */
271 for (i = (self->componentStackCount - 1); i >= 0; i--) {
272 [self->componentStack[i] release]; self->componentStack[i] = nil;
273 [self->contentStack[i] release]; self->contentStack[i] = nil;
277 [self->urlPrefix release];
278 [self->elementID release];
279 [self->reqElementID release];
280 [self->activeFormElement release];
281 [self->page release];
282 [self->awakeComponents release];
283 [self->appURL release];
284 [self->baseURL release];
285 [self->session release];
286 [self->variables release];
287 [self->request release];
288 [self->response release];
289 [self->ctxId release];
295 - (void)setSession:(WOSession *)_session {
296 ASSIGN(self->session, _session);
298 - (void)setNewSession:(WOSession *)_session {
299 [self setSession:_session];
300 self->wcFlags.hasNewSession = 1;
304 /* in WO4 -session creates a new session if none is associated */
306 if (self->session == nil) {
307 [[self application] _initializeSessionInContext:self];
309 if (self->session == nil) {
310 [self logWithFormat:@"%s: missing session for context ..",
311 __PRETTY_FUNCTION__];
315 return self->session;
318 - (NSString *)contextID {
319 NSAssert(self->ctxId, @"context without id !");
321 // in WO4 -contextID returns nil if there is no associated session
322 return self->session ? self->ctxId : nil;
325 IMHO the above isn't true, otherwise session cannot be automagically
328 TODO: well, we might want to generate component URLs which work without
329 a session - at least in theory the ID tree should be stable even
330 without a session (and if proper uids are used for dynamic content).
331 eg this would be quite useful for SOPE.
337 - (WORequest *)request {
338 return self->request;
340 - (WOResponse *)response {
341 return self->response;
345 return (self->session != nil) ? YES : NO;
347 - (BOOL)hasNewSession {
348 if (!self->wcFlags.hasNewSession)
350 return [self hasSession];
353 - (BOOL)savePageRequired {
354 return self->wcFlags.savePageRequired ? YES : NO;
359 - (void)pushCursor:(id)_obj {
360 if (debugCursor == -1) {
361 debugCursor = [[NSUserDefaults standardUserDefaults]
362 boolForKey:@"WODebugCursor"]
366 if (debugCursor) [self logWithFormat:@"enter cursor: %@", _obj];
367 [[self component] pushCursor:_obj];
371 if (debugCursor) [self logWithFormat:@"leave cursor ..."];
372 return [[self component] popCursor];
376 return [(id <WOPageGenerationContext>)[self component] cursor];
382 return (self->componentStackCount > 0)
383 ? self->componentStack[self->componentStackCount - 1]
387 - (void)setPage:(WOComponent *)_page {
388 [_page ensureAwakeInContext:self];
389 ASSIGN(self->page, _page);
395 void WOContext_enterComponent
396 (WOContext *self, WOComponent *_component, WOElement *_content)
398 WOComponent *parent = nil;
400 NSCAssert(_component, @"missing component to enter ...");
404 [self->application logWithFormat:@"enter component %@ (content=%@) ..",
405 [_component name], _content];
408 parent = self->componentStackCount > 0
409 ? self->componentStack[self->componentStackCount - 1]
412 NSCAssert2(self->componentStackCount < NGObjWeb_MAX_COMPONENT_NESTING_DEPTH,
413 @"exceeded maximum component nesting depth (%i):\n%@",
414 NGObjWeb_MAX_COMPONENT_NESTING_DEPTH,
415 [self _componentStack]);
416 self->componentStack[(int)self->componentStackCount] = [_component retain];
417 self->contentStack[(int)self->componentStackCount] = [_content retain];
418 self->componentStackCount++;
420 [self _awakeComponent:_component];
423 if ([_component synchronizesVariablesWithBindings])
424 WOComponent_syncFromParent(_component, parent);
427 void WOContext_leaveComponent(WOContext *self, WOComponent *_component) {
428 WOComponent *parent = nil;
432 parent = (self->componentStackCount > 1)
433 ? self->componentStack[self->componentStackCount - 2]
437 if ([_component synchronizesVariablesWithBindings])
438 WOComponent_syncToParent(_component, parent);
441 PROFILE_CHECKPOINT("after sync");
443 /* remove last object */
444 self->componentStackCount--;
445 NSCAssert(self->componentStackCount >= 0,
446 @"tried to pop component from empty component stack !");
447 [self->componentStack[(int)self->componentStackCount] release];
448 self->componentStack[(int)self->componentStackCount] = nil;
449 [self->contentStack[(int)self->componentStackCount] release];
450 self->contentStack[(int)self->componentStackCount] = nil;
453 [self->application logWithFormat:@"left component %@.", [_component name]];
458 - (void)enterComponent:(WOComponent *)_comp content:(WOElement *)_content {
459 WOContext_enterComponent(self, _comp, _content);
461 - (void)leaveComponent:(WOComponent *)_component {
463 WOContext_leaveComponent(self, _component);
467 - (WOComponent *)parentComponent {
468 return (self->componentStackCount > 1)
469 ? self->componentStack[(int)self->componentStackCount - 2]
473 - (WODynamicElement *)componentContent {
474 return (self->componentStackCount > 0)
475 ? self->contentStack[(int)self->componentStackCount - 1]
479 - (unsigned)componentStackCount {
480 return self->componentStackCount;
482 - (NSArray *)_componentStack {
483 return [NSArray arrayWithObjects:self->componentStack
484 count:self->componentStackCount];
489 - (NSURL *)serverURL {
495 if ((rq = [self request]) == nil) {
496 [self logWithFormat:@"missing request in -baseURL call .."];
500 if ((serverURL = [rq headerForKey:@"x-webobjects-server-url"]) == nil) {
501 if ((host = [rq headerForKey:@"host"]))
502 serverURL = [@"http://" stringByAppendingString:host];
505 // TODO: fix that (host is also broken for example with SOUP)
506 /* sometimes the port is broken in the server URL ... */
507 if ([serverURL hasSuffix:@":0"]) { // bad bad bad
508 if ((host = [rq headerForKey:@"host"])) {
510 scheme = [serverURL hasPrefix:@"https://"] ? @"https://" : @"http://";
511 serverURL = [scheme stringByAppendingString:host];
516 if ([serverURL length] == 0) {
517 [self errorWithFormat:@"could not find x-webobjects-server-url header !"];
521 if ((url = [NSURL URLWithString:serverURL]) == nil) {
522 [self logWithFormat:@"could not construct NSURL from string '%@'",
534 return self->baseURL;
536 if ((rq = [self request]) == nil) {
537 [self logWithFormat:@"missing request in -baseURL call .."];
541 serverURL = [self serverURL];
543 [[NSURL URLWithString:[rq uri] relativeToURL:serverURL] retain];
545 if (self->baseURL == nil) {
547 @"could not construct NSURL for uri '%@' and base '%@' ...",
548 [rq uri], serverURL];
550 return self->baseURL;
553 - (NSURL *)applicationURL {
556 if (self->appURL != nil)
559 // TODO: we should ensure that the suffix (.woa) is in the URL
561 s = [self->request adaptorPrefix];
562 if ([s length] > 0) {
563 s = [[[s stringByAppendingString:@"/"]
564 stringByAppendingString:[self->request applicationName]]
565 stringByAppendingString:@"/"];
568 s = [[self->request applicationName] stringByAppendingString:@"/"];
571 [[NSURL URLWithString:s relativeToURL:[self serverURL]] retain];
574 - (NSURL *)urlForKey:(NSString *)_key {
575 _key = [_key stringByAppendingString:@"/"];
576 return [NSURL URLWithString:_key relativeToURL:[self applicationURL]];
581 - (void)setInForm:(BOOL)_form {
582 self->wcFlags.inForm = _form ? 1 : 0;
585 return self->wcFlags.inForm ? YES : NO;
588 - (void)addActiveFormElement:(WOElement *)_formElement {
589 if (self->activeFormElement) {
590 [[self component] debugWithFormat:@"active form element already set !"];
594 ASSIGN(self->activeFormElement, _formElement);
595 [self setRequestSenderID:[self elementID]];
597 - (WOElement *)activeFormElement {
598 return self->activeFormElement;
601 /* context variables (transient) */
603 - (void)setObject:(id)_obj forKey:(NSString *)_key {
604 if (self->variables == nil) {
606 [[NSMutableDictionary allocWithZone:[self zone]]
607 initWithCapacity:16];
611 [self->variables setObject:_obj forKey:_key];
613 [self->variables removeObjectForKey:_key];
615 - (id)objectForKey:(NSString *)_key {
616 return [self->variables objectForKey:_key];
618 - (void)removeObjectForKey:(NSString *)_key {
619 [self->variables removeObjectForKey:_key];
622 - (NSDictionary *)variableDictionary {
623 return self->variables;
626 - (void)takeValue:(id)_value forKey:(NSString *)_key {
627 if (WOSetKVCValueUsingMethod(self, _key, _value))
630 else if (WOGetKVCGetMethod(self, _key) == NULL) {
632 _value = [NSNull null];
634 if (self->variables == nil) {
636 [[NSMutableDictionary allocWithZone:[self zone]]
637 initWithCapacity:16];
639 [self->variables setObject:_value forKey:_key];
643 // only a 'get' method is defined for _key !
644 [self handleTakeValue:_value forUnboundKey:_key];
647 - (id)valueForKey:(NSString *)_key {
650 if ((value = WOGetKVCValueUsingMethod(self, _key)))
652 value = [self->variables objectForKey:_key];
658 - (id)copyWithZone:(NSZone *)_zone {
659 return [self retain];
664 - (NSString *)description {
666 WOApplication *app = [self application];
668 if ([self hasSession])
669 sid = [[self session] sessionID];
671 return [NSString stringWithFormat:
672 @"<0x%p[%@]: %@ app=%@ sn=%@ eid=%@ rqeid=%@>",
673 self, NSStringFromClass([self class]),
676 sid != nil ? sid : (NSString *)@"none",
683 - (NSString *)elementID {
684 return [self->elementID elementID];
686 - (void)appendElementIDComponent:(NSString *)_eid {
687 [self->elementID appendElementIDComponent:_eid];
689 - (void)appendZeroElementIDComponent {
690 [self->elementID appendZeroElementIDComponent];
692 - (void)deleteAllElementIDComponents {
693 [self->elementID deleteAllElementIDComponents];
695 - (void)deleteLastElementIDComponent {
696 [self->elementID deleteLastElementIDComponent];
698 - (void)incrementLastElementIDComponent {
699 [self->elementID incrementLastElementIDComponent];
701 - (void)appendIntElementIDComponent:(int)_eid {
702 [self->elementID appendIntElementIDComponent:_eid];
705 /* the following can be later moved to WOElementID */
707 - (id)currentElementID {
708 return [self->reqElementID currentElementID];
710 - (id)consumeElementID {
711 return [self->reqElementID consumeElementID];
716 - (void)_generateCompleteURLs {
717 /* described in Apple TIL article 70101 */
720 - (void)setQueryPathSeparator:(NSString *)_sp {
721 ASSIGNCOPY(self->qpJoin, _sp);
723 - (NSString *)queryPathSeparator {
727 - (void)setGenerateXMLStyleEmptyElements:(BOOL)_flag {
728 self->wcFlags.xmlStyleEmptyElements = _flag ? 1 : 0;
730 - (BOOL)generateXMLStyleEmptyElements {
731 return self->wcFlags.xmlStyleEmptyElements ? YES : NO;
734 - (void)setGenerateEmptyAttributes:(BOOL)_flag {
735 self->wcFlags.allowEmptyAttributes = _flag ? 1 : 0;
737 - (BOOL)generateEmptyAttributes {
738 return self->wcFlags.allowEmptyAttributes ? YES : NO;
741 - (NSString *)queryStringFromDictionary:(NSDictionary *)_queryDict {
747 qs = [MutableStrClass stringWithCapacity:256];
748 keys = [_queryDict keyEnumerator];
749 for (isFirst = YES; (key = [keys nextObject]) != nil; ) {
752 value = [_queryDict objectForKey:key];
754 /* check for multi-value parameter */
756 if ([value isKindOfClass:[NSArray class]]) {
760 for (i = 0, count = [a count]; i < count; i++) {
761 value = [a objectAtIndex:i];
763 if (isFirst) isFirst = NO;
764 else [qs appendString:self->qpJoin];
766 // TODO: code duplication ...
767 value = ![value isNotNull] ? (NSString *)nil : [value stringValue];
768 key = [key stringByEscapingURL];
769 value = [value stringByEscapingURL];
771 [qs appendString:key];
773 [qs appendString:@"="];
774 [qs appendString:value];
780 /* regular, single-value parameter */
782 if (isFirst) isFirst = NO;
783 else [qs appendString:self->qpJoin];
785 value = ![value isNotNull] ? (NSString *)nil : [value stringValue];
786 key = [key stringByEscapingURL];
787 value = [value stringByEscapingURL];
789 [qs appendString:key];
791 [qs appendString:@"="];
792 [qs appendString:value];
799 - (NSString *)directActionURLForActionNamed:(NSString *)_actionName
800 queryDictionary:(NSDictionary *)_queryDict
802 NSMutableString *url;
805 url = [MutableStrClass stringWithCapacity:256];
808 [url appendString:@"/"];
810 [url appendString:_actionName];
812 /* add query parameters */
814 qs = [self queryStringFromDictionary:_queryDict];
816 return [self urlWithRequestHandlerKey:
817 [WOAppClass directActionRequestHandlerKey]
818 path:url queryString:qs];
821 - (NSString *)componentActionURL {
822 // TODO: add a -cComponentActionURL
823 // (without NSString for use with appendContentCString:)
825 // 26% -urlWithRequestHandler...
826 // 21% -elementID (was 40% !! :-)
827 // ~20% mutable string ops
830 This makes the request handler save the page in the session at the
831 end of the request (only necessary if the page generates URLs which
834 self->wcFlags.savePageRequired = 1;
837 // TODO: who uses that? Its not enabled per default
838 // TODO: what does this do?
842 qs = [MutableStrClass stringWithCapacity:64];
844 [qs appendString:WORequestValueSenderID];
845 [qs appendString:@"="];
846 [qs appendString:[self elementID]];
847 [qs appendString:self->qpJoin];
848 [qs appendString:WORequestValueSessionID];
849 [qs appendString:@"="];
850 [qs appendString:[[self session] sessionID]];
851 [qs appendString:self->qpJoin];
852 [qs appendString:WORequestValueContextID];
853 [qs appendString:@"="];
854 [qs appendString:[self contextID]];
856 p = [[self page] componentActionURLForContext:self];
859 if ([p hasPrefix:@"/"]) p = [p substringFromIndex:1];
862 return [self urlWithRequestHandlerKey:
863 [WOAppClass componentRequestHandlerKey]
868 /* old style URLs ... */
869 static NSMutableString *url = nil; // THREAD
870 static IMP addStr = NULL;
874 coRqhKey = [WOAppClass componentRequestHandlerKey];
877 Optimization: use relative URL if the request already was a component
878 action (with a valid session)
880 if (!self->wcFlags.hasNewSession) {
881 if ([[self->request requestHandlerKey] isEqualToString:coRqhKey])
882 return [self->elementID elementID];
886 url = [[MutableStrClass alloc] initWithCapacity:256];
887 addStr = [url methodForSelector:@selector(appendString:)];
888 addStr(url, @selector(appendString:), @"/");
891 [url setString:@"/"];
894 Note: component actions *always* require sessions to be able to locate
895 the request component !
897 addStr(url, @selector(appendString:), [[self session] sessionID]);
898 addStr(url, @selector(appendString:), @"/");
899 addStr(url, @selector(appendString:), [self->elementID elementID]);
901 s = [self urlWithRequestHandlerKey:coRqhKey
902 path:url queryString:nil];
907 - (NSString *)urlWithRequestHandlerKey:(NSString *)_key
908 path:(NSString *)_path
909 queryString:(NSString *)_query
911 if (testNSURLs) { /* use NSURLs for processing */
914 if ([_path hasPrefix:@"/"]) {
916 [self warnWithFormat:@"got absolute path '%@'", _path];
918 _path = [_path substringFromIndex:1];
921 if (_key == nil) _key = [WOAppClass componentRequestHandlerKey];
922 rqUrl = [self urlForKey:_key];
924 if ([_query length] > 0) {
927 s = [_path mutableCopy];
928 [s appendString:@"?"];
929 [s appendString:_query];
930 rqUrl = [NSURL URLWithString:s relativeToURL:rqUrl];
934 rqUrl = [NSURL URLWithString:_path relativeToURL:rqUrl];
936 //[self logWithFormat:@"constructed component URL: %@", rqUrl];
938 return [rqUrl stringValueRelativeToURL:[self baseURL]];
941 NSMutableString *url;
945 if (_key == nil) _key = [WOAppClass componentRequestHandlerKey];
947 url = [MutableStrClass stringWithCapacity:256];
948 addStr = [url methodForSelector:@selector(appendString:)];
951 if (self->urlPrefix == nil) {
953 if ((tmp = [self->request headerForKey:@"x-webobjects-server-url"])) {
954 if ([tmp hasSuffix:@":0"] && [tmp length] > 2) // TODO: BAD BAD BAD
955 tmp = [tmp substringToIndex:([tmp length] - 2)];
956 addStr(url, @selector(appendString:), tmp);
958 else if ((tmp = [self->request headerForKey:@"host"])) {
959 addStr(url, @selector(appendString:), @"http://");
960 addStr(url, @selector(appendString:), tmp);
964 addStr(url, @selector(appendString:), [self->request adaptorPrefix]);
965 addStr(url, @selector(appendString:), @"/");
966 tmp = [[self request] applicationName];
967 if ([tmp length] == 0)
968 tmp = [(WOApplication *)[self application] name];
969 if ([tmp length] > 0) {
970 addStr(url, @selector(appendString:), tmp);
971 if (WOApplicationSuffix)
972 addStr(url, @selector(appendString:), WOApplicationSuffix);
973 addStr(url, @selector(appendString:), @"/");
977 self->urlPrefix = [url copy];
978 if (debugOn) [self debugWithFormat:@"URL prefix: '%@'", self->urlPrefix];
981 /* prefix is cached :-) */
982 addStr(url, @selector(appendString:), self->urlPrefix);
986 addStr(url, @selector(appendString:), _key);
988 addStr(url, @selector(appendString:), _path);
989 if ([_query length] > 0) {
990 addStr(url, @selector(appendString:), @"?");
991 addStr(url, @selector(appendString:), _query);
996 - (NSString *)completeURLWithRequestHandlerKey:(NSString *)_key
997 path:(NSString *)_path queryString:(NSString *)_query
998 isSecure:(BOOL)_isSecure port:(int)_port
1000 NSMutableString *url = [MutableStrClass stringWithCapacity:256];
1001 [url appendString:_isSecure ? @"https://" : @"http://"];
1002 [url appendString:[[self request] headerForKey:@"host"]];
1004 if (!(_isSecure && _port == 443) && !(!_isSecure && _port == 80))
1005 [url appendFormat:@":%i", _port];
1007 [url appendString:[self urlWithRequestHandlerKey:_key
1009 queryString:_query]];
1013 - (void)setRequestSenderID:(NSString *)_rid {
1016 eid = [[WOElementID alloc] initWithString:_rid];
1017 [self->reqElementID release];
1018 self->reqElementID = eid;
1020 - (NSString *)senderID {
1022 return [self->reqElementID elementID];
1024 NSMutableString *eid;
1028 eid = [MutableStrClass stringWithCapacity:(self->reqElementIdCount * 4) + 1];
1029 addStr = [eid methodForSelector:@selector(appendString:)];
1030 for (i = 0; i < self->reqElementIdCount; i++) {
1031 if (i != 0) addStr(eid, @selector(appendString:), @".");
1032 addStr(eid, @selector(appendString:), [self->reqElementId[i] stringValue]);
1038 /* languages for resource lookup (non-WO) */
1040 - (NSArray *)resourceLookupLanguages {
1041 return [self hasSession]
1042 ? [[self session] languages]
1043 : [[self request] browserLanguages];
1047 /* DeprecatedMethodsInWO4 */
1050 if (self->application == nil)
1051 self->application = [WOAppClass application];
1053 if (self->application == nil) {
1054 [self logWithFormat:
1055 @"%s: missing application for context %@",
1056 __PRETTY_FUNCTION__, self];
1059 return self->application;
1062 - (void)setDistributionEnabled:(BOOL)_flag {
1064 [[self session] setDistributionEnabled:_flag];
1066 - (BOOL)isDistributionEnabled {
1068 return [[self session] isDistributionEnabled];
1072 return [self componentActionURL];
1075 - (NSString *)urlSessionPrefix {
1076 NSMutableString *url;
1079 url = [MutableStrClass stringWithCapacity:128];
1081 [url appendString:[[self request] adaptorPrefix]];
1082 [url appendString:@"/"];
1083 tmp = [[self request] applicationName];
1085 tmp ? tmp : [(WOApplication *)[self application] name]];
1088 if ([url length] == 0) {
1089 [self warnWithFormat:@"(%s): could not determine session URL prefix !",
1090 __PRETTY_FUNCTION__];
1097 @end /* WOContext */
1100 @implementation WOComponent(Cursors)
1102 - (void)pushCursor:(id)_obj {
1104 [self logWithFormat:@"enter cursor: %@", _obj];
1106 if (!self->cycleContext)
1107 self->cycleContext = [[NSMutableArray alloc] initWithCapacity:8];
1109 /* add to cursor stack */
1110 [self->cycleContext addObject:(_obj != nil ? _obj : (id)[NSNull null])];
1112 /* set active cursor */
1113 [self setObject:_obj forKey:@"_"];
1117 NSMutableArray *ctxStack;
1120 /* retrieve last context */
1121 old = [[self objectForKey:@"_"] retain];
1122 [self setObject:nil forKey:@"_"];
1124 /* restore old ctx */
1125 if ((ctxStack = self->cycleContext) != nil) {
1128 if ((count = [ctxStack count]) > 0) {
1129 [ctxStack removeObjectAtIndex:(count - 1)];
1135 obj = [ctxStack objectAtIndex:(count - 1)];
1137 if (![obj isNotNull]) obj = nil;
1138 [self setObject:obj forKey:@"_"];
1144 [self warnWithFormat:@"-popCursor called without cycle ctx !"];
1149 [self logWithFormat:@"leave cursor: %@ (restored=%@)",
1150 old, [self cursor]];
1153 return [old autorelease];
1157 NSMutableArray *ctxStack;
1159 // TODO: why do we check for _ODCycleCtx, if we query '_' ?
1161 if ((ctxStack = self->cycleContext) == nil)
1162 /* no cycle context setup for component ... */
1164 if ([ctxStack count] == 0)
1165 /* nothing contained in cycle context ... */
1168 return [self objectForKey:@"_"];
1171 @end /* WOComponent(Cursors) */