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 "XmlRpcCoder.h"
24 #include "XmlRpcValue.h"
25 #include "XmlRpcSaxHandler.h"
26 #include <SaxObjC/SaxXMLReaderFactory.h>
27 #include "XmlRpcMethodCall.h"
28 #include "XmlRpcMethodResponse.h"
31 /* self->unarchivedObjects does *not* store objects with xmlrpc base type! */
33 @interface XmlRpcDecoder(PrivateMethodes)
34 - (id)_decodeObject:(XmlRpcValue *)_value;
37 @interface NSData(NGExtensions)
38 - (NSData *)dataByDecodingBase64;
41 @implementation XmlRpcDecoder
43 - (BOOL)profileXmlRpcCoding {
44 return [[NSUserDefaults standardUserDefaults]
45 boolForKey:@"ProfileXmlRpcCoding"];
48 #if HAVE_NSXMLPARSER && 0
49 #warning using NSXMLParser!
50 static BOOL useNSXMLParser = YES;
52 static BOOL useNSXMLParser = NO;
54 static id saxHandler = nil;
55 static id xmlRpcParser = nil;
57 static Class ArrayClass = Nil;
58 static Class DictionaryClass = Nil;
59 static Class DataClass = Nil;
60 static Class NumberClass = Nil;
61 static Class StringClass = Nil;
62 static Class DateClass = Nil;
63 static BOOL doDebug = NO;
66 if (ArrayClass == Nil) ArrayClass = [NSArray class];
67 if (DictionaryClass == Nil) DictionaryClass = [NSDictionary class];
68 if (DataClass == Nil) DataClass = [NSData class];
69 if (NumberClass == Nil) NumberClass = [NSNumber class];
70 if (StringClass == Nil) StringClass = [NSString class];
71 if (DateClass == Nil) DateClass = [NSCalendarDate class];
75 if ((self = [super init])) {
76 self->unarchivedObjects = [[NSMutableArray alloc] init];
77 self->awakeObjects = [[NSMutableSet alloc] init];
78 self->valueStack = [[NSMutableArray alloc] initWithCapacity:4];
79 self->timeZone = [[NSTimeZone timeZoneWithAbbreviation:@"GMT"] retain];
84 - (NSStringEncoding)encodingForXMLEncodingString:(NSString *)_enc {
85 _enc = [_enc lowercaseString];
86 if ([_enc isEqualToString:@"utf-8"])
87 return NSUTF8StringEncoding;
88 else if ([_enc isEqualToString:@"iso-8859-1"])
89 return NSISOLatin1StringEncoding;
90 #if !(NeXT_Foundation_LIBRARY || APPLE_Foundation_LIBRARY)
91 else if ([_enc isEqualToString:@"iso-8859-9"])
92 return NSISOLatin9StringEncoding;
94 else if ([_enc isEqualToString:@"ascii"])
95 return NSASCIIStringEncoding;
97 NSLog(@"%s: UNKNOWN XML ENCODING '%@'", __PRETTY_FUNCTION__, _enc);
101 - (id)initForReadingWithString:(NSString *)_string {
102 if ((self = [self init])) {
105 r = [_string rangeOfString:@"?>"];
106 if ([_string hasPrefix:@"<?xml "] && r.length != 0) {
109 xmlDecl = [_string substringToIndex:r.location];
111 r = [xmlDecl rangeOfString:@"encoding='"];
113 xmlDecl = [_string substringFromIndex:(r.location + 10)];
114 r = [xmlDecl rangeOfString:@"'"];
115 xmlDecl = r.length == 0
117 : [xmlDecl substringToIndex:r.location];
120 r = [xmlDecl rangeOfString:@"encoding=\""];
122 xmlDecl = [_string substringFromIndex:(r.location + 10)];
123 r = [xmlDecl rangeOfString:@"\""];
124 xmlDecl = r.length == 0
126 : [xmlDecl substringToIndex:r.location];
132 if ([xmlDecl length] > 0) {
133 NSStringEncoding enc;
135 if ((enc = [self encodingForXMLEncodingString:xmlDecl]) != 0) {
136 self->data = [[_string dataUsingEncoding:enc] retain];
137 if (self->data == nil) {
138 NSLog(@"WARNING(%s): couldn't get data for string '%@', "
139 @"encoding %i !", __PRETTY_FUNCTION__, _string, enc);
147 if (self->data == nil)
148 self->data = [[_string dataUsingEncoding:NSUTF8StringEncoding] retain];
152 - (id)initForReadingWithData:(NSData *)_data {
153 if ((self = [self init])) {
154 self->data = [_data retain];
160 [self->data release];
161 [self->valueStack release];
162 [self->unarchivedObjects release];
163 [self->awakeObjects release];
165 [self->timeZone release];
171 - (void)setDefaultTimeZone:(NSTimeZone *)_timeZone {
172 [self->timeZone autorelease];
173 self->timeZone = [_timeZone retain];
176 - (NSTimeZone *)defaultTimeZone {
177 return self->timeZone;
182 - (void)_ensureSaxAndParser {
183 if (saxHandler == nil) {
184 if ((saxHandler = [[XmlRpcSaxHandler alloc] init]) == nil) {
185 NSLog(@"%s: did not find sax handler ...", __PRETTY_FUNCTION__);
190 if (useNSXMLParser) return;
191 if (xmlRpcParser != nil) return;
194 [[SaxXMLReaderFactory standardXMLReaderFactory]
195 createXMLReaderForMimeType:@"text/xml"];
196 if (xmlRpcParser == nil) {
197 NSLog(@"%s: did not find an XML parser ...", __PRETTY_FUNCTION__);
201 [xmlRpcParser setContentHandler:saxHandler];
202 [xmlRpcParser setDTDHandler:saxHandler];
203 [xmlRpcParser setErrorHandler:saxHandler];
204 [xmlRpcParser retain];
207 - (id)_decodeValueOfClass:(Class)_class {
210 obj = [[self->valueStack lastObject] value];
212 if ([obj isKindOfClass:_class])
215 NSLog(@"WARNING(%s): obj (%@) is not of proper class ('%@'<-->'%@'",
218 NSStringFromClass(_class),
219 NSStringFromClass([obj class]),
224 - (XmlRpcMethodCall *)_decodeMethodCall:(XmlRpcMethodCall *)_baseCall {
226 NSEnumerator *paramEnum;
227 XmlRpcMethodCall *result = nil;
228 XmlRpcValue *param = nil;
229 NSMutableArray *decParams = nil; // decoded parameters
231 params = [_baseCall parameters]; // XmlRpcValues!!
232 decParams = [[NSMutableArray alloc] initWithCapacity:[params count]];
234 paramEnum = [params objectEnumerator];
235 while ((param = [paramEnum nextObject])) {
238 if ((obj = [self _decodeObject:param]) != nil)
239 [decParams addObject:obj];
242 result = [[XmlRpcMethodCall alloc] initWithMethodName:[_baseCall methodName]
243 parameters:decParams];
247 return [result autorelease];
250 - (XmlRpcMethodResponse *)_decodeMethodResponse:(XmlRpcMethodResponse *)_resp {
251 static Class ExceptionClass = Nil;
252 XmlRpcMethodResponse *response = nil;
253 id result = [_resp result];
255 if (ExceptionClass == Nil)
256 ExceptionClass = [NSException class];
258 if (![result isKindOfClass:ExceptionClass]) {
260 result = [self _decodeObject:result]; // => XmlRpcValue
262 response = [[XmlRpcMethodResponse alloc] initWithResult:result];
264 return [response autorelease];
267 - (NSStringEncoding)logEncoding {
268 return NSUTF8StringEncoding;
270 - (id)decodeRootObject {
274 NSLog(@"%s: begin (data: %i bytes, nesting: %i)", __PRETTY_FUNCTION__,
275 [self->data length], self->nesting);
277 if ([self->data length] == 0) return nil;
281 [self _ensureSaxAndParser];
282 NSAssert(saxHandler, @"missing sax handler ...");
283 NSAssert(xmlRpcParser || useNSXMLParser, @"missing parser handler ...");
286 if (doDebug) NSLog(@"%s: SAX handler: %@", __PRETTY_FUNCTION__, saxHandler);
288 if (useNSXMLParser) {
291 parser = [[NSXMLParser alloc] initWithData:self->data];
293 NSLog(@"%s: using NSXMLParser: %@", __PRETTY_FUNCTION__, parser);
294 [parser setDelegate:saxHandler];
295 [parser setShouldProcessNamespaces:NO];
296 [parser setShouldReportNamespacePrefixes:NO];
297 [parser setShouldResolveExternalEntities:NO];
303 [xmlRpcParser parseFromSource:self->data systemId:@"<xmlrpc-data>"];
305 if ((tmp = [saxHandler methodCall]) != nil) {
306 tmp = [self _decodeMethodCall:tmp];
308 else if ((tmp = [saxHandler methodResponse]) != nil) {
309 tmp = [self _decodeMethodResponse:tmp];
311 else if (tmp == nil) {
314 s = [[NSString alloc] initWithData:self->data encoding:[self logEncoding]];
315 NSLog(@"%s: couldn't parse source\n"
320 __PRETTY_FUNCTION__, xmlRpcParser, saxHandler, self->data, s);
324 NSLog(@"%s: neither call nor response (handler=%@): %@ ..",
331 // [saxHandler reset]; // hh asks: why is that commented out?
332 if (doDebug) NSLog(@"%s: end: %@", __PRETTY_FUNCTION__, tmp);
336 - (XmlRpcMethodCall *)decodeMethodCall {
337 XmlRpcMethodCall *result;
338 NSTimeInterval st = 0.0;
340 if ([self profileXmlRpcCoding])
341 st = [[NSDate date] timeIntervalSince1970];
343 result = [self decodeRootObject];
345 if ([self profileXmlRpcCoding]) {
347 diff = [[NSDate date] timeIntervalSince1970] - st;
349 printf("+++ decodeMethodCall: %0.5fs\n", diff);
352 return ([result isKindOfClass:[XmlRpcMethodCall class]])
357 - (XmlRpcMethodResponse *)decodeMethodResponse {
358 XmlRpcMethodResponse *result;
359 NSTimeInterval st = 0.0;
361 if ([self profileXmlRpcCoding])
362 st = [[NSDate date] timeIntervalSince1970];
364 result = [self decodeRootObject];
366 if ([self profileXmlRpcCoding]) {
368 diff = [[NSDate date] timeIntervalSince1970] - st;
370 printf("+++ decodeMethodResponse: %0.5fs\n", diff);
373 return [result isKindOfClass:[XmlRpcMethodResponse class]]
378 - (id)_decodeObject:(XmlRpcValue *)_value {
381 if (_value == nil) return nil;
383 [self->valueStack addObject:_value];
384 result = [self decodeObject];
385 [self->valueStack removeLastObject];
390 Class objClass = Nil;
395 if ((self->nesting == 1) && ([self->valueStack count] == 0)) {
396 object = [self decodeRootObject];
400 id value = [self->valueStack lastObject];
402 className = [value className];
404 if ([className length] == 0) {
405 object = [value value];
407 else if ((objClass = NSClassFromString(className)) != Nil) {
408 object = [objClass decodeObjectWithXmlRpcCoder:self];
411 NSLog(@"%s: class (%@) specified by value (%@) wasn't found.",
412 __PRETTY_FUNCTION__, className, value);
413 object = [value value];
416 if (object) [self->unarchivedObjects addObject:object];
424 - (NSDictionary *)decodeStruct {
427 NSMutableDictionary *tmp;
428 NSEnumerator *keyEnum;
431 dict = (NSDictionary *)[[self->valueStack lastObject] value];
433 if (!([dict respondsToSelector:@selector(keyEnumerator)] &&
434 [dict respondsToSelector:@selector(objectForKey:)]))
437 keyEnum = [dict keyEnumerator];
438 tmp = [[NSMutableDictionary alloc] initWithCapacity:8];
440 while ((key = [keyEnum nextObject])) {
444 v = [dict objectForKey:key];
445 if ((obj = [self _decodeObject:v]) != nil)
446 [tmp setObject:obj forKey:key];
448 result = [NSDictionary dictionaryWithDictionary:tmp];
453 - (NSArray *)decodeArray {
456 NSMutableArray *tmp = nil;
457 NSEnumerator *valEnum;
458 XmlRpcValue *val = nil;
460 array = (NSArray *)[[self->valueStack lastObject] value];
461 if (![array respondsToSelector:@selector(objectEnumerator)])
464 valEnum = [array objectEnumerator];
465 tmp = [[NSMutableArray alloc] initWithCapacity:8];
467 while ((val = [valEnum nextObject])) {
470 if ((obj = [self _decodeObject:val]) != nil)
473 result = [NSArray arrayWithArray:tmp];
478 - (NSData *)decodeBase64 {
479 return [[self _decodeValueOfClass:DataClass]
480 dataByDecodingBase64];
483 - (BOOL)decodeBoolean {
484 return [[self _decodeValueOfClass:NumberClass] boolValue];
488 return [[self _decodeValueOfClass:NumberClass] intValue];
491 - (double)decodeDouble {
492 return [[self _decodeValueOfClass:NumberClass] doubleValue];
495 - (NSString *)decodeString {
496 return [self _decodeValueOfClass:StringClass];
499 - (NSCalendarDate *)decodeDateTime {
500 NSCalendarDate *date;
504 if ((date = [self _decodeValueOfClass:DateClass]) == nil)
507 if ((tz = [date timeZone]))
510 if (![date respondsToSelector:@selector(setTimeZone:)]) {
511 /* a plain date ... */
512 NSLog(@"cannot set timezone on date: %@", date);
516 /* apply timezone correction */
517 secFromGMT = [self->timeZone secondsFromGMT];
518 [date setTimeZone:self->timeZone];
519 date = [date dateByAddingYears:0 months:0 days:0
520 hours:0 minutes:0 seconds:-secFromGMT];
524 - (XmlRpcValue *)beginDecodingKey:(NSString *)_key {
525 XmlRpcValue *newValue;
528 obj = (NSDictionary *)[[self->valueStack lastObject] value];
529 NSAssert(_key != nil, @"_key is not allowed to be nil");
531 if (![obj isKindOfClass:DictionaryClass]) {
532 NSLog(@"WARNING(%s): obj (%@) is not kind of class NSDictionary !!",
533 __PRETTY_FUNCTION__, obj, nil);
537 if ((newValue = [obj objectForKey:_key]) == nil)
538 /* got no value for key ... */
541 [self->valueStack addObject:newValue];
544 - (void)finishedDecodingKey {
545 [self->valueStack removeLastObject];
548 - (id)_decodeValueForKey:(NSString *)_key selector:(SEL)_selector {
549 XmlRpcValue *newValue;
552 if ((newValue = [self beginDecodingKey:_key]) == nil)
555 result = [[self performSelector:_selector] retain];
556 [self finishedDecodingKey];
557 return [result autorelease];
560 - (NSDictionary *)decodeStructForKey:(NSString *)_key {
561 return [self _decodeValueForKey:_key selector:@selector(decodeStruct)];
564 - (NSArray *)decodeArrayForKey:(NSString *)_key {
565 return [self _decodeValueForKey:_key selector:@selector(decodeArray)];
568 - (NSData *)decodeBase64ForKey:(NSString *)_key {
569 return [self _decodeValueForKey:_key selector:@selector(decodeBase64)];
572 - (BOOL)decodeBooleanForKey:(NSString *)_key {
573 XmlRpcValue *newValue;
576 if ((newValue = [self beginDecodingKey:_key]) == nil)
577 /* any useful alternatives ? */
580 result = [self decodeBoolean];
581 [self finishedDecodingKey];
585 - (int)decodeIntForKey:(NSString *)_key {
586 XmlRpcValue *newValue;
589 if ((newValue = [self beginDecodingKey:_key]) == nil)
590 /* any useful alternatives ? */
593 result = [self decodeInt];
594 [self finishedDecodingKey];
598 - (double)decodeDoubleForKey:(NSString *)_key {
599 XmlRpcValue *newValue;
602 if ((newValue = [self beginDecodingKey:_key]) == nil)
603 /* any useful alternatives ? */
606 result = [self decodeDouble];
607 [self finishedDecodingKey];
611 - (NSString *)decodeStringForKey:(NSString *)_key {
612 return [self _decodeValueForKey:_key selector:@selector(decodeString)];
615 - (NSCalendarDate *)decodeDateTimeForKey:(NSString *)_key {
616 return [self _decodeValueForKey:_key selector:@selector(decodeDateTime)];
619 - (id)decodeObjectForKey:(NSString *)_key {
620 return [self _decodeValueForKey:_key selector:@selector(decodeObject)];
625 - (void)ensureObjectAwake:(id)_object {
626 if ([self->awakeObjects containsObject:_object])
629 if ([_object respondsToSelector:@selector(awakeFromXmlRpcDecoder:)])
630 [_object awakeFromXmlRpcDecoder:self];
631 [self->awakeObjects addObject:_object];
633 - (void)awakeObjects {
637 e = [self->unarchivedObjects objectEnumerator];
638 while ((obj = [e nextObject]))
639 [self ensureObjectAwake:obj];
642 - (void)finishInitializationOfObjects {
646 e = [self->unarchivedObjects objectEnumerator];
647 while ((obj = [e nextObject])) {
648 if ([obj respondsToSelector:
649 @selector(finishInitializationWithXmlRpcDecoder:)])
650 [obj finishInitializationWithXmlRpcDecoder:self];
654 @end /* XmlRpcDecoder */