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 "XmlRpcMethodCall.h"
25 #include "XmlRpcMethodResponse.h"
28 @interface NSMutableString(XmlRpcDecoder)
29 - (void)appendXmlRpcString:(NSString *)_value;
32 @interface NSData(UsedNGExtensions)
33 - (NSData *)dataByEncodingBase64;
36 @implementation XmlRpcEncoder
38 static NSTimeZone *gmt = nil;
39 static BOOL profileOn = NO;
40 static Class NSNumberClass = Nil;
43 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
44 profileOn = [ud boolForKey:@"ProfileXmlRpcCoding"];
45 gmt = [[NSTimeZone timeZoneWithAbbreviation:@"GMT"] retain];
46 NSNumberClass = [NSNumber class];
49 - (id)initForWritingWithMutableString:(NSMutableString *)_string {
50 if ((self = [super init])) {
51 self->string = [_string retain];
52 self->timeZone = [gmt retain];
58 [self->string release];
60 [self->objectStack release];
61 [self->objectHasStructStack release];
63 [self->timeZone release];
67 - (BOOL)profileXmlRpcCoding {
73 - (void)setDefaultTimeZone:(NSTimeZone *)_timeZone {
74 [self->timeZone autorelease];
75 self->timeZone = [_timeZone retain];
79 This returns the timezone which will be associated with XML-RPC
80 datetime objects. Note: XML-RPC datetime values have no! associated timezone,
81 it's recommended that you always use UTC though.
83 - (NSTimeZone *)defaultTimeZone {
84 return self->timeZone;
89 - (void)encodeMethodCall:(XmlRpcMethodCall *)_methodCall {
90 NSEnumerator *paramEnum = [[_methodCall parameters] objectEnumerator];
92 NSTimeInterval st = 0.0;
94 if ([self profileXmlRpcCoding])
95 st = [[NSDate date] timeIntervalSince1970];
97 [self->string appendString:@"<?xml version='1.0'?>"];
98 [self->string appendString:@"<methodCall>"];
100 [self->string appendString:@"<methodName>"];
101 [self->string appendString:[_methodCall methodName]];
102 [self->string appendString:@"</methodName>"];
104 [self->string appendString:@"<params>"];
106 while ((param = [paramEnum nextObject])) {
107 [self->string appendString:@"<param>"];
108 [self->string appendString:@"<value>"];
110 [self encodeObject:param];
112 [self->string appendString:@"</value>"];
113 [self->string appendString:@"</param>"];
116 [self->string appendString:@"</params>"];
117 [self->string appendString:@"</methodCall>"];
119 if ([self profileXmlRpcCoding]) {
121 diff = [[NSDate date] timeIntervalSince1970] - st;
123 printf("+++ encodeMethodCall: %0.5fs\n", diff);
127 - (void)encodeMethodResponse:(XmlRpcMethodResponse *)_methodResponse {
128 id result = [_methodResponse result];
129 static Class ExceptionClass = Nil;
130 NSTimeInterval st = 0.0;
132 if ([self profileXmlRpcCoding])
133 st = [[NSDate date] timeIntervalSince1970];
135 if (ExceptionClass == Nil)
136 ExceptionClass = [NSException class];
138 [self->string appendString:@"<?xml version='1.0'?>"];
139 [self->string appendString:@"<methodResponse>"];
141 if ([result isKindOfClass:ExceptionClass]) {
142 [self->string appendString:@"<fault>"];
143 [self->string appendString:@"<value>"];
145 [self encodeObject:result];
147 [self->string appendString:@"</value>"];
148 [self->string appendString:@"</fault>"];
151 [self->string appendString:@"<params>"];
152 [self->string appendString:@"<param>"];
153 [self->string appendString:@"<value>"];
155 [self encodeObject:result];
157 [self->string appendString:@"</value>"];
158 [self->string appendString:@"</param>"];
159 [self->string appendString:@"</params>"];
162 [self->string appendString:@"</methodResponse>"];
164 if ([self profileXmlRpcCoding]) {
166 diff = [[NSDate date] timeIntervalSince1970] - st;
168 printf("+++ enocdeMethodResponse: %0.5fs\n", diff);
174 [self->objectStack release]; self->objectStack = nil;
175 [self->objectHasStructStack release]; self->objectHasStructStack = nil;
178 - (void)_ensureStacks {
179 if (self->objectStack == nil)
180 self->objectStack = [[NSMutableArray alloc] initWithCapacity:8];
181 if (self->objectHasStructStack == nil)
182 self->objectHasStructStack = [[NSMutableArray alloc] initWithCapacity:8];
185 - (void)_encodeObject:(id)_object {
187 [self _ensureStacks];
188 [self->objectStack addObject:_object];
189 [self->objectHasStructStack addObject:@"NO"];
191 [_object encodeWithXmlRpcCoder:self];
193 if ([[self->objectHasStructStack lastObject] boolValue]) {
194 [self->string appendString:@"</struct>"];
196 [self->objectHasStructStack removeLastObject];
197 [self->objectStack removeLastObject];
201 - (NSString *)_className {
204 if ((obj = [self->objectStack lastObject]) == nil)
207 if ([obj isKindOfClass:[NSString class]])
210 return NSStringFromClass([obj classForCoder]);
213 - (void)encodeObject:(id)_obj {
214 [self _encodeObject:_obj];
217 - (BOOL)isObjectClassAttributeEnabled {
218 // adding of NSObjectClass removed due to compatibility issues !
222 - (void)_appendTagName:(NSString *)_tagName attributes:(NSDictionary *)_attrs {
223 NSEnumerator *keyEnum = [_attrs keyEnumerator];
226 [self->string appendString:@"<"];
227 [self->string appendString:_tagName];
229 if ([self isObjectClassAttributeEnabled]) {
230 NSString *className = [self _className];
232 if ([className length] > 0) {
233 [self->string appendString:@" NSObjectClass=\""];
234 [self->string appendXmlRpcString:className];
235 [self->string appendString:@"\""];
239 while ((key = [keyEnum nextObject])) {
240 [self->string appendString:@" "];
241 [self->string appendString:key];
242 [self->string appendString:@"=\""];
243 [self->string appendXmlRpcString:[_attrs objectForKey:key]];
244 [self->string appendString:@"\""];
246 [self->string appendString:@">"];
249 - (void)_appendTagName:(NSString *)_tagName {
250 [self _appendTagName:_tagName attributes:nil];
253 - (void)_encodeNumber:(NSNumber *)_number tagName:(NSString *)_tagName {
254 [self _appendTagName:_tagName];
256 if ([_tagName isEqualToString:@"boolean"])
257 [self->string appendString:[_number boolValue] ? @"1" : @"0"];
258 else if ([_tagName isEqualToString:@"int"])
259 [self->string appendFormat:@"%i", [_number intValue]];
261 [self->string appendXmlRpcString:[_number stringValue]];
263 [self->string appendString:@"</"];
264 [self->string appendString:_tagName];
265 [self->string appendString:@">"];
269 - (void)encodeStruct:(NSDictionary *)_struct {
270 NSEnumerator *keys = nil;
273 [self _appendTagName:@"struct"];
275 keys = [_struct keyEnumerator];
276 while ((key = [keys nextObject])) {
277 [self->string appendString:@"<member>"];
279 [self->string appendString:@"<name>"];
280 [self->string appendXmlRpcString:key];
281 [self->string appendString:@"</name>"];
283 [self->string appendString:@"<value>"];
285 [self _encodeObject:[_struct objectForKey:key]];
287 [self->string appendString:@"</value>"];
289 [self->string appendString:@"</member>"];
292 [self->string appendString:@"</struct>"];
295 - (void)encodeArray:(NSArray *)_array {
296 NSEnumerator *valueEnum = [_array objectEnumerator];
299 [self _appendTagName:@"array"];
301 [self->string appendString:@"<data>"];
303 while ((value = [valueEnum nextObject])) {
304 [self->string appendString:@"<value>"];
306 [self _encodeObject:value];
308 [self->string appendString:@"</value>"];
311 [self->string appendString:@"</data>"];
312 [self->string appendString:@"</array>"];
315 - (void)encodeBase64:(NSData *)_data {
318 base64 = [[NSString alloc] initWithData:[_data dataByEncodingBase64]
319 encoding:NSASCIIStringEncoding];
321 [self _appendTagName:@"base64"];
322 [self->string appendString:base64];
323 [self->string appendString:@"</base64>"];
325 [base64 release]; base64 = nil;
328 - (void)encodeBoolean:(BOOL)_number {
329 [self _encodeNumber:[NSNumberClass numberWithBool:_number] tagName:@"boolean"];
331 - (void)encodeInt:(int)_number {
332 [self _encodeNumber:[NSNumberClass numberWithInt:_number] tagName:@"int"];
334 - (void)encodeDouble:(double)_number {
335 [self _encodeNumber:[NSNumberClass numberWithDouble:_number] tagName:@"double"];
338 - (void)encodeString:(NSString *)_string {
339 [self _appendTagName:@"string"];
340 [self->string appendXmlRpcString:_string];
341 [self->string appendString:@"</string>"];
344 - (void)encodeDateTime:(NSDate *)_date {
345 static NSDictionary *attrs = nil;
346 NSCalendarDate *date;
350 attrs = [[NSDictionary alloc]
351 initWithObjectsAndKeys:@"GMT",@"timeZone",nil];
354 /* convert parameter to GMT */
356 #if LIB_FOUNDATION_LIBRARY
357 /* TODO: not sure whether lF handles reference-date correctly ... */
358 date = [[NSCalendarDate alloc] initWithTimeIntervalSince1970:
359 [_date timeIntervalSince1970]];
361 date = [[NSCalendarDate alloc] initWithTimeIntervalSinceReferenceDate:
362 [_date timeIntervalSinceReferenceDate]];
364 [date setTimeZone:gmt];
366 /* format in XML-RPC date format */
368 s = [[NSString alloc] initWithFormat:@"%04i%02i%02iT%02i:%02i:%02i",
369 [date yearOfCommonEra], [date monthOfYear], [date dayOfMonth],
370 [date hourOfDay], [date minuteOfHour], [date secondOfMinute]];
372 [date release]; date = nil;
374 [self _appendTagName:@"dateTime.iso8601" attributes:attrs];
375 [self->string appendString:s];
376 [self->string appendString:@"</dateTime.iso8601>"];
381 - (void)_appendMember:(id)_obj forKey:(NSString *)_key selector:(SEL)_sel {
382 [self _ensureStacks];
384 if (![[self->objectHasStructStack lastObject] boolValue]) {
385 [self->objectHasStructStack removeLastObject];
386 [self->objectHasStructStack addObject:@"YES"];
387 [self _appendTagName:@"struct"];
390 [self->objectStack addObject:_obj];
391 [self->string appendString:@"<member><name>"];
392 [self->string appendString:_key];
393 [self->string appendString:@"</name><value>"];
396 this does not work for int/double on OSX, since OSX doesn't coerce the
397 argument to the receivers parameter type
399 [self performSelector:_sel withObject:_obj];
401 [self->string appendString:@"</value></member>"];
402 [self->objectStack removeLastObject];
405 - (void)_appendInt:(int)_i forKey:(NSString *)_key selector:(SEL)_sel {
406 /* special methods required for OSX */
407 void (*m)(id,SEL,int);
408 [self _ensureStacks];
410 if (![[self->objectHasStructStack lastObject] boolValue]) {
411 [self->objectHasStructStack removeLastObject];
412 [self->objectHasStructStack addObject:@"YES"];
413 [self _appendTagName:@"struct"];
416 [self->objectStack addObject:[NSNumberClass numberWithInt:_i]];
417 [self->string appendString:@"<member><name>"];
418 [self->string appendString:_key];
419 [self->string appendString:@"</name><value>"];
421 m = (void *)[self methodForSelector:_sel];
424 [self->string appendString:@"</value></member>"];
425 [self->objectStack removeLastObject];
427 - (void)_appendDouble:(double)_d forKey:(NSString *)_key selector:(SEL)_sel {
428 /* special methods required for OSX */
429 void (*m)(id,SEL,double);
430 [self _ensureStacks];
432 if (![[self->objectHasStructStack lastObject] boolValue]) {
433 [self->objectHasStructStack removeLastObject];
434 [self->objectHasStructStack addObject:@"YES"];
435 [self _appendTagName:@"struct"];
438 [self->objectStack addObject:[NSNumberClass numberWithDouble:_d]];
439 [self->string appendString:@"<member><name>"];
440 [self->string appendString:_key];
441 [self->string appendString:@"</name><value>"];
443 m = (void *)[self methodForSelector:_sel];
446 [self->string appendString:@"</value></member>"];
447 [self->objectStack removeLastObject];
450 - (void)encodeStruct:(NSDictionary *)_struct forKey:(NSString *)_key {
451 [self _appendMember:_struct forKey:_key selector:@selector(encodeStruct:)];
453 - (void)encodeArray:(NSArray *)_array forKey:(NSString *)_key {
454 [self _appendMember:_array forKey:_key selector:@selector(encodeArray:)];
456 - (void)encodeBase64:(NSData *)_data forKey:(NSString *)_key {
457 [self _appendMember:_data forKey:_key selector:@selector(encodeBase64:)];
459 - (void)encodeBoolean:(BOOL)_number forKey:(NSString *)_key {
460 [self _appendInt:(int)_number forKey:_key selector:@selector(encodeBoolean:)];
462 - (void)encodeInt:(int)_number forKey:(NSString *)_key {
463 [self _appendInt:_number forKey:_key selector:@selector(encodeInt:)];
465 - (void)encodeDouble:(double)_number forKey:(NSString *)_key {
466 [self _appendDouble:_number forKey:_key selector:@selector(encodeDouble:)];
468 - (void)encodeString:(NSString *)_string forKey:(NSString *)_key {
469 [self _appendMember:_string forKey:_key selector:@selector(encodeString:)];
471 - (void)encodeDateTime:(NSDate *)_date forKey:(NSString *)_key {
472 [self _appendMember:_date forKey:_key selector:@selector(encodeDateTime:)];
474 - (void)encodeObject:(id)_object forKey:(NSString *)_key {
475 [self _appendMember:_object forKey:_key selector:@selector(encodeObject:)];
478 @end /* XmlRpcEncoder */