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 = [[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 ? [_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 (![[[self->valueStack lastObject] value] isKindOfClass:DictionaryClass]) {
334 self->invalidCall = YES;
337 if (self->memberNameStack == nil)
338 self->memberNameStack = [[NSMutableArray alloc] initWithCapacity:8];
339 if (self->memberValueStack == nil)
340 self->memberValueStack = [[NSMutableArray alloc] initWithCapacity:8];
342 self->nextCharactersProcessor = NULL;
346 id tmp; // TODO: can't we type the var?
348 tmp = [[self->valueStack lastObject] value];
350 if ([self->memberNameStack count] != [self->memberValueStack count]) {
351 NSLog(@"Warning(%s): memberNameStack.count != memberValueStack.count"
354 self->memberNameStack,
355 self->memberValueStack,
357 [self->memberValueStack release]; self->memberValueStack = nil;
358 [self->memberNameStack release]; self->memberNameStack = nil;
359 self->invalidCall = YES;
361 else if ([self->memberNameStack count] == 0) {
362 NSLog(@"Warning(%s): memberNameStack and memberValueStack are empty!",
365 [self->memberValueStack release]; self->memberValueStack = nil;
366 [self->memberNameStack release]; self->memberNameStack = nil;
367 self->invalidCall = YES;
369 else if (![tmp isKindOfClass:DictionaryClass])
370 self->invalidCall = YES;
372 [(NSMutableDictionary *)tmp
373 setObject:[self->memberValueStack lastObject]
374 forKey:[self->memberNameStack lastObject]];
376 [self->memberNameStack removeLastObject];
377 [self->memberValueStack removeLastObject];
381 - (void)start_methodCall:(id<SaxAttributes>)_attrs {
382 /* can't create call here, args unknown !!! */
383 if (self->call != nil) {
385 NSLog(@"%s: method-call already setup!", __PRETTY_FUNCTION__);
386 self->invalidCall = YES;
389 if (doDebug) NSLog(@"%s: ...", __PRETTY_FUNCTION__);
391 - (void)end_methodCall {
392 if (self->call != nil) {
394 NSLog(@"%s: method-call already setup!", __PRETTY_FUNCTION__);
395 self->invalidCall = YES;
399 self->call = [[XmlRpcMethodCall alloc] initWithMethodName:self->methodName
400 parameters:self->params];
403 [self->methodName release]; self->methodName = nil;
404 [self->params release]; self->params = nil;
407 - (void)start_methodResponse:(id<SaxAttributes>)_attrs {
408 if (self->response != nil) {
410 NSLog(@"%s: method-response already setup!", __PRETTY_FUNCTION__);
411 self->invalidCall = YES;
416 - (void)end_methodResponse {
417 if (doDebug) NSLog(@"%s: begin ...", __PRETTY_FUNCTION__);
419 if (self->response != nil) {
421 NSLog(@"%s: method-response already setup!", __PRETTY_FUNCTION__);
422 self->invalidCall = YES;
426 if ([self->params count] > 1) {
428 NSLog(@"%s: has more than one params (%i)!", __PRETTY_FUNCTION__,
429 [self->params count]);
431 self->invalidCall = YES;
434 if (self->invalidCall) {
438 NSLog(@"%s: response marked as invalid!", __PRETTY_FUNCTION__);
440 error = [NSException exceptionWithName:@"error while parsing response"
441 reason:@"error while parsing response"
444 [self->params release];
445 self->params = [[NSMutableArray arrayWithObject:error] retain];
449 [[XmlRpcMethodResponse alloc] initWithResult:[self->params lastObject]];
451 NSLog(@"%s: response: %@", __PRETTY_FUNCTION__, self->response);
454 [self->params release]; self->params = nil;
455 if (doDebug) NSLog(@"%s: done.", __PRETTY_FUNCTION__);
458 - (void)start_methodName:(id<SaxAttributes>)_attrs {
459 if (self->call != nil) {
460 self->invalidCall = YES;
463 self->nextCharactersProcessor = @selector(_methodName:length:);
466 - (void)end_methodName {
467 self->nextCharactersProcessor = NULL;
469 - (void)_methodName:(unichar *)_chars length:(int)_len {
470 [self->methodName release];
471 self->methodName = [[NSString alloc] initWithCharacters:_chars length:_len];
474 - (void)start_fault:(id<SaxAttributes>)_attrs {
476 self->invalidCall = YES;
480 self->params = [[NSMutableArray alloc] initWithCapacity:2];
482 self->nextCharactersProcessor = NULL;
485 if (self->params == nil)
486 self->invalidCall = YES;
488 self->nextCharactersProcessor = NULL;
490 /* fixup result class */
491 if ([self->params count] != 1) {
492 NSLog(@"XML-RPC: incorrect params count (should be 1 for faults) ?: %@",
498 fault = [self->params objectAtIndex:0];
499 if (![fault isException]) {
500 if ([fault isDictionary]) {
501 [fault setClassName:@"NSException"];
508 NSLog(@"XML-RPC: got incorrect fault object (class=%@) ?: %@",
509 [fault className], fault);
510 name = [NSString stringWithFormat:@"XmlRpcFault<%@>",
511 [fault valueForKey:@"faultCode"]];
512 if (name == nil) name = @"GenericXmlRpcFault";
513 reason = [fault valueForKey:@"faultString"];
514 if (reason == nil) reason = name;
516 e = [NSException exceptionWithName:name reason:reason userInfo:nil];
517 [self->params replaceObjectAtIndex:0 withObject:e];
523 /* generic dispatcher */
525 - (void)startElement:(NSString *)_localName
526 namespace:(NSString *)_ns
527 rawName:(NSString *)_rawName
528 attributes:(id<SaxAttributes>)_attrs
534 [self->tagStack addObject:_rawName];
536 tmp = ((idx = [_attrs indexOfRawName:@"NSObjectClass"]) != NSNotFound)
537 ? [_attrs valueAtIndex:idx]
540 [self->className autorelease];
541 self->className = [tmp retain];
543 if (self->invalidCall) return;
545 if ([_rawName isEqualToString:@"dateTime.iso8601"])
546 _rawName = @"dateTime";
548 [self->characters setString:@""];
550 tmp = [NSString stringWithFormat:@"start_%@:",_rawName];
551 if ((sel = NSSelectorFromString(tmp))) {
552 if ([self respondsToSelector:sel])
553 [self performSelector:sel withObject:_attrs];
557 - (void)endElement:(NSString *)_localName
558 namespace:(NSString *)_ns
559 rawName:(NSString *)_rawName
561 unsigned stackDepth, lastIdx;
565 if (self->nextCharactersProcessor != NULL) {
566 void (*m)(id, SEL, unichar *, int);
570 len = [self->characters length];
571 chars = malloc(sizeof(unichar)*len);
572 [self->characters getCharacters:chars];
574 if ((m = (void*)[self methodForSelector:self->nextCharactersProcessor]))
575 m(self, self->nextCharactersProcessor, chars, len);
580 self->nextCharactersProcessor = NULL;
582 if ((stackDepth = [self->tagStack count]) == 0) {
583 self->invalidCall = YES;
586 lastIdx = stackDepth - 1;
587 if (![[self->tagStack objectAtIndex:lastIdx] isEqualToString:_rawName]) {
588 self->invalidCall = YES;
591 [self->tagStack removeObjectAtIndex:lastIdx];
594 if (self->invalidCall) {
598 if ([_rawName isEqualToString:@"dateTime.iso8601"])
599 _rawName = @"dateTime";
601 tmp = [NSString stringWithFormat:@"end_%@", _rawName];
602 if ((sel = NSSelectorFromString(tmp))) {
603 if ([self respondsToSelector:sel])
604 [self performSelector:sel];
608 - (void)characters:(unichar *)_chars length:(int)_len {
610 [self->characters appendString:
611 [NSString stringWithCharacters:_chars length:_len]];
617 - (void)warning:(SaxParseException *)_exception {
618 NSLog(@"XML-RPC warning: %@", _exception);
620 - (void)error:(SaxParseException *)_exception {
621 NSLog(@"XML-RPC error: %@", _exception);
624 @end /* XmlRpcSaxHandler */