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 "XmlRpcCoder.h"
23 #include "XmlRpcValue.h"
24 #include "XmlRpcSaxHandler.h"
25 #include <SaxObjC/SaxXMLReaderFactory.h>
26 #include "XmlRpcMethodCall.h"
27 #include "XmlRpcMethodResponse.h"
30 /* self->unarchivedObjects does *not* store objects with xmlrpc base type! */
32 @interface XmlRpcDecoder(PrivateMethodes)
33 - (id)_decodeObject:(XmlRpcValue *)_value;
36 @interface NSData(NGExtensions)
37 - (NSData *)dataByDecodingBase64;
40 @implementation XmlRpcDecoder
42 - (BOOL)profileXmlRpcCoding {
43 return [[NSUserDefaults standardUserDefaults]
44 boolForKey:@"ProfileXmlRpcCoding"];
47 #if HAVE_NSXMLPARSER && 0
48 #warning using NSXMLParser!
49 static BOOL useNSXMLParser = YES;
51 static BOOL useNSXMLParser = NO;
53 static id saxHandler = nil;
54 static id xmlRpcParser = nil;
56 static Class ArrayClass = Nil;
57 static Class DictionaryClass = Nil;
58 static Class DataClass = Nil;
59 static Class NumberClass = Nil;
60 static Class StringClass = Nil;
61 static Class DateClass = Nil;
62 static BOOL doDebug = NO;
65 if (ArrayClass == Nil) ArrayClass = [NSArray class];
66 if (DictionaryClass == Nil) DictionaryClass = [NSDictionary class];
67 if (DataClass == Nil) DataClass = [NSData class];
68 if (NumberClass == Nil) NumberClass = [NSNumber class];
69 if (StringClass == Nil) StringClass = [NSString class];
70 if (DateClass == Nil) DateClass = [NSCalendarDate class];
74 if ((self = [super init])) {
75 self->unarchivedObjects = [[NSMutableArray alloc] init];
76 self->awakeObjects = [[NSMutableSet alloc] init];
77 self->valueStack = [[NSMutableArray alloc] initWithCapacity:4];
78 self->timeZone = [[NSTimeZone timeZoneWithAbbreviation:@"GMT"] retain];
83 - (NSStringEncoding)encodingForXMLEncodingString:(NSString *)_enc {
84 _enc = [_enc lowercaseString];
85 if ([_enc isEqualToString:@"utf-8"])
86 return NSUTF8StringEncoding;
87 else if ([_enc isEqualToString:@"iso-8859-1"])
88 return NSISOLatin1StringEncoding;
89 #if !(NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY)
90 else if ([_enc isEqualToString:@"iso-8859-9"])
91 return NSISOLatin9StringEncoding;
93 else if ([_enc isEqualToString:@"ascii"])
94 return NSASCIIStringEncoding;
96 NSLog(@"%s: UNKNOWN XML ENCODING '%@'", __PRETTY_FUNCTION__, _enc);
100 - (id)initForReadingWithString:(NSString *)_string {
101 if ((self = [self init])) {
104 r = [_string rangeOfString:@"?>"];
105 if ([_string hasPrefix:@"<?xml "] && r.length != 0) {
108 xmlDecl = [_string substringToIndex:r.location];
110 r = [xmlDecl rangeOfString:@"encoding='"];
112 xmlDecl = [_string substringFromIndex:(r.location + 10)];
113 r = [xmlDecl rangeOfString:@"'"];
114 xmlDecl = r.length == 0
116 : [xmlDecl substringToIndex:r.location];
119 r = [xmlDecl rangeOfString:@"encoding=\""];
121 xmlDecl = [_string substringFromIndex:(r.location + 10)];
122 r = [xmlDecl rangeOfString:@"\""];
123 xmlDecl = r.length == 0
125 : [xmlDecl substringToIndex:r.location];
131 if ([xmlDecl length] > 0) {
132 NSStringEncoding enc;
134 if ((enc = [self encodingForXMLEncodingString:xmlDecl]) != 0) {
135 self->data = [[_string dataUsingEncoding:enc] retain];
136 if (self->data == nil) {
137 NSLog(@"WARNING(%s): couldn't get data for string '%@', "
138 @"encoding %i !", __PRETTY_FUNCTION__, _string, enc);
146 if (self->data == nil)
147 self->data = [[_string dataUsingEncoding:NSUTF8StringEncoding] retain];
151 - (id)initForReadingWithData:(NSData *)_data {
152 if ((self = [self init])) {
153 self->data = [_data retain];
159 [self->data release];
160 [self->valueStack release];
161 [self->unarchivedObjects release];
162 [self->awakeObjects release];
164 [self->timeZone release];
170 - (void)setDefaultTimeZone:(NSTimeZone *)_timeZone {
171 [self->timeZone autorelease];
172 self->timeZone = [_timeZone retain];
175 - (NSTimeZone *)defaultTimeZone {
176 return self->timeZone;
181 - (void)_ensureSaxAndParser {
182 if (saxHandler == nil) {
183 if ((saxHandler = [[XmlRpcSaxHandler alloc] init]) == nil) {
184 NSLog(@"%s: did not find sax handler ...", __PRETTY_FUNCTION__);
189 if (useNSXMLParser) return;
190 if (xmlRpcParser != nil) return;
193 [[SaxXMLReaderFactory standardXMLReaderFactory]
194 createXMLReaderForMimeType:@"text/xml"];
195 if (xmlRpcParser == nil) {
196 NSLog(@"%s: did not find an XML parser ...", __PRETTY_FUNCTION__);
200 [xmlRpcParser setContentHandler:saxHandler];
201 [xmlRpcParser setDTDHandler:saxHandler];
202 [xmlRpcParser setErrorHandler:saxHandler];
203 [xmlRpcParser retain];
206 - (id)_decodeValueOfClass:(Class)_class {
209 obj = [[self->valueStack lastObject] value];
211 if ([obj isKindOfClass:_class])
214 NSLog(@"WARNING(%s): obj (%@) is not of proper class ('%@'<-->'%@'",
217 NSStringFromClass(_class),
218 NSStringFromClass([obj class]),
223 - (XmlRpcMethodCall *)_decodeMethodCall:(XmlRpcMethodCall *)_baseCall {
225 NSEnumerator *paramEnum;
226 XmlRpcMethodCall *result = nil;
227 XmlRpcValue *param = nil;
228 NSMutableArray *decParams = nil; // decoded parameters
230 params = [_baseCall parameters]; // XmlRpcValues!!
231 decParams = [[NSMutableArray alloc] initWithCapacity:[params count]];
233 paramEnum = [params objectEnumerator];
234 while ((param = [paramEnum nextObject])) {
237 if ((obj = [self _decodeObject:param]) != nil)
238 [decParams addObject:obj];
241 result = [[XmlRpcMethodCall alloc] initWithMethodName:[_baseCall methodName]
242 parameters:decParams];
246 return [result autorelease];
249 - (XmlRpcMethodResponse *)_decodeMethodResponse:(XmlRpcMethodResponse *)_resp {
250 static Class ExceptionClass = Nil;
251 XmlRpcMethodResponse *response = nil;
252 id result = [_resp result];
254 if (ExceptionClass == Nil)
255 ExceptionClass = [NSException class];
257 if (![result isKindOfClass:ExceptionClass]) {
259 result = [self _decodeObject:result]; // => XmlRpcValue
261 response = [[XmlRpcMethodResponse alloc] initWithResult:result];
263 return [response autorelease];
266 - (NSStringEncoding)logEncoding {
267 return NSUTF8StringEncoding;
269 - (id)decodeRootObject {
273 NSLog(@"%s: begin (data: %i bytes, nesting: %i)", __PRETTY_FUNCTION__,
274 [self->data length], self->nesting);
276 if ([self->data length] == 0) return nil;
280 [self _ensureSaxAndParser];
281 NSAssert(saxHandler, @"missing sax handler ...");
282 NSAssert(xmlRpcParser || useNSXMLParser, @"missing parser handler ...");
285 if (doDebug) NSLog(@"%s: SAX handler: %@", __PRETTY_FUNCTION__, saxHandler);
287 if (useNSXMLParser) {
290 parser = [[NSXMLParser alloc] initWithData:self->data];
292 NSLog(@"%s: using NSXMLParser: %@", __PRETTY_FUNCTION__, parser);
293 [parser setDelegate:saxHandler];
294 [parser setShouldProcessNamespaces:NO];
295 [parser setShouldReportNamespacePrefixes:NO];
296 [parser setShouldResolveExternalEntities:NO];
302 [xmlRpcParser parseFromSource:self->data systemId:@"<xmlrpc-data>"];
304 if ((tmp = [saxHandler methodCall]) != nil) {
305 tmp = [self _decodeMethodCall:tmp];
307 else if ((tmp = [saxHandler methodResponse]) != nil) {
308 tmp = [self _decodeMethodResponse:tmp];
310 else if (tmp == nil) {
313 s = [[NSString alloc] initWithData:self->data encoding:[self logEncoding]];
314 NSLog(@"%s: couldn't parse source\n"
319 __PRETTY_FUNCTION__, xmlRpcParser, saxHandler, self->data, s);
323 NSLog(@"%s: neither call nor response (handler=%@): %@ ..",
330 // [saxHandler reset]; // hh asks: why is that commented out?
331 if (doDebug) NSLog(@"%s: end: %@", __PRETTY_FUNCTION__, tmp);
335 - (XmlRpcMethodCall *)decodeMethodCall {
336 XmlRpcMethodCall *result;
337 NSTimeInterval st = 0.0;
339 if ([self profileXmlRpcCoding])
340 st = [[NSDate date] timeIntervalSince1970];
342 result = [self decodeRootObject];
344 if ([self profileXmlRpcCoding]) {
346 diff = [[NSDate date] timeIntervalSince1970] - st;
348 printf("+++ decodeMethodCall: %0.5fs\n", diff);
351 return ([result isKindOfClass:[XmlRpcMethodCall class]])
356 - (XmlRpcMethodResponse *)decodeMethodResponse {
357 XmlRpcMethodResponse *result;
358 NSTimeInterval st = 0.0;
360 if ([self profileXmlRpcCoding])
361 st = [[NSDate date] timeIntervalSince1970];
363 result = [self decodeRootObject];
365 if ([self profileXmlRpcCoding]) {
367 diff = [[NSDate date] timeIntervalSince1970] - st;
369 printf("+++ decodeMethodResponse: %0.5fs\n", diff);
372 return [result isKindOfClass:[XmlRpcMethodResponse class]]
377 - (id)_decodeObject:(XmlRpcValue *)_value {
380 if (_value == nil) return nil;
382 [self->valueStack addObject:_value];
383 result = [self decodeObject];
384 [self->valueStack removeLastObject];
389 Class objClass = Nil;
394 if ((self->nesting == 1) && ([self->valueStack count] == 0)) {
395 object = [self decodeRootObject];
399 id value = [self->valueStack lastObject];
401 className = [value className];
403 if ([className length] == 0) {
404 object = [value value];
406 else if ((objClass = NSClassFromString(className)) != Nil) {
407 object = [objClass decodeObjectWithXmlRpcCoder:self];
410 NSLog(@"%s: class (%@) specified by value (%@) wasn't found.",
411 __PRETTY_FUNCTION__, className, value);
412 object = [value value];
415 if (object) [self->unarchivedObjects addObject:object];
423 - (NSDictionary *)decodeStruct {
426 NSMutableDictionary *tmp;
427 NSEnumerator *keyEnum;
430 dict = (NSDictionary *)[[self->valueStack lastObject] value];
432 if (!([dict respondsToSelector:@selector(keyEnumerator)] &&
433 [dict respondsToSelector:@selector(objectForKey:)]))
436 keyEnum = [dict keyEnumerator];
437 tmp = [[NSMutableDictionary alloc] initWithCapacity:8];
439 while ((key = [keyEnum nextObject])) {
443 v = [dict objectForKey:key];
444 if ((obj = [self _decodeObject:v]) != nil)
445 [tmp setObject:obj forKey:key];
447 result = [NSDictionary dictionaryWithDictionary:tmp];
452 - (NSArray *)decodeArray {
455 NSMutableArray *tmp = nil;
456 NSEnumerator *valEnum;
457 XmlRpcValue *val = nil;
459 array = (NSArray *)[[self->valueStack lastObject] value];
460 if (![array respondsToSelector:@selector(objectEnumerator)])
463 valEnum = [array objectEnumerator];
464 tmp = [[NSMutableArray alloc] initWithCapacity:8];
466 while ((val = [valEnum nextObject])) {
469 if ((obj = [self _decodeObject:val]) != nil)
472 result = [NSArray arrayWithArray:tmp];
477 - (NSData *)decodeBase64 {
478 #if 0 /* data is already decoded in the XmlRpcValue */
479 tmp = [tmp dataByDecodingBase64];
481 return [self _decodeValueOfClass:DataClass];
484 - (BOOL)decodeBoolean {
485 return [[self _decodeValueOfClass:NumberClass] boolValue];
489 return [[self _decodeValueOfClass:NumberClass] intValue];
492 - (double)decodeDouble {
493 return [[self _decodeValueOfClass:NumberClass] doubleValue];
496 - (NSString *)decodeString {
497 return [self _decodeValueOfClass:StringClass];
500 - (NSCalendarDate *)decodeDateTime {
501 NSCalendarDate *date;
505 if ((date = [self _decodeValueOfClass:DateClass]) == nil)
508 if ((tz = [date timeZone]))
511 if (![date respondsToSelector:@selector(setTimeZone:)]) {
512 /* a plain date ... */
513 NSLog(@"cannot set timezone on date: %@", date);
517 /* apply timezone correction */
518 secFromGMT = [self->timeZone secondsFromGMT];
519 [date setTimeZone:self->timeZone];
520 date = [date dateByAddingYears:0 months:0 days:0
521 hours:0 minutes:0 seconds:-secFromGMT];
525 - (XmlRpcValue *)beginDecodingKey:(NSString *)_key {
526 XmlRpcValue *newValue;
529 obj = (NSDictionary *)[[self->valueStack lastObject] value];
530 NSAssert(_key != nil, @"_key is not allowed to be nil");
532 if (![obj isKindOfClass:DictionaryClass]) {
533 NSLog(@"WARNING(%s): obj (%@) is not kind of class NSDictionary !!",
534 __PRETTY_FUNCTION__, obj, nil);
538 if ((newValue = [obj objectForKey:_key]) == nil)
539 /* got no value for key ... */
542 [self->valueStack addObject:newValue];
545 - (void)finishedDecodingKey {
546 [self->valueStack removeLastObject];
549 - (id)_decodeValueForKey:(NSString *)_key selector:(SEL)_selector {
550 XmlRpcValue *newValue;
553 if ((newValue = [self beginDecodingKey:_key]) == nil)
556 result = [[self performSelector:_selector] retain];
557 [self finishedDecodingKey];
558 return [result autorelease];
561 - (NSDictionary *)decodeStructForKey:(NSString *)_key {
562 return [self _decodeValueForKey:_key selector:@selector(decodeStruct)];
565 - (NSArray *)decodeArrayForKey:(NSString *)_key {
566 return [self _decodeValueForKey:_key selector:@selector(decodeArray)];
569 - (NSData *)decodeBase64ForKey:(NSString *)_key {
570 return [self _decodeValueForKey:_key selector:@selector(decodeBase64)];
573 - (BOOL)decodeBooleanForKey:(NSString *)_key {
574 XmlRpcValue *newValue;
577 if ((newValue = [self beginDecodingKey:_key]) == nil)
578 /* any useful alternatives ? */
581 result = [self decodeBoolean];
582 [self finishedDecodingKey];
586 - (int)decodeIntForKey:(NSString *)_key {
587 XmlRpcValue *newValue;
590 if ((newValue = [self beginDecodingKey:_key]) == nil)
591 /* any useful alternatives ? */
594 result = [self decodeInt];
595 [self finishedDecodingKey];
599 - (double)decodeDoubleForKey:(NSString *)_key {
600 XmlRpcValue *newValue;
603 if ((newValue = [self beginDecodingKey:_key]) == nil)
604 /* any useful alternatives ? */
607 result = [self decodeDouble];
608 [self finishedDecodingKey];
612 - (NSString *)decodeStringForKey:(NSString *)_key {
613 return [self _decodeValueForKey:_key selector:@selector(decodeString)];
616 - (NSCalendarDate *)decodeDateTimeForKey:(NSString *)_key {
617 return [self _decodeValueForKey:_key selector:@selector(decodeDateTime)];
620 - (id)decodeObjectForKey:(NSString *)_key {
621 return [self _decodeValueForKey:_key selector:@selector(decodeObject)];
626 - (void)ensureObjectAwake:(id)_object {
627 if ([self->awakeObjects containsObject:_object])
630 if ([_object respondsToSelector:@selector(awakeFromXmlRpcDecoder:)])
631 [_object awakeFromXmlRpcDecoder:self];
632 [self->awakeObjects addObject:_object];
634 - (void)awakeObjects {
638 e = [self->unarchivedObjects objectEnumerator];
639 while ((obj = [e nextObject]))
640 [self ensureObjectAwake:obj];
643 - (void)finishInitializationOfObjects {
647 e = [self->unarchivedObjects objectEnumerator];
648 while ((obj = [e nextObject])) {
649 if ([obj respondsToSelector:
650 @selector(finishInitializationWithXmlRpcDecoder:)])
651 [obj finishInitializationWithXmlRpcDecoder:self];
655 @end /* XmlRpcDecoder */