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 "XmlRpcSaxHandler.h"
23 #include "XmlRpcMethodCall.h"
24 #include "XmlRpcMethodResponse.h"
25 #include "NSObject+XmlRpc.h"
26 #include "XmlRpcValue.h"
29 @implementation XmlRpcSaxHandler
31 The SAX handler used to decode XML-RPC responses and requests. If the
32 parsing finishes successfully, either -methodCall or -methodResponse will
33 return an properly initialized object representing the XML-RPC response.
35 The SAX handler is used by the XmlRpcDecoder class internally, in most
36 cases you shouldn't need to access it directly.
39 static Class ArrayClass = Nil;
40 static Class DictionaryClass = Nil;
41 static BOOL doDebug = NO;
44 if (ArrayClass == Nil) ArrayClass = [NSMutableArray class];
45 if (DictionaryClass == Nil) DictionaryClass = [NSMutableDictionary class];
49 if (doDebug) NSLog(@"%s: begin ...", __PRETTY_FUNCTION__);
50 [self->response release]; self->response = nil;
51 [self->call release]; self->call = nil;
52 [self->methodName release]; self->methodName = nil;
53 [self->params release]; self->params = nil;
55 // for recursive structures (struct, array)
56 [self->valueStack removeAllObjects];
58 [self->className release]; self->className = nil;
60 [self->memberNameStack removeAllObjects];
61 [self->memberValueStack removeAllObjects];
62 [self->timeZone release]; self->timeZone = nil;
63 [self->dateTime release]; self->dateTime = nil;
65 [self->characters setString:@""];
67 self->valueNestingLevel = 0;
68 self->nextCharactersProcessor = NULL;
69 self->invalidCall = NO;
70 [self->tagStack removeAllObjects];
75 [self->characters release];
76 [self->tagStack release];
77 [self->valueStack release];
79 [self->memberNameStack release];
80 [self->memberValueStack release];
87 - (XmlRpcMethodCall *)methodCall {
91 - (XmlRpcMethodResponse *)methodResponse {
92 return self->response;
96 return [self->params lastObject]; // => NSException || XmlRpcValue
101 - (void)_addValueToParas:(id)_value {
102 id topValue = [(XmlRpcValue *)[self->valueStack lastObject] value];
104 if ([topValue isKindOfClass:ArrayClass])
105 [topValue addObject:_value];
106 else if ([topValue isKindOfClass:DictionaryClass])
107 [self->memberValueStack addObject:_value];
109 [self->params addObject:_value];
114 - (void)startDocument {
115 if (doDebug) NSLog(@"%s: begin ...", __PRETTY_FUNCTION__);
118 if (self->tagStack == nil)
119 self->tagStack = [[NSMutableArray alloc] initWithCapacity:8];
120 if (self->valueStack == nil)
121 self->valueStack = [[NSMutableArray alloc] initWithCapacity:8];
122 if (self->characters == nil)
123 self->characters = [[NSMutableString alloc] initWithCapacity:128];
125 if (doDebug) NSLog(@"%s: done ...", __PRETTY_FUNCTION__);
127 - (void)endDocument {
128 if (doDebug) NSLog(@"%s: begin ...", __PRETTY_FUNCTION__);
130 if ([self->tagStack count] > 0) {
131 self->invalidCall = YES;
132 NSLog(@"Warning(%s): tagStack is not empty (%@)",
137 if (self->call != nil && self->response != nil) {
138 self->invalidCall = YES;
139 NSLog(@"Warning(%s): got methodCall *AND* methodResponse!!! (%@<->%@)",
145 if (self->invalidCall) {
146 if (doDebug) NSLog(@"%s: marked as invalid call!", __PRETTY_FUNCTION__);
147 [self->call release]; self->call = nil;
148 [self->response release]; self->response = nil;
150 if (doDebug) NSLog(@"%s: done ...", __PRETTY_FUNCTION__);
155 - (void)start_name:(id<SaxAttributes>)_attrs {
156 self->nextCharactersProcessor = @selector(_name:length:);
159 self->nextCharactersProcessor = NULL;
161 - (void)_name:(unichar *)_chars length:(int)_len {
163 name = [NSString stringWithCharacters:_chars length:_len];
164 [self->memberNameStack addObject:name];
167 - (void)start_params:(id<SaxAttributes>)_attrs {
169 self->invalidCall = YES;
172 self->params = [[NSMutableArray alloc] initWithCapacity:8];
175 if (self->params == nil)
176 self->invalidCall =YES;
179 - (void)start_value:(id<SaxAttributes>)_attrs {
180 self->valueNestingLevel++;
181 self->nextCharactersProcessor = @selector(_baseValue:length:);
184 self->valueNestingLevel--;
187 - (void)_dateValue:(unichar *)_chars length:(int)_len {
191 self->dateTime = [[NSObject objectWithXmlRpcType:@"dateTime.iso8601"
192 characters:_chars length:_len]
196 - (void)_baseValue:(unichar *)_chars length:(int)_len {
199 if (self->valueNestingLevel == 0) {
200 NSLog(@"%s: invalidCall......... self->valueNestingLevel = 0",
201 __PRETTY_FUNCTION__);
205 value = [NSObject objectWithXmlRpcType:[self->tagStack lastObject]
206 characters:_chars length:_len];
209 value = [NSNull null];
211 value = [[XmlRpcValue alloc] initWithValue:value className:self->className];
213 if (self->params == nil) {
214 NSLog(@"%s: invalidCall......... self->params = nil",
215 __PRETTY_FUNCTION__);
218 [self _addValueToParas:value];
223 - (void)start_i4:(id<SaxAttributes>)_attrs {
224 self->nextCharactersProcessor = @selector(_baseValue:length:);
226 - (void)start_int:(id<SaxAttributes>)_attrs {
227 self->nextCharactersProcessor = @selector(_baseValue:length:);
229 - (void)start_double:(id<SaxAttributes>)_attrs {
230 self->nextCharactersProcessor = @selector(_baseValue:length:);
232 - (void)start_base64:(id<SaxAttributes>)_attrs {
233 self->nextCharactersProcessor = @selector(_baseValue:length:);
235 - (void)start_boolean:(id<SaxAttributes>)_attrs {
236 self->nextCharactersProcessor = @selector(_baseValue:length:);
238 - (void)start_string:(id<SaxAttributes>)_attrs {
239 self->nextCharactersProcessor = @selector(_baseValue:length:);
241 - (void)start_dateTime:(id<SaxAttributes>)_attrs {
245 [self->timeZone release]; self->timeZone = nil;
246 [self->dateTime release]; self->dateTime = nil;
248 tz = ((idx = [_attrs indexOfRawName:@"timeZone"]) != NSNotFound)
249 ? (id)[_attrs valueAtIndex:idx]
253 self->timeZone = [[NSTimeZone timeZoneWithAbbreviation:tz] retain];
255 self->nextCharactersProcessor = @selector(_dateValue:length:);
258 - (void)end_dateTime {
259 if (self->dateTime) {
260 NSCalendarDate *date;
264 if ([self->dateTime respondsToSelector:@selector(setTimeZone:)]) {
265 secFromGMT = [self->timeZone secondsFromGMT];
266 [self->dateTime setTimeZone:self->timeZone];
267 date = [self->dateTime dateByAddingYears:0 months:0 days:0
268 hours:0 minutes:0 seconds:-secFromGMT];
271 NSLog(@"WARNING(%s): cannot set timezone on date: %@",
272 __PRETTY_FUNCTION__, self->dateTime);
273 date = self->dateTime;
276 value = [[XmlRpcValue alloc] initWithValue:date
277 className:@"NSCalendarDate"];
279 [self _addValueToParas:value];
282 [self->timeZone release]; self->timeZone = nil;
283 [self->dateTime release]; self->dateTime = nil;
284 self->nextCharactersProcessor = NULL;
288 - (void)start_array:(id<SaxAttributes>)_attrs {
289 id value = [NSMutableArray arrayWithCapacity:8];
291 value = [[XmlRpcValue alloc] initWithValue:value className:self->className];
293 [self _addValueToParas:value];
294 [self->valueStack addObject:value];
296 self->nextCharactersProcessor = NULL;
301 if ([self->valueStack count] > 0)
302 [self->valueStack removeLastObject];
304 NSLog(@"%s: valueStack should be empty: %@",
310 - (void)start_struct:(id<SaxAttributes>)_attrs {
311 id value = [NSMutableDictionary dictionaryWithCapacity:8];
313 value = [[XmlRpcValue alloc] initWithValue:value className:self->className];
315 [self _addValueToParas:value];
316 [self->valueStack addObject:value];
318 self->nextCharactersProcessor = NULL;
323 if ([self->valueStack count] > 0)
324 [self->valueStack removeLastObject];
326 NSLog(@"%s: valueStack should be empty: %@",
332 - (void)start_member:(id<SaxAttributes>)_attrs {
333 if (![[(XmlRpcValue *)[self->valueStack lastObject] value]
334 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; // TODO: can't we type the var?
349 tmp = [(XmlRpcValue *)[self->valueStack lastObject] value];
351 if ([self->memberNameStack count] != [self->memberValueStack count]) {
352 NSLog(@"Warning(%s): memberNameStack.count != memberValueStack.count"
355 self->memberNameStack,
356 self->memberValueStack,
358 [self->memberValueStack release]; self->memberValueStack = nil;
359 [self->memberNameStack release]; self->memberNameStack = nil;
360 self->invalidCall = YES;
362 else if ([self->memberNameStack count] == 0) {
363 NSLog(@"Warning(%s): memberNameStack and memberValueStack are empty!",
366 [self->memberValueStack release]; self->memberValueStack = nil;
367 [self->memberNameStack release]; self->memberNameStack = nil;
368 self->invalidCall = YES;
370 else if (![tmp isKindOfClass:DictionaryClass])
371 self->invalidCall = YES;
373 [(NSMutableDictionary *)tmp
374 setObject:[self->memberValueStack lastObject]
375 forKey:[self->memberNameStack lastObject]];
377 [self->memberNameStack removeLastObject];
378 [self->memberValueStack removeLastObject];
382 - (void)start_methodCall:(id<SaxAttributes>)_attrs {
383 /* can't create call here, args unknown !!! */
384 if (self->call != nil) {
386 NSLog(@"%s: method-call already setup!", __PRETTY_FUNCTION__);
387 self->invalidCall = YES;
390 if (doDebug) NSLog(@"%s: ...", __PRETTY_FUNCTION__);
392 - (void)end_methodCall {
393 if (self->call != nil) {
395 NSLog(@"%s: method-call already setup!", __PRETTY_FUNCTION__);
396 self->invalidCall = YES;
400 self->call = [[XmlRpcMethodCall alloc] initWithMethodName:self->methodName
401 parameters:self->params];
404 [self->methodName release]; self->methodName = nil;
405 [self->params release]; self->params = nil;
408 - (void)start_methodResponse:(id<SaxAttributes>)_attrs {
409 if (self->response != nil) {
411 NSLog(@"%s: method-response already setup!", __PRETTY_FUNCTION__);
412 self->invalidCall = YES;
417 - (void)end_methodResponse {
418 if (doDebug) NSLog(@"%s: begin ...", __PRETTY_FUNCTION__);
420 if (self->response != nil) {
422 NSLog(@"%s: method-response already setup!", __PRETTY_FUNCTION__);
423 self->invalidCall = YES;
427 if ([self->params count] > 1) {
429 NSLog(@"%s: has more than one params (%i)!", __PRETTY_FUNCTION__,
430 [self->params count]);
432 self->invalidCall = YES;
435 if (self->invalidCall) {
439 NSLog(@"%s: response marked as invalid!", __PRETTY_FUNCTION__);
441 error = [NSException exceptionWithName:@"error while parsing response"
442 reason:@"error while parsing response"
445 [self->params release];
446 self->params = [[NSMutableArray arrayWithObject:error] retain];
450 [[XmlRpcMethodResponse alloc] initWithResult:[self->params lastObject]];
452 NSLog(@"%s: response: %@", __PRETTY_FUNCTION__, self->response);
455 [self->params release]; self->params = nil;
456 if (doDebug) NSLog(@"%s: done.", __PRETTY_FUNCTION__);
459 - (void)start_methodName:(id<SaxAttributes>)_attrs {
460 if (self->call != nil) {
461 self->invalidCall = YES;
464 self->nextCharactersProcessor = @selector(_methodName:length:);
467 - (void)end_methodName {
468 self->nextCharactersProcessor = NULL;
470 - (void)_methodName:(unichar *)_chars length:(int)_len {
471 [self->methodName release];
472 self->methodName = [[NSString alloc] initWithCharacters:_chars length:_len];
475 - (void)start_fault:(id<SaxAttributes>)_attrs {
477 self->invalidCall = YES;
481 self->params = [[NSMutableArray alloc] initWithCapacity:2];
483 self->nextCharactersProcessor = NULL;
486 if (self->params == nil)
487 self->invalidCall = YES;
489 self->nextCharactersProcessor = NULL;
491 /* fixup result class */
492 if ([self->params count] != 1) {
493 NSLog(@"XML-RPC: incorrect params count (should be 1 for faults) ?: %@",
499 fault = [self->params objectAtIndex:0];
500 if (![fault isException]) {
501 if ([fault isDictionary]) {
502 [fault setClassName:@"NSException"];
509 NSLog(@"XML-RPC: got incorrect fault object (class=%@) ?: %@",
510 [fault className], fault);
511 name = [NSString stringWithFormat:@"XmlRpcFault<%@>",
512 [fault valueForKey:@"faultCode"]];
513 if (name == nil) name = @"GenericXmlRpcFault";
514 reason = [fault valueForKey:@"faultString"];
515 if (reason == nil) reason = name;
517 e = [NSException exceptionWithName:name reason:reason userInfo:nil];
518 [self->params replaceObjectAtIndex:0 withObject:e];
524 /* generic dispatcher */
526 - (void)startElement:(NSString *)_localName
527 namespace:(NSString *)_ns
528 rawName:(NSString *)_rawName
529 attributes:(id<SaxAttributes>)_attrs
535 [self->tagStack addObject:_rawName];
537 tmp = ((idx = [_attrs indexOfRawName:@"NSObjectClass"]) != NSNotFound)
538 ? (id)[_attrs valueAtIndex:idx]
541 [self->className autorelease];
542 self->className = [tmp retain];
544 if (self->invalidCall) return;
546 if ([_rawName isEqualToString:@"dateTime.iso8601"])
547 _rawName = @"dateTime";
549 [self->characters setString:@""];
551 tmp = [NSString stringWithFormat:@"start_%@:",_rawName];
552 if ((sel = NSSelectorFromString(tmp))) {
553 if ([self respondsToSelector:sel])
554 [self performSelector:sel withObject:_attrs];
558 - (void)endElement:(NSString *)_localName
559 namespace:(NSString *)_ns
560 rawName:(NSString *)_rawName
562 unsigned stackDepth, lastIdx;
566 if (self->nextCharactersProcessor != NULL) {
567 void (*m)(id, SEL, unichar *, int);
571 len = [self->characters length];
572 chars = malloc(sizeof(unichar)*len);
573 [self->characters getCharacters:chars];
575 if ((m = (void*)[self methodForSelector:self->nextCharactersProcessor]))
576 m(self, self->nextCharactersProcessor, chars, len);
581 self->nextCharactersProcessor = NULL;
583 if ((stackDepth = [self->tagStack count]) == 0) {
584 self->invalidCall = YES;
587 lastIdx = stackDepth - 1;
588 if (![[self->tagStack objectAtIndex:lastIdx] isEqualToString:_rawName]) {
589 self->invalidCall = YES;
592 [self->tagStack removeObjectAtIndex:lastIdx];
595 if (self->invalidCall) {
599 if ([_rawName isEqualToString:@"dateTime.iso8601"])
600 _rawName = @"dateTime";
602 tmp = [NSString stringWithFormat:@"end_%@", _rawName];
603 if ((sel = NSSelectorFromString(tmp))) {
604 if ([self respondsToSelector:sel])
605 [self performSelector:sel];
609 - (void)characters:(unichar *)_chars length:(int)_len {
611 [self->characters appendString:
612 [NSString stringWithCharacters:_chars length:_len]];
618 - (void)warning:(SaxParseException *)_exception {
619 NSLog(@"XML-RPC warning: %@", _exception);
621 - (void)error:(SaxParseException *)_exception {
622 NSLog(@"XML-RPC error: %@", _exception);
625 @end /* XmlRpcSaxHandler */