2 Copyright (C) 2000-2003 SKYRIX Software AG
4 This file is part of OGo
6 OGo 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 OGo 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 OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23 #include "XmlRpcSaxHandler.h"
24 #include "XmlRpcMethodCall.h"
25 #include "XmlRpcMethodResponse.h"
26 #include "NSObject+XmlRpc.h"
27 #include "XmlRpcValue.h"
30 @implementation XmlRpcSaxHandler
32 The SAX handler used to decode XML-RPC responses and requests. If the
33 parsing finishes successfully, either -methodCall or -methodResponse will
34 return an properly initialized object representing the XML-RPC response.
36 The SAX handler is used by the XmlRpcDecoder class internally, in most
37 cases you shouldn't need to access it directly.
40 static Class ArrayClass = Nil;
41 static Class DictionaryClass = Nil;
42 static BOOL doDebug = NO;
45 if (ArrayClass == Nil) ArrayClass = [NSMutableArray class];
46 if (DictionaryClass == Nil) DictionaryClass = [NSMutableDictionary class];
50 if (doDebug) NSLog(@"%s: begin ...", __PRETTY_FUNCTION__);
51 [self->response release]; self->response = nil;
52 [self->call release]; self->call = nil;
53 [self->methodName release]; self->methodName = nil;
54 [self->params release]; self->params = nil;
56 // for recursive structures (struct, array)
57 [self->valueStack removeAllObjects];
59 [self->className release]; self->className = nil;
61 [self->memberNameStack removeAllObjects];
62 [self->memberValueStack removeAllObjects];
63 [self->timeZone release]; self->timeZone = nil;
64 [self->dateTime release]; self->dateTime = nil;
66 [self->characters setString:@""];
68 self->valueNestingLevel = 0;
69 self->nextCharactersProcessor = NULL;
70 self->invalidCall = NO;
71 [self->tagStack removeAllObjects];
76 [self->characters release];
77 [self->tagStack release];
78 [self->valueStack release];
80 [self->memberNameStack release];
81 [self->memberValueStack release];
88 - (XmlRpcMethodCall *)methodCall {
92 - (XmlRpcMethodResponse *)methodResponse {
93 return self->response;
97 return [self->params lastObject]; // => NSException || XmlRpcValue
102 - (void)_addValueToParas:(id)_value {
103 id topValue = [[self->valueStack lastObject] value];
105 if ([topValue isKindOfClass:ArrayClass])
106 [topValue addObject:_value];
107 else if ([topValue isKindOfClass:DictionaryClass])
108 [self->memberValueStack addObject:_value];
110 [self->params addObject:_value];
115 - (void)startDocument {
116 if (doDebug) NSLog(@"%s: begin ...", __PRETTY_FUNCTION__);
119 if (self->tagStack == nil)
120 self->tagStack = [[NSMutableArray alloc] initWithCapacity:8];
121 if (self->valueStack == nil)
122 self->valueStack = [[NSMutableArray alloc] initWithCapacity:8];
123 if (self->characters == nil)
124 self->characters = [[NSMutableString alloc] initWithCapacity:128];
126 if (doDebug) NSLog(@"%s: done ...", __PRETTY_FUNCTION__);
128 - (void)endDocument {
129 if (doDebug) NSLog(@"%s: begin ...", __PRETTY_FUNCTION__);
131 if ([self->tagStack count] > 0) {
132 self->invalidCall = YES;
133 NSLog(@"Warning(%s): tagStack is not empty (%@)",
138 if (self->call != nil && self->response != nil) {
139 self->invalidCall = YES;
140 NSLog(@"Warning(%s): got methodCall *AND* methodResponse!!! (%@<->%@)",
146 if (self->invalidCall) {
147 if (doDebug) NSLog(@"%s: marked as invalid call!", __PRETTY_FUNCTION__);
148 [self->call release]; self->call = nil;
149 [self->response release]; self->response = nil;
151 if (doDebug) NSLog(@"%s: done ...", __PRETTY_FUNCTION__);
156 - (void)start_name:(id<SaxAttributes>)_attrs {
157 self->nextCharactersProcessor = @selector(_name:length:);
160 self->nextCharactersProcessor = NULL;
162 - (void)_name:(unichar *)_chars length:(int)_len {
164 name = [NSString stringWithCharacters:_chars length:_len];
165 [self->memberNameStack addObject:name];
168 - (void)start_params:(id<SaxAttributes>)_attrs {
170 self->invalidCall = YES;
173 self->params = [[NSMutableArray alloc] initWithCapacity:8];
176 if (self->params == nil)
177 self->invalidCall =YES;
180 - (void)start_value:(id<SaxAttributes>)_attrs {
181 self->valueNestingLevel++;
182 self->nextCharactersProcessor = @selector(_baseValue:length:);
185 self->valueNestingLevel--;
188 - (void)_dateValue:(unichar *)_chars length:(int)_len {
192 self->dateTime = [[NSObject objectWithXmlRpcType:@"dateTime.iso8601"
193 characters:_chars length:_len]
197 - (void)_baseValue:(unichar *)_chars length:(int)_len {
200 if (self->valueNestingLevel == 0) {
201 NSLog(@"%s: invalidCall......... self->valueNestingLevel = 0",
202 __PRETTY_FUNCTION__);
206 value = [NSObject objectWithXmlRpcType:[self->tagStack lastObject]
207 characters:_chars length:_len];
210 value = [NSNull null];
212 value = [[XmlRpcValue alloc] initWithValue:value className:self->className];
214 if (self->params == nil) {
215 NSLog(@"%s: invalidCall......... self->params = nil",
216 __PRETTY_FUNCTION__);
219 [self _addValueToParas:value];
224 - (void)start_i4:(id<SaxAttributes>)_attrs {
225 self->nextCharactersProcessor = @selector(_baseValue:length:);
227 - (void)start_int:(id<SaxAttributes>)_attrs {
228 self->nextCharactersProcessor = @selector(_baseValue:length:);
230 - (void)start_double:(id<SaxAttributes>)_attrs {
231 self->nextCharactersProcessor = @selector(_baseValue:length:);
233 - (void)start_base64:(id<SaxAttributes>)_attrs {
234 self->nextCharactersProcessor = @selector(_baseValue:length:);
236 - (void)start_boolean:(id<SaxAttributes>)_attrs {
237 self->nextCharactersProcessor = @selector(_baseValue:length:);
239 - (void)start_string:(id<SaxAttributes>)_attrs {
240 self->nextCharactersProcessor = @selector(_baseValue:length:);
242 - (void)start_dateTime:(id<SaxAttributes>)_attrs {
246 [self->timeZone release]; self->timeZone = nil;
247 [self->dateTime release]; self->dateTime = nil;
249 tz = ((idx = [_attrs indexOfRawName:@"timeZone"]) != NSNotFound)
250 ? [_attrs valueAtIndex:idx]
254 self->timeZone = [[NSTimeZone timeZoneWithAbbreviation:tz] retain];
256 self->nextCharactersProcessor = @selector(_dateValue:length:);
259 - (void)end_dateTime {
260 if (self->dateTime) {
261 NSCalendarDate *date;
265 if ([self->dateTime respondsToSelector:@selector(setTimeZone:)]) {
266 secFromGMT = [self->timeZone secondsFromGMT];
267 [self->dateTime setTimeZone:self->timeZone];
268 date = [self->dateTime dateByAddingYears:0 months:0 days:0
269 hours:0 minutes:0 seconds:-secFromGMT];
272 NSLog(@"WARNING(%s): cannot set timezone on date: %@",
273 __PRETTY_FUNCTION__, self->dateTime);
274 date = self->dateTime;
277 value = [[XmlRpcValue alloc] initWithValue:date
278 className:@"NSCalendarDate"];
280 [self _addValueToParas:value];
283 [self->timeZone release]; self->timeZone = nil;
284 [self->dateTime release]; self->dateTime = nil;
285 self->nextCharactersProcessor = NULL;
289 - (void)start_array:(id<SaxAttributes>)_attrs {
290 id value = [NSMutableArray arrayWithCapacity:8];
292 value = [[XmlRpcValue alloc] initWithValue:value className:self->className];
294 [self _addValueToParas:value];
295 [self->valueStack addObject:value];
297 self->nextCharactersProcessor = NULL;
302 if ([self->valueStack count] > 0)
303 [self->valueStack removeLastObject];
305 NSLog(@"%s: valueStack should be empty: %@",
311 - (void)start_struct:(id<SaxAttributes>)_attrs {
312 id value = [NSMutableDictionary dictionaryWithCapacity:8];
314 value = [[XmlRpcValue alloc] initWithValue:value className:self->className];
316 [self _addValueToParas:value];
317 [self->valueStack addObject:value];
319 self->nextCharactersProcessor = NULL;
324 if ([self->valueStack count] > 0)
325 [self->valueStack removeLastObject];
327 NSLog(@"%s: valueStack should be empty: %@",
333 - (void)start_member:(id<SaxAttributes>)_attrs {
334 if (![[[self->valueStack lastObject] value] isKindOfClass:DictionaryClass]) {
335 self->invalidCall = YES;
338 if (self->memberNameStack == nil)
339 self->memberNameStack = [[NSMutableArray alloc] initWithCapacity:8];
340 if (self->memberValueStack == nil)
341 self->memberValueStack = [[NSMutableArray alloc] initWithCapacity:8];
343 self->nextCharactersProcessor = NULL;
347 id tmp = [[self->valueStack lastObject] value];
349 if ([self->memberNameStack count] != [self->memberValueStack count]) {
350 NSLog(@"Warning(%s): memberNameStack.count != memberValueStack.count"
353 self->memberNameStack,
354 self->memberValueStack,
356 [self->memberValueStack release]; self->memberValueStack = nil;
357 [self->memberNameStack release]; self->memberNameStack = nil;
358 self->invalidCall = YES;
360 else if ([self->memberNameStack count] == 0) {
361 NSLog(@"Warning(%s): memberNameStack and memberValueStack are empty!",
364 [self->memberValueStack release]; self->memberValueStack = nil;
365 [self->memberNameStack release]; self->memberNameStack = nil;
366 self->invalidCall = YES;
368 else if (![tmp isKindOfClass:DictionaryClass])
369 self->invalidCall = YES;
371 [tmp setObject:[self->memberValueStack lastObject]
372 forKey:[self->memberNameStack lastObject]];
374 [self->memberNameStack removeLastObject];
375 [self->memberValueStack removeLastObject];
379 - (void)start_methodCall:(id<SaxAttributes>)_attrs {
380 /* can't create call here, args unknown !!! */
381 if (self->call != nil) {
383 NSLog(@"%s: method-call already setup!", __PRETTY_FUNCTION__);
384 self->invalidCall = YES;
387 if (doDebug) NSLog(@"%s: ...", __PRETTY_FUNCTION__);
389 - (void)end_methodCall {
390 if (self->call != nil) {
392 NSLog(@"%s: method-call already setup!", __PRETTY_FUNCTION__);
393 self->invalidCall = YES;
397 self->call = [[XmlRpcMethodCall alloc] initWithMethodName:self->methodName
398 parameters:self->params];
401 [self->methodName release]; self->methodName = nil;
402 [self->params release]; self->params = nil;
405 - (void)start_methodResponse:(id<SaxAttributes>)_attrs {
406 if (self->response != nil) {
408 NSLog(@"%s: method-response already setup!", __PRETTY_FUNCTION__);
409 self->invalidCall = YES;
414 - (void)end_methodResponse {
415 if (doDebug) NSLog(@"%s: begin ...", __PRETTY_FUNCTION__);
417 if (self->response != nil) {
419 NSLog(@"%s: method-response already setup!", __PRETTY_FUNCTION__);
420 self->invalidCall = YES;
424 if ([self->params count] > 1) {
426 NSLog(@"%s: has more than one params (%i)!", __PRETTY_FUNCTION__,
427 [self->params count]);
429 self->invalidCall = YES;
432 if (self->invalidCall) {
436 NSLog(@"%s: response marked as invalid!", __PRETTY_FUNCTION__);
438 error = [NSException exceptionWithName:@"error while parsing response"
439 reason:@"error while parsing response"
442 [self->params release];
443 self->params = [[NSMutableArray arrayWithObject:error] retain];
447 [[XmlRpcMethodResponse alloc] initWithResult:[self->params lastObject]];
449 NSLog(@"%s: response: %@", __PRETTY_FUNCTION__, self->response);
452 [self->params release]; self->params = nil;
453 if (doDebug) NSLog(@"%s: done.", __PRETTY_FUNCTION__);
456 - (void)start_methodName:(id<SaxAttributes>)_attrs {
457 if (self->call != nil) {
458 self->invalidCall = YES;
461 self->nextCharactersProcessor = @selector(_methodName:length:);
464 - (void)end_methodName {
465 self->nextCharactersProcessor = NULL;
467 - (void)_methodName:(unichar *)_chars length:(int)_len {
468 [self->methodName release];
469 self->methodName = [[NSString alloc] initWithCharacters:_chars length:_len];
472 - (void)start_fault:(id<SaxAttributes>)_attrs {
474 self->invalidCall = YES;
478 self->params = [[NSMutableArray alloc] initWithCapacity:2];
480 self->nextCharactersProcessor = NULL;
483 if (self->params == nil)
484 self->invalidCall = YES;
486 self->nextCharactersProcessor = NULL;
488 /* fixup result class */
489 if ([self->params count] != 1) {
490 NSLog(@"XML-RPC: incorrect params count (should be 1 for faults) ?: %@",
496 fault = [self->params objectAtIndex:0];
497 if (![fault isException]) {
498 if ([fault isDictionary]) {
499 [fault setClassName:@"NSException"];
506 NSLog(@"XML-RPC: got incorrect fault object (class=%@) ?: %@",
507 [fault className], fault);
508 name = [NSString stringWithFormat:@"XmlRpcFault<%@>",
509 [fault valueForKey:@"faultCode"]];
510 if (name == nil) name = @"GenericXmlRpcFault";
511 reason = [fault valueForKey:@"faultString"];
512 if (reason == nil) reason = name;
514 e = [NSException exceptionWithName:name reason:reason userInfo:nil];
515 [self->params replaceObjectAtIndex:0 withObject:e];
521 /* generic dispatcher */
523 - (void)startElement:(NSString *)_localName
524 namespace:(NSString *)_ns
525 rawName:(NSString *)_rawName
526 attributes:(id<SaxAttributes>)_attrs
532 [self->tagStack addObject:_rawName];
534 tmp = ((idx = [_attrs indexOfRawName:@"NSObjectClass"]) != NSNotFound)
535 ? [_attrs valueAtIndex:idx]
538 [self->className autorelease];
539 self->className = [tmp retain];
541 if (self->invalidCall) return;
543 if ([_rawName isEqualToString:@"dateTime.iso8601"])
544 _rawName = @"dateTime";
546 [self->characters setString:@""];
548 tmp = [NSString stringWithFormat:@"start_%@:",_rawName];
549 if ((sel = NSSelectorFromString(tmp))) {
550 if ([self respondsToSelector:sel])
551 [self performSelector:sel withObject:_attrs];
555 - (void)endElement:(NSString *)_localName
556 namespace:(NSString *)_ns
557 rawName:(NSString *)_rawName
559 unsigned stackDepth, lastIdx;
563 if (self->nextCharactersProcessor != NULL) {
564 void (*m)(id, SEL, unichar *, int);
568 len = [self->characters length];
569 chars = malloc(sizeof(unichar)*len);
570 [self->characters getCharacters:chars];
572 if ((m = (void*)[self methodForSelector:self->nextCharactersProcessor]))
573 m(self, self->nextCharactersProcessor, chars, len);
578 self->nextCharactersProcessor = NULL;
580 if ((stackDepth = [self->tagStack count]) == 0) {
581 self->invalidCall = YES;
584 lastIdx = stackDepth - 1;
585 if (![[self->tagStack objectAtIndex:lastIdx] isEqualToString:_rawName]) {
586 self->invalidCall = YES;
589 [self->tagStack removeObjectAtIndex:lastIdx];
592 if (self->invalidCall) {
596 if ([_rawName isEqualToString:@"dateTime.iso8601"])
597 _rawName = @"dateTime";
599 tmp = [NSString stringWithFormat:@"end_%@", _rawName];
600 if ((sel = NSSelectorFromString(tmp))) {
601 if ([self respondsToSelector:sel])
602 [self performSelector:sel];
606 - (void)characters:(unichar *)_chars length:(int)_len {
608 [self->characters appendString:
609 [NSString stringWithCharacters:_chars length:_len]];
615 - (void)warning:(SaxParseException *)_exception {
616 NSLog(@"XML-RPC warning: %@", _exception);
618 - (void)error:(SaxParseException *)_exception {
619 NSLog(@"XML-RPC error: %@", _exception);
622 @end /* XmlRpcSaxHandler */