2 Copyright (C) 2002-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 "SoActionInvocation.h"
23 #include "SoClassSecurityInfo.h"
24 #include "SoProduct.h"
25 #include "WOContext+SoObjects.h"
26 #include <NGObjWeb/WOApplication.h>
27 #include <NGObjWeb/WODirectAction.h>
28 #include <NGObjWeb/WOResponse.h>
32 @implementation SoActionInvocation
34 static int debugOn = 0;
37 static BOOL didInit = NO;
39 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
42 debugOn = [ud boolForKey:@"SoPageInvocationDebugEnabled"] ? 1 : 0;
46 - (id)initWithActionClassName:(NSString *)_cn actionName:(NSString *)_action {
47 if ((self = [super init])) {
48 self->actionClassName = [_cn copy];
49 self->actionName = [_action copy];
53 - (id)initWithActionClassName:(NSString *)_cn {
54 return [self initWithActionClassName:_cn actionName:nil];
57 return [self initWithActionClassName:nil actionName:nil];
61 [self->argumentSpecifications release];
62 [self->methodObject release];
63 [self->object release];
64 [self->actionClassName release];
65 [self->actionName release];
71 - (NSString *)defaultActionClassName {
72 return @"DirectAction";
74 - (NSString *)actionClassName {
75 return [self->actionClassName isNotNull]
76 ? self->actionClassName
77 : [self defaultActionClassName];
79 - (NSString *)actionName {
80 return self->actionName;
83 - (void)setArgumentSpecifications:(NSDictionary *)_specs {
84 ASSIGNCOPY(self->argumentSpecifications, _specs);
86 - (NSDictionary *)argumentSpecifications {
87 return self->argumentSpecifications;
90 /* argument processing */
92 - (NSDictionary *)extractSOAPArgumentsFromContext:(id)_ctx
93 specification:(id)_spec
96 spec is supposed to be a dictionary with the KVC keys as the
97 keys and DOM query pathes as the values.
99 NSMutableDictionary *args;
104 // TODO: I guess that should be improved a bit in the dispatcher
105 if ((soapEnvelope = [_ctx valueForKey:@"SOAPEnvelope"]) == nil) {
106 // TODO: generate some kind of fault? (NSException?)
107 [self errorWithFormat:@"no SOAP envelope available in context!"];
111 /* for each key argument we have a query path */
113 args = [NSMutableDictionary dictionaryWithCapacity:8];
114 keys = [_spec keyEnumerator];
115 while ((key = [keys nextObject])) {
119 qppath = [_spec valueForKey:key];
120 value = [qppath isNotNull] ? [soapEnvelope lookupQueryPath:qppath] : nil;
122 [args setObject:(value != nil ? value : [NSNull null]) forKey:key];
127 - (NSDictionary *)extractArgumentsFromContext:(id)_ctx
128 forRequestType:(NSString *)_type
129 specification:(id)_spec
131 if ([_type isEqualToString:@"SOAP"])
132 return [self extractSOAPArgumentsFromContext:_ctx specification:_spec];
134 [self errorWithFormat:
135 @"cannot extract parameters for request type: '%@'", _type];
139 /* page construction */
141 - (id)instantiateMethodInContext:(id)_ctx {
146 [self debugWithFormat:@"instantiate method: %@", self->methodObject];
149 [self debugWithFormat:
150 @"Note: got no explicit context for method instantiation, using "
151 @"application context."];
152 _ctx = [[WOApplication application] context];
157 if ((clazz = NSClassFromString([self actionClassName])) == Nil) {
158 [self errorWithFormat:@"did not find action class: %@",
159 [self actionClassName]];
165 if ([clazz instancesRespondToSelector:@selector(initWithContext:)])
166 lMethod = [[clazz alloc] initWithContext:_ctx];
167 else if ([clazz instancesRespondToSelector:@selector(initWithRequest:)]) {
168 lMethod = [[clazz alloc] initWithRequest:
169 [(id<WOPageGenerationContext>)_ctx request]];
172 lMethod = [[clazz alloc] init];
174 if (debugOn) [self debugWithFormat:@" page: %@", lMethod];
188 - (void)_prepareContext:(id)_ctx withMethodObject:(id)_method {
189 /* for subclasses (set page in context in page invocation) */
191 - (void)_prepareMethod:(id)_method inContext:(id)_ctx {
192 /* for subclasses (triggers takeValues phase in page invocation) */
195 - (void)_applyArgumentsOnMethod:(id)_method inContext:(id)_ctx {
196 NSDictionary *argspec;
199 argspec = [self->argumentSpecifications objectForKey:[_ctx soRequestType]];
203 args = [self extractArgumentsFromContext:_ctx
204 forRequestType:[_ctx soRequestType]
205 specification:argspec];
206 if (debugOn) [self debugWithFormat:@"extracted args %@", args];
208 if (args != nil) [_method takeValuesFromDictionary:args];
211 - (void)_applyPositionalArguments:(NSArray *)_args onMethod:(id)_method
215 unsigned i, argCount, infoCount;
217 info = [self->argumentSpecifications objectForKey:@"positionalKeys"];
218 infoCount = [info count];
219 argCount = [_args count];
220 if ((info == nil) && (argCount > 0)) {
221 [self warnWithFormat:
222 @"found no argument specification for positional keys!"];
226 /* copy available arguments to key */
228 for (i = 0; i < argCount; i++) {
229 if (i >= infoCount) {
230 [self warnWithFormat:
231 @"could not apply argument %d (no key info)", (i + 1)];
235 [_method takeValue:[_args objectAtIndex:i] forKey:[info objectAtIndex:i]];
238 /* fill up missing arguments */
240 for (i = argCount; i < infoCount; i++)
241 [_method takeValue:nil forKey:[info objectAtIndex:i]];
244 /* calling the method */
246 - (id)callOnObject:(id)_client
247 withPositionalParametersWhenNotNil:(NSArray *)_positionalArgs
250 /* method used for both, positional and key arguments */
253 if (self->object != _client) {
255 return [[self bindToObject:_client inContext:_ctx]
256 callOnObject:_client inContext:_ctx];
259 if ((method = self->methodObject) == nil)
260 method = [self instantiateMethodInContext:_ctx];
263 [self logWithFormat:@"found no method named '%@' for call !",
264 [self actionClassName]];
268 /* make page the "request" page */
270 [self _prepareContext:_ctx withMethodObject:method];
272 /* set client object in page */
274 if ([method respondsToSelector:@selector(setClientObject:)])
275 [method setClientObject:_client];
277 if ([_positionalArgs isNotNull]) {
278 [self _applyPositionalArguments:_positionalArgs onMethod:method
282 /* TODO: what should be done first?, take values or args? */
283 [self _prepareMethod:method inContext:_ctx];
284 [self _applyArgumentsOnMethod:method inContext:_ctx];
289 if (self->actionName) {
291 [self debugWithFormat:@" performing action %@ on page: %@",
292 self->actionName, method];
294 return [method performActionNamed:self->actionName];
298 [self debugWithFormat:@" performing default action on page: %@",
301 return [method defaultAction];
305 - (id)callOnObject:(id)_client inContext:(id)_ctx {
306 return [self callOnObject:_client
307 withPositionalParametersWhenNotNil:nil /* not positional */
310 - (id)callOnObject:(id)_client
311 withPositionalParameters:(NSArray *)_args
314 if (_args == nil) _args = [NSArray array];
315 return [self callOnObject:_client
316 withPositionalParametersWhenNotNil:_args
323 return self->object != nil ? YES : NO;
326 - (id)bindToObject:(id)_object inContext:(id)_ctx {
327 SoActionInvocation *inv;
329 if (_object == nil) return nil;
331 // TODO: clean up this section, a bit hackish
332 inv = [[[self class] alloc] initWithActionClassName:self->actionClassName];
333 inv = [inv autorelease];
335 inv->object = [_object retain];
336 inv->actionName = [self->actionName copy];
337 inv->argumentSpecifications = [self->argumentSpecifications copy];
339 inv->methodObject = [[inv instantiateMethodInContext:_ctx] retain];
340 if (inv->methodObject == nil) {
341 [self errorWithFormat:@"did not find method '%@'", [self actionClassName]];
348 /* delivering as content (can happen in DAV !) */
350 - (void)appendToResponse:(WOResponse *)_r inContext:(WOContext *)_ctx {
351 [_r appendContentString:@"native action method: "];
352 [_r appendContentHTMLString:[self description]];
355 /* key/value coding */
357 - (id)valueForUndefinedKey:(NSString *)_key {
358 if (debugOn) [self debugWithFormat:@"return nil for KVC key: '%@'", _key];
364 - (void)appendAttributesToDescription:(NSMutableString *)ms {
367 if ((tmp = [self actionClassName])) [ms appendFormat:@" class=%@", tmp];
368 if (self->actionName) [ms appendFormat:@" action=%@", self->actionName];
370 if (self->object) [ms appendString:@" bound"];
371 if (self->methodObject) [ms appendString:@" instantiated"];
373 if ([self->argumentSpecifications count] > 0) {
376 tmp = [self->argumentSpecifications allKeys];
377 tmp = [tmp componentsJoinedByString:@","];
378 [ms appendFormat:@" arg-handlers=%@",tmp];
381 - (NSString *)description {
384 ms = [NSMutableString stringWithCapacity:64];
385 [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
386 [self appendAttributesToDescription:ms];
387 [ms appendString:@">"];
393 - (NSString *)loggingPrefix {
394 return [NSString stringWithFormat:@"[so-action 0x%08X %@]",
395 self, self->actionClassName];
397 - (BOOL)isDebuggingEnabled {
398 return debugOn ? YES : NO;
401 @end /* SoActionInvocation */