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), (int)self);
106 self->ctxId = [[NSString alloc] initWithCString:buf];
108 /* per default close tags in XML style */
109 self->wcFlags.xmlStyleEmptyElements = 1;
110 self->wcFlags.allowEmptyAttributes = 0;
112 self->elementID = [[WOElementID alloc] init];
113 self->awakeComponents = [[NSMutableSet alloc] initWithCapacity:64];
115 self->request = [_request retain];
116 self->response = [[WOResponse responseWithRequest:_request] retain];
122 return [[[self alloc] init] autorelease];
125 return [self initWithRequest:nil];
130 - (void)_addAwakeComponent:(WOComponent *)_component {
131 if (_component == nil)
134 if ([self->awakeComponents containsObject:_component])
137 /* wake up component */
138 if (debugComponentAwake)
139 [self logWithFormat:@"mark component awake: %@", _component];
141 [self->awakeComponents addObject:_component];
144 - (void)_awakeComponent:(WOComponent *)_component {
145 if (_component == nil)
148 if ([self->awakeComponents containsObject:_component])
151 /* wake up component */
152 if (debugComponentAwake)
153 [self logWithFormat:@"awake component: %@", _component];
155 [_component _awakeWithContext:self];
157 [self _addAwakeComponent:_component];
159 if (debugComponentAwake)
160 [self logWithFormat:@"woke up component: %@", _component];
163 - (void)sleepComponents {
165 WOComponent *component;
166 BOOL sendSleepToPage;
168 if (debugComponentAwake) {
169 [self logWithFormat:@"sleep %d components ...",
170 [self->awakeComponents count]];
173 sendSleepToPage = YES;
174 e = [self->awakeComponents objectEnumerator];
175 while ((component = [e nextObject])) {
176 if (debugComponentAwake)
177 [self logWithFormat:@" sleep component: %@", component];
178 [component _sleepWithContext:self];
179 if (component == self->page) sendSleepToPage = NO;
181 if (sendSleepToPage && (self->page != nil)) {
182 if (debugComponentAwake)
183 [self logWithFormat:@" sleep page: %@", self->page];
184 [self->page _sleepWithContext:self];
187 if (debugComponentAwake) {
188 [self logWithFormat:@"done sleep %d components.",
189 [self->awakeComponents count]];
191 [self->awakeComponents removeAllObjects];
194 #if WITH_DEALLOC_OBSERVERS
195 - (void)addDeallocObserver:(id)_observer {
196 if (_observer == NULL) return;
199 if (self->deallocObservers == NULL) {
200 self->deallocObservers = calloc(8, sizeof(id));
201 self->deallocObserverCount = 0;
202 self->deallocObserverCapacity = 8;
206 if (self->deallocObserverCapacity == self->deallocObserverCount) {
207 /* need to increase array */
210 newa = calloc(self->deallocObserverCapacity * 2, sizeof(id));
211 memcpy(newa, self->deallocObservers,
212 sizeof(id) * self->deallocObserverCount);
213 free(self->deallocObservers);
214 self->deallocObservers = newa;
215 self->deallocObserverCapacity *= 2;
219 self->deallocObservers[self->deallocObserverCount] = _observer;
220 self->deallocObserverCount++;
222 - (void)removeDeallocObserver:(id)_observer {
223 /* the observer currently will only grow (this should be OK for WOContext) */
225 if (_observer == NULL) return;
227 for (i = self->deallocObserverCount - 1; i >= 0; i++) {
228 if ((self->deallocObservers[i]) == _observer)
229 self->deallocObservers[i] = NULL;
235 [self sleepComponents];
237 #if WITH_DEALLOC_OBSERVERS
238 if (self->deallocObservers) {
242 printf("%s: dealloc observer capacity: %i\n",
243 __PRETTY_FUNCTION__, self->deallocObserverCapacity);
246 /* GC!! process in reverse order ... */
247 for (i = self->deallocObserverCount - 1; i >= 0; i++)
248 [self->deallocObservers[i] _objectWillDealloc:self];
250 free(self->deallocObservers);
251 self->deallocObservers = NULL;
255 [self->activeUser release];
256 [self->rootURL release];
257 [self->objectPermissionCache release];
258 [self->traversalStack release];
259 [self->clientObject release];
260 [self->objectDispatcher release];
261 [self->soRequestType release];
262 [self->pathInfo release];
264 [[NSNotificationCenter defaultCenter]
265 postNotificationName:@"WOContextWillDeallocate"
268 { /* release component stack */
270 for (i = (self->componentStackCount - 1); i >= 0; i--) {
271 [self->componentStack[i] release]; self->componentStack[i] = nil;
272 [self->contentStack[i] release]; self->contentStack[i] = nil;
276 [self->urlPrefix release];
277 [self->elementID release];
278 [self->reqElementID release];
279 [self->activeFormElement release];
280 [self->page release];
281 [self->awakeComponents release];
282 [self->appURL release];
283 [self->baseURL release];
284 [self->session release];
285 [self->variables release];
286 [self->request release];
287 [self->response release];
288 [self->ctxId release];
294 - (void)setSession:(WOSession *)_session {
295 ASSIGN(self->session, _session);
297 - (void)setNewSession:(WOSession *)_session {
298 [self setSession:_session];
299 self->wcFlags.hasNewSession = 1;
303 /* in WO4 -session creates a new session if none is associated */
305 if (self->session == nil) {
306 [[self application] _initializeSessionInContext:self];
308 if (self->session == nil) {
309 [self logWithFormat:@"%s: missing session for context ..",
310 __PRETTY_FUNCTION__];
314 return self->session;
317 - (NSString *)contextID {
318 NSAssert(self->ctxId, @"context without id !");
320 // in WO4 -contextID returns nil if there is no associated session
321 return self->session ? self->ctxId : nil;
324 IMHO the above isn't true, otherwise session cannot be automagically
327 TODO: well, we might want to generate component URLs which work without
328 a session - at least in theory the ID tree should be stable even
329 without a session (and if proper uids are used for dynamic content).
330 eg this would be quite useful for SOPE.
336 - (WORequest *)request {
337 return self->request;
339 - (WOResponse *)response {
340 return self->response;
344 return (self->session != nil) ? YES : NO;
346 - (BOOL)hasNewSession {
347 if (!self->wcFlags.hasNewSession)
349 return [self hasSession];
352 - (BOOL)savePageRequired {
353 return self->wcFlags.savePageRequired ? YES : NO;
358 - (void)pushCursor:(id)_obj {
359 if (debugCursor == -1) {
360 debugCursor = [[NSUserDefaults standardUserDefaults]
361 boolForKey:@"WODebugCursor"]
365 if (debugCursor) [self logWithFormat:@"enter cursor: %@", _obj];
366 [[self component] pushCursor:_obj];
370 if (debugCursor) [self logWithFormat:@"leave cursor ..."];
371 return [[self component] popCursor];
375 return [(id <WOPageGenerationContext>)[self component] cursor];
381 return (self->componentStackCount > 0)
382 ? self->componentStack[self->componentStackCount - 1]
386 - (void)setPage:(WOComponent *)_page {
387 [_page ensureAwakeInContext:self];
388 ASSIGN(self->page, _page);
394 void WOContext_enterComponent
395 (WOContext *self, WOComponent *_component, WOElement *_content)
397 WOComponent *parent = nil;
399 NSCAssert(_component, @"missing component to enter ...");
403 [self->application logWithFormat:@"enter component %@ (content=%@) ..",
404 [_component name], _content];
407 parent = self->componentStackCount > 0
408 ? self->componentStack[self->componentStackCount - 1]
411 NSCAssert2(self->componentStackCount < NGObjWeb_MAX_COMPONENT_NESTING_DEPTH,
412 @"exceeded maximum component nesting depth (%i):\n%@",
413 NGObjWeb_MAX_COMPONENT_NESTING_DEPTH,
414 [self _componentStack]);
415 self->componentStack[(int)self->componentStackCount] = [_component retain];
416 self->contentStack[(int)self->componentStackCount] = [_content retain];
417 self->componentStackCount++;
419 [self _awakeComponent:_component];
422 if ([_component synchronizesVariablesWithBindings])
423 WOComponent_syncFromParent(_component, parent);
426 void WOContext_leaveComponent(WOContext *self, WOComponent *_component) {
427 WOComponent *parent = nil;
431 parent = (self->componentStackCount > 1)
432 ? self->componentStack[self->componentStackCount - 2]
436 if ([_component synchronizesVariablesWithBindings])
437 WOComponent_syncToParent(_component, parent);
440 PROFILE_CHECKPOINT("after sync");
442 /* remove last object */
443 self->componentStackCount--;
444 NSCAssert(self->componentStackCount >= 0,
445 @"tried to pop component from empty component stack !");
446 [self->componentStack[(int)self->componentStackCount] release];
447 self->componentStack[(int)self->componentStackCount] = nil;
448 [self->contentStack[(int)self->componentStackCount] release];
449 self->contentStack[(int)self->componentStackCount] = nil;
452 [self->application logWithFormat:@"left component %@.", [_component name]];
457 - (void)enterComponent:(WOComponent *)_comp content:(WOElement *)_content {
458 WOContext_enterComponent(self, _comp, _content);
460 - (void)leaveComponent:(WOComponent *)_component {
462 WOContext_leaveComponent(self, _component);
466 - (WOComponent *)parentComponent {
467 return (self->componentStackCount > 1)
468 ? self->componentStack[(int)self->componentStackCount - 2]
472 - (WODynamicElement *)componentContent {
473 return (self->componentStackCount > 0)
474 ? self->contentStack[(int)self->componentStackCount - 1]
478 - (unsigned)componentStackCount {
479 return self->componentStackCount;
481 - (NSArray *)_componentStack {
482 return [NSArray arrayWithObjects:self->componentStack
483 count:self->componentStackCount];
488 - (NSURL *)serverURL {
494 if ((rq = [self request]) == nil) {
495 [self logWithFormat:@"missing request in -baseURL call .."];
499 if ((serverURL = [rq headerForKey:@"x-webobjects-server-url"]) == nil) {
500 if ((host = [rq headerForKey:@"host"]))
501 serverURL = [@"http://" stringByAppendingString:host];
504 // TODO: fix that (host is also broken for example with SOUP)
505 /* sometimes the port is broken in the server URL ... */
506 if ([serverURL hasSuffix:@":0"]) { // bad bad bad
507 if ((host = [rq headerForKey:@"host"])) {
509 scheme = [serverURL hasPrefix:@"https://"] ? @"https://" : @"http://";
510 serverURL = [scheme stringByAppendingString:host];
515 if ([serverURL length] == 0) {
516 [self errorWithFormat:@"could not find x-webobjects-server-url header !"];
520 if ((url = [NSURL URLWithString:serverURL]) == nil) {
521 [self logWithFormat:@"could not construct NSURL from string '%@'",
533 return self->baseURL;
535 if ((rq = [self request]) == nil) {
536 [self logWithFormat:@"missing request in -baseURL call .."];
540 serverURL = [self serverURL];
542 [[NSURL URLWithString:[rq uri] relativeToURL:serverURL] retain];
544 if (self->baseURL == nil) {
546 @"could not construct NSURL for uri '%@' and base '%@' ...",
547 [rq uri], serverURL];
549 return self->baseURL;
552 - (NSURL *)applicationURL {
555 if (self->appURL != nil)
558 // TODO: we should ensure that the suffix (.woa) is in the URL
560 s = [self->request adaptorPrefix];
561 if ([s length] > 0) {
562 s = [[[s stringByAppendingString:@"/"]
563 stringByAppendingString:[self->request applicationName]]
564 stringByAppendingString:@"/"];
567 s = [[self->request applicationName] stringByAppendingString:@"/"];
570 [[NSURL URLWithString:s relativeToURL:[self serverURL]] retain];
573 - (NSURL *)urlForKey:(NSString *)_key {
574 _key = [_key stringByAppendingString:@"/"];
575 return [NSURL URLWithString:_key relativeToURL:[self applicationURL]];
580 - (void)setInForm:(BOOL)_form {
581 self->wcFlags.inForm = _form ? 1 : 0;
584 return self->wcFlags.inForm ? YES : NO;
587 - (void)addActiveFormElement:(WOElement *)_formElement {
588 if (self->activeFormElement) {
589 [[self component] debugWithFormat:@"active form element already set !"];
593 ASSIGN(self->activeFormElement, _formElement);
594 [self setRequestSenderID:[self elementID]];
596 - (WOElement *)activeFormElement {
597 return self->activeFormElement;
600 /* context variables (transient) */
602 - (void)setObject:(id)_obj forKey:(NSString *)_key {
603 if (self->variables == nil) {
605 [[NSMutableDictionary allocWithZone:[self zone]]
606 initWithCapacity:16];
610 [self->variables setObject:_obj forKey:_key];
612 [self->variables removeObjectForKey:_key];
614 - (id)objectForKey:(NSString *)_key {
615 return [self->variables objectForKey:_key];
617 - (void)removeObjectForKey:(NSString *)_key {
618 [self->variables removeObjectForKey:_key];
621 - (NSDictionary *)variableDictionary {
622 return self->variables;
625 - (void)takeValue:(id)_value forKey:(NSString *)_key {
626 if (WOSetKVCValueUsingMethod(self, _key, _value))
629 else if (WOGetKVCGetMethod(self, _key) == NULL) {
631 _value = [NSNull null];
633 if (self->variables == nil) {
635 [[NSMutableDictionary allocWithZone:[self zone]]
636 initWithCapacity:16];
638 [self->variables setObject:_value forKey:_key];
642 // only a 'get' method is defined for _key !
643 [self handleTakeValue:_value forUnboundKey:_key];
646 - (id)valueForKey:(NSString *)_key {
649 if ((value = WOGetKVCValueUsingMethod(self, _key)))
651 value = [self->variables objectForKey:_key];
657 - (id)copyWithZone:(NSZone *)_zone {
658 return [self retain];
663 - (NSString *)description {
665 WOApplication *app = [self application];
667 if ([self hasSession])
668 sid = [[self session] sessionID];
670 return [NSString stringWithFormat:
671 @"<0x%08X[%@]: %@ app=%@ sn=%@ eid=%@ rqeid=%@>",
672 (unsigned)self, NSStringFromClass([self class]),
682 - (NSString *)elementID {
683 return [self->elementID elementID];
685 - (void)appendElementIDComponent:(NSString *)_eid {
686 [self->elementID appendElementIDComponent:_eid];
688 - (void)appendZeroElementIDComponent {
689 [self->elementID appendZeroElementIDComponent];
691 - (void)deleteAllElementIDComponents {
692 [self->elementID deleteAllElementIDComponents];
694 - (void)deleteLastElementIDComponent {
695 [self->elementID deleteLastElementIDComponent];
697 - (void)incrementLastElementIDComponent {
698 [self->elementID incrementLastElementIDComponent];
700 - (void)appendIntElementIDComponent:(int)_eid {
701 [self->elementID appendIntElementIDComponent:_eid];
704 /* the following can be later moved to WOElementID */
706 - (id)currentElementID {
707 return [self->reqElementID currentElementID];
709 - (id)consumeElementID {
710 return [self->reqElementID consumeElementID];
715 - (void)_generateCompleteURLs {
716 /* described in Apple TIL article 70101 */
719 - (void)setQueryPathSeparator:(NSString *)_sp {
720 ASSIGNCOPY(self->qpJoin, _sp);
722 - (NSString *)queryPathSeparator {
726 - (void)setGenerateXMLStyleEmptyElements:(BOOL)_flag {
727 self->wcFlags.xmlStyleEmptyElements = _flag ? 1 : 0;
729 - (BOOL)generateXMLStyleEmptyElements {
730 return self->wcFlags.xmlStyleEmptyElements ? YES : NO;
733 - (void)setGenerateEmptyAttributes:(BOOL)_flag {
734 self->wcFlags.allowEmptyAttributes = _flag ? 1 : 0;
736 - (BOOL)generateEmptyAttributes {
737 return self->wcFlags.allowEmptyAttributes ? YES : NO;
740 - (NSString *)queryStringFromDictionary:(NSDictionary *)_queryDict {
746 qs = [MutableStrClass stringWithCapacity:256];
747 keys = [_queryDict keyEnumerator];
748 for (isFirst = YES; (key = [keys nextObject]) != nil; ) {
751 value = [_queryDict objectForKey:key];
753 /* check for multi-value parameter */
755 if ([value isKindOfClass:[NSArray class]]) {
759 for (i = 0, count = [a count]; i < count; i++) {
760 value = [a objectAtIndex:i];
762 if (isFirst) isFirst = NO;
763 else [qs appendString:self->qpJoin];
765 // TODO: code duplication ...
766 value = ![value isNotNull] ? nil : [value stringValue];
767 key = [key stringByEscapingURL];
768 value = [value stringByEscapingURL];
770 [qs appendString:key];
772 [qs appendString:@"="];
773 [qs appendString:value];
779 /* regular, single-value parameter */
781 if (isFirst) isFirst = NO;
782 else [qs appendString:self->qpJoin];
784 value = ![value isNotNull] ? nil : [value stringValue];
785 key = [key stringByEscapingURL];
786 value = [value stringByEscapingURL];
788 [qs appendString:key];
790 [qs appendString:@"="];
791 [qs appendString:value];
798 - (NSString *)directActionURLForActionNamed:(NSString *)_actionName
799 queryDictionary:(NSDictionary *)_queryDict
801 NSMutableString *url;
804 url = [MutableStrClass stringWithCapacity:256];
807 [url appendString:@"/"];
809 [url appendString:_actionName];
811 /* add query parameters */
813 qs = [self queryStringFromDictionary:_queryDict];
815 return [self urlWithRequestHandlerKey:
816 [WOAppClass directActionRequestHandlerKey]
817 path:url queryString:qs];
820 - (NSString *)componentActionURL {
821 // TODO: add a -cComponentActionURL
822 // (without NSString for use with appendContentCString:)
824 // 26% -urlWithRequestHandler...
825 // 21% -elementID (was 40% !! :-)
826 // ~20% mutable string ops
829 This makes the request handler save the page in the session at the
830 end of the request (only necessary if the page generates URLs which
833 self->wcFlags.savePageRequired = 1;
836 // TODO: who uses that? Its not enabled per default
837 // TODO: what does this do?
841 qs = [MutableStrClass stringWithCapacity:64];
843 [qs appendString:WORequestValueSenderID];
844 [qs appendString:@"="];
845 [qs appendString:[self elementID]];
846 [qs appendString:self->qpJoin];
847 [qs appendString:WORequestValueSessionID];
848 [qs appendString:@"="];
849 [qs appendString:[[self session] sessionID]];
850 [qs appendString:self->qpJoin];
851 [qs appendString:WORequestValueContextID];
852 [qs appendString:@"="];
853 [qs appendString:[self contextID]];
855 p = [[self page] componentActionURLForContext:self];
858 if ([p hasPrefix:@"/"]) p = [p substringFromIndex:1];
861 return [self urlWithRequestHandlerKey:
862 [WOAppClass componentRequestHandlerKey]
867 /* old style URLs ... */
868 static NSMutableString *url = nil; // THREAD
869 static IMP addStr = NULL;
873 coRqhKey = [WOAppClass componentRequestHandlerKey];
876 Optimization: use relative URL if the request already was a component
877 action (with a valid session)
879 if (!self->wcFlags.hasNewSession) {
880 if ([[self->request requestHandlerKey] isEqualToString:coRqhKey])
881 return [self->elementID elementID];
885 url = [[MutableStrClass alloc] initWithCapacity:256];
886 addStr = [url methodForSelector:@selector(appendString:)];
887 addStr(url, @selector(appendString:), @"/");
890 [url setString:@"/"];
893 Note: component actions *always* require sessions to be able to locate
894 the request component !
896 addStr(url, @selector(appendString:), [[self session] sessionID]);
897 addStr(url, @selector(appendString:), @"/");
898 addStr(url, @selector(appendString:), [self->elementID elementID]);
900 s = [self urlWithRequestHandlerKey:coRqhKey
901 path:url queryString:nil];
906 - (NSString *)urlWithRequestHandlerKey:(NSString *)_key
907 path:(NSString *)_path
908 queryString:(NSString *)_query
910 if (testNSURLs) { /* use NSURLs for processing */
913 if ([_path hasPrefix:@"/"]) {
915 [self warnWithFormat:@"got absolute path '%@'", _path];
917 _path = [_path substringFromIndex:1];
920 if (_key == nil) _key = [WOAppClass componentRequestHandlerKey];
921 rqUrl = [self urlForKey:_key];
923 if ([_query length] > 0) {
926 s = [_path mutableCopy];
927 [s appendString:@"?"];
928 [s appendString:_query];
929 rqUrl = [NSURL URLWithString:s relativeToURL:rqUrl];
933 rqUrl = [NSURL URLWithString:_path relativeToURL:rqUrl];
935 //[self logWithFormat:@"constructed component URL: %@", rqUrl];
937 return [rqUrl stringValueRelativeToURL:[self baseURL]];
940 NSMutableString *url;
944 if (_key == nil) _key = [WOAppClass componentRequestHandlerKey];
946 url = [MutableStrClass stringWithCapacity:256];
947 addStr = [url methodForSelector:@selector(appendString:)];
950 if (self->urlPrefix == nil) {
952 if ((tmp = [self->request headerForKey:@"x-webobjects-server-url"])) {
953 if ([tmp hasSuffix:@":0"] && [tmp length] > 2) // TODO: BAD BAD BAD
954 tmp = [tmp substringToIndex:([tmp length] - 2)];
955 addStr(url, @selector(appendString:), tmp);
957 else if ((tmp = [self->request headerForKey:@"host"])) {
958 addStr(url, @selector(appendString:), @"http://");
959 addStr(url, @selector(appendString:), tmp);
963 addStr(url, @selector(appendString:), [self->request adaptorPrefix]);
964 addStr(url, @selector(appendString:), @"/");
965 tmp = [[self request] applicationName];
966 if ([tmp length] == 0)
967 tmp = [(WOApplication *)[self application] name];
968 if ([tmp length] > 0) {
969 addStr(url, @selector(appendString:), tmp);
970 if (WOApplicationSuffix)
971 addStr(url, @selector(appendString:), WOApplicationSuffix);
972 addStr(url, @selector(appendString:), @"/");
976 self->urlPrefix = [url copy];
977 if (debugOn) [self debugWithFormat:@"URL prefix: '%@'", self->urlPrefix];
980 /* prefix is cached :-) */
981 addStr(url, @selector(appendString:), self->urlPrefix);
985 addStr(url, @selector(appendString:), _key);
987 addStr(url, @selector(appendString:), _path);
988 if ([_query length] > 0) {
989 addStr(url, @selector(appendString:), @"?");
990 addStr(url, @selector(appendString:), _query);
995 - (NSString *)completeURLWithRequestHandlerKey:(NSString *)_key
996 path:(NSString *)_path queryString:(NSString *)_query
997 isSecure:(BOOL)_isSecure port:(int)_port
999 NSMutableString *url = [MutableStrClass stringWithCapacity:256];
1000 [url appendString:_isSecure ? @"https://" : @"http://"];
1001 [url appendString:[[self request] headerForKey:@"host"]];
1003 if (!(_isSecure && _port == 443) && !(!_isSecure && _port == 80))
1004 [url appendFormat:@":%i", _port];
1006 [url appendString:[self urlWithRequestHandlerKey:_key
1008 queryString:_query]];
1012 - (void)setRequestSenderID:(NSString *)_rid {
1015 eid = [[WOElementID alloc] initWithString:_rid];
1016 [self->reqElementID release];
1017 self->reqElementID = eid;
1019 - (NSString *)senderID {
1021 return [self->reqElementID elementID];
1023 NSMutableString *eid;
1027 eid = [MutableStrClass stringWithCapacity:(self->reqElementIdCount * 4) + 1];
1028 addStr = [eid methodForSelector:@selector(appendString:)];
1029 for (i = 0; i < self->reqElementIdCount; i++) {
1030 if (i != 0) addStr(eid, @selector(appendString:), @".");
1031 addStr(eid, @selector(appendString:), [self->reqElementId[i] stringValue]);
1037 /* languages for resource lookup (non-WO) */
1039 - (NSArray *)resourceLookupLanguages {
1040 return [self hasSession]
1041 ? [[self session] languages]
1042 : [[self request] browserLanguages];
1046 /* DeprecatedMethodsInWO4 */
1049 if (self->application == nil)
1050 self->application = [WOAppClass application];
1052 if (self->application == nil) {
1053 [self logWithFormat:
1054 @"%s: missing application for context %@",
1055 __PRETTY_FUNCTION__, self];
1058 return self->application;
1061 - (void)setDistributionEnabled:(BOOL)_flag {
1063 [[self session] setDistributionEnabled:_flag];
1065 - (BOOL)isDistributionEnabled {
1067 return [[self session] isDistributionEnabled];
1071 return [self componentActionURL];
1074 - (NSString *)urlSessionPrefix {
1075 NSMutableString *url;
1078 url = [MutableStrClass stringWithCapacity:128];
1080 [url appendString:[[self request] adaptorPrefix]];
1081 [url appendString:@"/"];
1082 tmp = [[self request] applicationName];
1084 tmp ? tmp : [(WOApplication *)[self application] name]];
1087 if ([url length] == 0) {
1088 [self warnWithFormat:@"(%s): could not determine session URL prefix !",
1089 __PRETTY_FUNCTION__];
1096 @end /* WOContext */
1099 @implementation WOComponent(Cursors)
1101 - (void)pushCursor:(id)_obj {
1103 [self logWithFormat:@"enter cursor: %@", _obj];
1105 if (!self->cycleContext)
1106 self->cycleContext = [[NSMutableArray alloc] initWithCapacity:8];
1108 /* add to cursor stack */
1109 [self->cycleContext addObject:(_obj ? _obj : [NSNull null])];
1111 /* set active cursor */
1112 [self setObject:_obj forKey:@"_"];
1116 NSMutableArray *ctxStack;
1119 /* retrieve last context */
1120 old = [[self objectForKey:@"_"] retain];
1121 [self setObject:nil forKey:@"_"];
1123 /* restore old ctx */
1124 if ((ctxStack = self->cycleContext) != nil) {
1127 if ((count = [ctxStack count]) > 0) {
1128 [ctxStack removeObjectAtIndex:(count - 1)];
1134 obj = [ctxStack objectAtIndex:(count - 1)];
1136 if (![obj isNotNull]) obj = nil;
1137 [self setObject:obj forKey:@"_"];
1143 [self warnWithFormat:@"-popCursor called without cycle ctx !"];
1148 [self logWithFormat:@"leave cursor: %@ (restored=%@)",
1149 old, [self cursor]];
1152 return [old autorelease];
1156 NSMutableArray *ctxStack;
1158 // TODO: why do we check for _ODCycleCtx, if we query '_' ?
1160 if ((ctxStack = self->cycleContext) == nil)
1161 /* no cycle context setup for component ... */
1163 if ([ctxStack count] == 0)
1164 /* nothing contained in cycle context ... */
1167 return [self objectForKey:@"_"];
1170 @end /* WOComponent(Cursors) */