/*
Copyright (C) 2000-2005 SKYRIX Software AG
This file is part of SOPE.
SOPE is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with SOPE; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
#include "XmlRpcCoder.h"
#include "XmlRpcMethodCall.h"
#include "XmlRpcMethodResponse.h"
#include "common.h"
@interface NSMutableString(XmlRpcDecoder)
- (void)appendXmlRpcString:(NSString *)_value;
@end
@interface NSData(UsedNGExtensions)
- (NSData *)dataByEncodingBase64;
@end
@implementation XmlRpcEncoder
static NSTimeZone *gmt = nil;
static BOOL profileOn = NO;
static Class NSNumberClass = Nil;
+ (void)initialize {
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
profileOn = [ud boolForKey:@"ProfileXmlRpcCoding"];
gmt = [[NSTimeZone timeZoneWithAbbreviation:@"GMT"] retain];
NSNumberClass = [NSNumber class];
}
- (id)initForWritingWithMutableString:(NSMutableString *)_string {
if ((self = [super init])) {
self->string = [_string retain];
self->timeZone = [gmt retain];
}
return self;
}
- (void)dealloc {
[self->string release];
[self->objectStack release];
[self->objectHasStructStack release];
[self->timeZone release];
[super dealloc];
}
- (BOOL)profileXmlRpcCoding {
return profileOn;
}
/* accessors */
- (void)setDefaultTimeZone:(NSTimeZone *)_timeZone {
[self->timeZone autorelease];
self->timeZone = [_timeZone retain];
}
/*"
This returns the timezone which will be associated with XML-RPC
datetime objects. Note: XML-RPC datetime values have no! associated timezone,
it's recommended that you always use UTC though.
"*/
- (NSTimeZone *)defaultTimeZone {
return self->timeZone;
}
/* *** */
- (void)encodeMethodCall:(XmlRpcMethodCall *)_methodCall {
NSEnumerator *paramEnum = [[_methodCall parameters] objectEnumerator];
id param;
NSTimeInterval st = 0.0;
if ([self profileXmlRpcCoding])
st = [[NSDate date] timeIntervalSince1970];
[self->string appendString:@""];
[self->string appendString:@""];
[self->string appendString:@""];
[self->string appendString:[_methodCall methodName]];
[self->string appendString:@""];
[self->string appendString:@""];
while ((param = [paramEnum nextObject])) {
[self->string appendString:@""];
[self->string appendString:@""];
[self encodeObject:param];
[self->string appendString:@""];
[self->string appendString:@""];
}
[self->string appendString:@""];
[self->string appendString:@""];
if ([self profileXmlRpcCoding]) {
NSTimeInterval diff;
diff = [[NSDate date] timeIntervalSince1970] - st;
printf("+++ encodeMethodCall: %0.5fs\n", diff);
}
}
- (void)encodeMethodResponse:(XmlRpcMethodResponse *)_methodResponse {
id result = [_methodResponse result];
static Class ExceptionClass = Nil;
NSTimeInterval st = 0.0;
if ([self profileXmlRpcCoding])
st = [[NSDate date] timeIntervalSince1970];
if (ExceptionClass == Nil)
ExceptionClass = [NSException class];
[self->string appendString:@""];
[self->string appendString:@""];
if ([result isKindOfClass:ExceptionClass]) {
[self->string appendString:@""];
[self->string appendString:@""];
[self encodeObject:result];
[self->string appendString:@""];
[self->string appendString:@""];
}
else {
[self->string appendString:@""];
[self->string appendString:@""];
[self->string appendString:@""];
[self encodeObject:result];
[self->string appendString:@""];
[self->string appendString:@""];
[self->string appendString:@""];
}
[self->string appendString:@""];
if ([self profileXmlRpcCoding]) {
NSTimeInterval diff;
diff = [[NSDate date] timeIntervalSince1970] - st;
printf("+++ enocdeMethodResponse: %0.5fs\n", diff);
}
}
- (void)_reset {
[self->objectStack release]; self->objectStack = nil;
[self->objectHasStructStack release]; self->objectHasStructStack = nil;
}
- (void)_ensureStacks {
if (self->objectStack == nil)
self->objectStack = [[NSMutableArray alloc] initWithCapacity:8];
if (self->objectHasStructStack == nil)
self->objectHasStructStack = [[NSMutableArray alloc] initWithCapacity:8];
}
- (void)_encodeObject:(id)_object {
if (_object) {
[self _ensureStacks];
[self->objectStack addObject:_object];
[self->objectHasStructStack addObject:@"NO"];
[_object encodeWithXmlRpcCoder:self];
if ([[self->objectHasStructStack lastObject] boolValue]) {
[self->string appendString:@""];
}
[self->objectHasStructStack removeLastObject];
[self->objectStack removeLastObject];
}
}
- (NSString *)_className {
id obj;
if ((obj = [self->objectStack lastObject]) == nil)
return nil;
if ([obj isKindOfClass:[NSString class]])
return @"NSString";
return NSStringFromClass([obj classForCoder]);
}
- (void)encodeObject:(id)_obj {
[self _encodeObject:_obj];
}
- (BOOL)isObjectClassAttributeEnabled {
// adding of NSObjectClass removed due to compatibility issues !
return NO;
}
- (void)_appendTagName:(NSString *)_tagName attributes:(NSDictionary *)_attrs {
NSEnumerator *keyEnum = [_attrs keyEnumerator];
NSString *key = nil;
[self->string appendString:@"<"];
[self->string appendString:_tagName];
if ([self isObjectClassAttributeEnabled]) {
NSString *className = [self _className];
if ([className length] > 0) {
[self->string appendString:@" NSObjectClass=\""];
[self->string appendXmlRpcString:className];
[self->string appendString:@"\""];
}
}
while ((key = [keyEnum nextObject])) {
[self->string appendString:@" "];
[self->string appendString:key];
[self->string appendString:@"=\""];
[self->string appendXmlRpcString:[_attrs objectForKey:key]];
[self->string appendString:@"\""];
}
[self->string appendString:@">"];
}
- (void)_appendTagName:(NSString *)_tagName {
[self _appendTagName:_tagName attributes:nil];
}
- (void)_encodeNumber:(NSNumber *)_number tagName:(NSString *)_tagName {
[self _appendTagName:_tagName];
if ([_tagName isEqualToString:@"boolean"])
[self->string appendString:[_number boolValue] ? @"1" : @"0"];
else if ([_tagName isEqualToString:@"int"])
[self->string appendFormat:@"%i", [_number intValue]];
else
[self->string appendXmlRpcString:[_number stringValue]];
[self->string appendString:@""];
[self->string appendString:_tagName];
[self->string appendString:@">"];
}
- (void)encodeStruct:(NSDictionary *)_struct {
NSEnumerator *keys = nil;
id key = nil;
[self _appendTagName:@"struct"];
keys = [_struct keyEnumerator];
while ((key = [keys nextObject])) {
[self->string appendString:@""];
[self->string appendString:@""];
[self->string appendXmlRpcString:key];
[self->string appendString:@""];
[self->string appendString:@""];
[self _encodeObject:[_struct objectForKey:key]];
[self->string appendString:@""];
[self->string appendString:@""];
}
[self->string appendString:@""];
}
- (void)encodeArray:(NSArray *)_array {
NSEnumerator *valueEnum = [_array objectEnumerator];
id value;
[self _appendTagName:@"array"];
[self->string appendString:@""];
while ((value = [valueEnum nextObject])) {
[self->string appendString:@""];
[self _encodeObject:value];
[self->string appendString:@""];
}
[self->string appendString:@""];
[self->string appendString:@""];
}
- (void)encodeBase64:(NSData *)_data {
NSString *base64;
base64 = [[NSString alloc] initWithData:[_data dataByEncodingBase64]
encoding:NSASCIIStringEncoding];
[self _appendTagName:@"base64"];
[self->string appendString:base64];
[self->string appendString:@""];
[base64 release]; base64 = nil;
}
- (void)encodeBoolean:(BOOL)_number {
[self _encodeNumber:[NSNumberClass numberWithBool:_number] tagName:@"boolean"];
}
- (void)encodeInt:(int)_number {
[self _encodeNumber:[NSNumberClass numberWithInt:_number] tagName:@"int"];
}
- (void)encodeDouble:(double)_number {
[self _encodeNumber:[NSNumberClass numberWithDouble:_number] tagName:@"double"];
}
- (void)encodeString:(NSString *)_string {
[self _appendTagName:@"string"];
[self->string appendXmlRpcString:_string];
[self->string appendString:@""];
}
- (void)encodeDateTime:(NSDate *)_date {
static NSDictionary *attrs = nil;
NSCalendarDate *date;
NSString *s;
if (attrs == nil) {
attrs = [[NSDictionary alloc]
initWithObjectsAndKeys:@"GMT",@"timeZone",nil];
}
/* convert parameter to GMT */
#if LIB_FOUNDATION_LIBRARY
/* TODO: not sure whether lF handles reference-date correctly ... */
date = [[NSCalendarDate alloc] initWithTimeIntervalSince1970:
[_date timeIntervalSince1970]];
#else
date = [[NSCalendarDate alloc] initWithTimeIntervalSinceReferenceDate:
[_date timeIntervalSinceReferenceDate]];
#endif
[date setTimeZone:gmt];
/* format in XML-RPC date format */
s = [[NSString alloc] initWithFormat:@"%04i%02i%02iT%02i:%02i:%02i",
[date yearOfCommonEra], [date monthOfYear], [date dayOfMonth],
[date hourOfDay], [date minuteOfHour], [date secondOfMinute]];
[date release]; date = nil;
[self _appendTagName:@"dateTime.iso8601" attributes:attrs];
[self->string appendString:s];
[self->string appendString:@""];
[s release];
}
- (void)_appendMember:(id)_obj forKey:(NSString *)_key selector:(SEL)_sel {
[self _ensureStacks];
if (![[self->objectHasStructStack lastObject] boolValue]) {
[self->objectHasStructStack removeLastObject];
[self->objectHasStructStack addObject:@"YES"];
[self _appendTagName:@"struct"];
}
if (_obj != nil) {
[self->objectStack addObject:_obj];
[self->string appendString:@""];
[self->string appendString:_key];
[self->string appendString:@""];
/*
this does not work for int/double on OSX, since OSX doesn't coerce the
argument to the receivers parameter type
*/
[self performSelector:_sel withObject:_obj];
[self->string appendString:@""];
[self->objectStack removeLastObject];
}
}
- (void)_appendInt:(int)_i forKey:(NSString *)_key selector:(SEL)_sel {
/* special methods required for OSX */
void (*m)(id,SEL,int);
[self _ensureStacks];
if (![[self->objectHasStructStack lastObject] boolValue]) {
[self->objectHasStructStack removeLastObject];
[self->objectHasStructStack addObject:@"YES"];
[self _appendTagName:@"struct"];
}
[self->objectStack addObject:[NSNumberClass numberWithInt:_i]];
[self->string appendString:@""];
[self->string appendString:_key];
[self->string appendString:@""];
m = (void *)[self methodForSelector:_sel];
m(self, _sel, _i);
[self->string appendString:@""];
[self->objectStack removeLastObject];
}
- (void)_appendDouble:(double)_d forKey:(NSString *)_key selector:(SEL)_sel {
/* special methods required for OSX */
void (*m)(id,SEL,double);
[self _ensureStacks];
if (![[self->objectHasStructStack lastObject] boolValue]) {
[self->objectHasStructStack removeLastObject];
[self->objectHasStructStack addObject:@"YES"];
[self _appendTagName:@"struct"];
}
[self->objectStack addObject:[NSNumberClass numberWithDouble:_d]];
[self->string appendString:@""];
[self->string appendString:_key];
[self->string appendString:@""];
m = (void *)[self methodForSelector:_sel];
m(self, _sel, _d);
[self->string appendString:@""];
[self->objectStack removeLastObject];
}
- (void)encodeStruct:(NSDictionary *)_struct forKey:(NSString *)_key {
[self _appendMember:_struct forKey:_key selector:@selector(encodeStruct:)];
}
- (void)encodeArray:(NSArray *)_array forKey:(NSString *)_key {
[self _appendMember:_array forKey:_key selector:@selector(encodeArray:)];
}
- (void)encodeBase64:(NSData *)_data forKey:(NSString *)_key {
[self _appendMember:_data forKey:_key selector:@selector(encodeBase64:)];
}
- (void)encodeBoolean:(BOOL)_number forKey:(NSString *)_key {
[self _appendInt:(int)_number forKey:_key selector:@selector(encodeBoolean:)];
}
- (void)encodeInt:(int)_number forKey:(NSString *)_key {
[self _appendInt:_number forKey:_key selector:@selector(encodeInt:)];
}
- (void)encodeDouble:(double)_number forKey:(NSString *)_key {
[self _appendDouble:_number forKey:_key selector:@selector(encodeDouble:)];
}
- (void)encodeString:(NSString *)_string forKey:(NSString *)_key {
[self _appendMember:_string forKey:_key selector:@selector(encodeString:)];
}
- (void)encodeDateTime:(NSDate *)_date forKey:(NSString *)_key {
[self _appendMember:_date forKey:_key selector:@selector(encodeDateTime:)];
}
- (void)encodeObject:(id)_object forKey:(NSString *)_key {
[self _appendMember:_object forKey:_key selector:@selector(encodeObject:)];
}
@end /* XmlRpcEncoder */