]> err.no Git - sope/blob - sope-xml/XmlRpc/XmlRpcEncoder.m
added makefiles for FHS hack
[sope] / sope-xml / XmlRpc / XmlRpcEncoder.m
1 /*
2   Copyright (C) 2000-2003 SKYRIX Software AG
3
4   This file is part of OGo
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21 // $Id$
22
23 #include "XmlRpcCoder.h"
24 #include "XmlRpcMethodCall.h"
25 #include "XmlRpcMethodResponse.h"
26 #include "common.h"
27
28 @interface NSMutableString(XmlRpcDecoder)
29 - (void)appendXmlRpcString:(NSString *)_value;
30 @end
31
32 @interface NSData(UsedNGExtensions)
33 - (NSData *)dataByEncodingBase64;
34 @end
35
36 @implementation XmlRpcEncoder
37
38 static NSTimeZone *gmt     = nil;
39 static BOOL profileOn      = NO;
40 static Class NSNumberClass = Nil;
41
42 + (void)initialize {
43   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
44   profileOn = [ud boolForKey:@"ProfileXmlRpcCoding"];
45   gmt       = [[NSTimeZone timeZoneWithAbbreviation:@"GMT"] retain];
46   NSNumberClass = [NSNumber class];
47 }
48
49 - (id)initForWritingWithMutableString:(NSMutableString *)_string {
50   if ((self = [super init])) {
51     self->string = [_string retain];
52     self->timeZone = [gmt retain];
53   }
54   return self;
55 }
56
57 - (void)dealloc {
58   [self->string release];
59   
60   [self->objectStack release];
61   [self->objectHasStructStack release];
62
63   [self->timeZone release];
64   [super dealloc];
65 }
66
67 - (BOOL)profileXmlRpcCoding {
68   return profileOn;
69 }
70
71 /* accessors */
72
73 - (void)setDefaultTimeZone:(NSTimeZone *)_timeZone {
74   [self->timeZone autorelease];
75   self->timeZone = [_timeZone retain];
76 }
77
78 /*"
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.
82 "*/
83 - (NSTimeZone *)defaultTimeZone {
84   return self->timeZone;
85 }
86
87 /* *** */
88
89 - (void)encodeMethodCall:(XmlRpcMethodCall *)_methodCall {
90   NSEnumerator *paramEnum = [[_methodCall parameters] objectEnumerator];
91   id           param;
92   NSTimeInterval st     = 0.0;
93
94   if ([self profileXmlRpcCoding])
95     st = [[NSDate date] timeIntervalSince1970];
96   
97   [self->string appendString:@"<?xml version='1.0'?>"];
98   [self->string appendString:@"<methodCall>"];
99   
100   [self->string appendString:@"<methodName>"];
101   [self->string appendString:[_methodCall methodName]];
102   [self->string appendString:@"</methodName>"];
103   
104   [self->string appendString:@"<params>"];
105   
106   while ((param = [paramEnum nextObject])) {
107     [self->string appendString:@"<param>"];
108     [self->string appendString:@"<value>"];
109
110     [self encodeObject:param];
111     
112     [self->string appendString:@"</value>"];
113     [self->string appendString:@"</param>"];
114   }
115
116   [self->string appendString:@"</params>"];
117   [self->string appendString:@"</methodCall>"];
118
119   if ([self profileXmlRpcCoding]) {
120     NSTimeInterval diff;
121     diff = [[NSDate date] timeIntervalSince1970] - st;
122     
123     printf("+++     encodeMethodCall: %0.5fs\n", diff);
124   }
125 }
126
127 - (void)encodeMethodResponse:(XmlRpcMethodResponse *)_methodResponse {
128   id result = [_methodResponse result];
129   static Class ExceptionClass = Nil;
130   NSTimeInterval st           = 0.0;
131
132   if ([self profileXmlRpcCoding])
133     st = [[NSDate date] timeIntervalSince1970];
134
135   if (ExceptionClass == Nil)
136     ExceptionClass = [NSException class];
137
138   [self->string appendString:@"<?xml version='1.0'?>"];
139   [self->string appendString:@"<methodResponse>"];
140
141   if ([result isKindOfClass:ExceptionClass]) {
142     [self->string appendString:@"<fault>"];
143     [self->string appendString:@"<value>"];
144
145     [self encodeObject:result];
146     
147     [self->string appendString:@"</value>"];
148     [self->string appendString:@"</fault>"];
149   }
150   else {
151     [self->string appendString:@"<params>"];
152     [self->string appendString:@"<param>"];
153     [self->string appendString:@"<value>"];
154
155     [self encodeObject:result];
156     
157     [self->string appendString:@"</value>"];
158     [self->string appendString:@"</param>"];
159     [self->string appendString:@"</params>"];
160   }
161
162   [self->string appendString:@"</methodResponse>"];
163
164   if ([self profileXmlRpcCoding]) {
165     NSTimeInterval diff;
166     diff = [[NSDate date] timeIntervalSince1970] - st;
167     
168     printf("+++ enocdeMethodResponse: %0.5fs\n", diff);
169   }
170 }
171
172
173 - (void)_reset {
174   [self->objectStack release];          self->objectStack          = nil;
175   [self->objectHasStructStack release]; self->objectHasStructStack = nil;
176 }
177
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];
183 }
184
185 - (void)_encodeObject:(id)_object {
186   if (_object) {
187     [self _ensureStacks];
188     [self->objectStack addObject:_object];
189     [self->objectHasStructStack addObject:@"NO"];
190
191     [_object encodeWithXmlRpcCoder:self];
192
193     if ([[self->objectHasStructStack lastObject] boolValue]) {
194       [self->string appendString:@"</struct>"];
195     }
196     [self->objectHasStructStack removeLastObject];
197     [self->objectStack removeLastObject];
198   }
199 }
200
201 - (NSString *)_className {
202   id obj;
203   
204   if ((obj = [self->objectStack lastObject]) == nil)
205     return nil;
206
207   if ([obj isKindOfClass:[NSString class]])
208     return @"NSString";
209   
210   return NSStringFromClass([obj classForCoder]);
211 }
212
213 - (void)encodeObject:(id)_obj {
214   [self _encodeObject:_obj];
215 }
216
217 - (BOOL)isObjectClassAttributeEnabled {
218   // adding of NSObjectClass removed due to compatibility issues !
219   return NO;
220 }
221
222 - (void)_appendTagName:(NSString *)_tagName attributes:(NSDictionary *)_attrs {
223   NSEnumerator *keyEnum   = [_attrs keyEnumerator];
224   NSString     *key       = nil;
225   
226   [self->string appendString:@"<"];
227   [self->string appendString:_tagName];
228   
229   if ([self isObjectClassAttributeEnabled]) {
230     NSString *className = [self _className];
231     
232     if ([className length] > 0) {
233       [self->string appendString:@" NSObjectClass=\""];
234       [self->string appendXmlRpcString:className];
235       [self->string appendString:@"\""];
236     }
237   }
238   
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:@"\""];
245   }
246   [self->string appendString:@">"];  
247 }
248
249 - (void)_appendTagName:(NSString *)_tagName {
250   [self _appendTagName:_tagName attributes:nil];
251 }
252
253 - (void)_encodeNumber:(NSNumber *)_number tagName:(NSString *)_tagName {
254   [self _appendTagName:_tagName];
255   
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]];
260   else
261     [self->string appendXmlRpcString:[_number stringValue]];
262   
263   [self->string appendString:@"</"];
264   [self->string appendString:_tagName];
265   [self->string appendString:@">"];
266   
267 }
268
269 - (void)encodeStruct:(NSDictionary *)_struct {
270   NSEnumerator *keys = nil;
271   id           key   = nil;
272   
273   [self _appendTagName:@"struct"];
274   
275   keys = [_struct keyEnumerator];
276   while ((key = [keys nextObject])) {
277     [self->string appendString:@"<member>"];
278     
279     [self->string appendString:@"<name>"];
280     [self->string appendXmlRpcString:key];
281     [self->string appendString:@"</name>"];
282
283     [self->string appendString:@"<value>"];
284
285     [self _encodeObject:[_struct objectForKey:key]];
286     
287     [self->string appendString:@"</value>"];
288     
289     [self->string appendString:@"</member>"];
290   }
291   
292   [self->string appendString:@"</struct>"];
293 }
294
295 - (void)encodeArray:(NSArray *)_array {
296   NSEnumerator    *valueEnum = [_array objectEnumerator];
297   id value;
298
299   [self _appendTagName:@"array"];
300
301   [self->string appendString:@"<data>"];
302   
303   while ((value = [valueEnum nextObject])) {
304     [self->string appendString:@"<value>"];
305
306     [self _encodeObject:value];
307     
308     [self->string appendString:@"</value>"];
309   }
310   
311   [self->string appendString:@"</data>"];
312   [self->string appendString:@"</array>"];
313 }
314
315 - (void)encodeBase64:(NSData *)_data {
316   NSString *base64;
317   
318   base64 = [[NSString alloc] initWithData:[_data dataByEncodingBase64]
319                              encoding:NSASCIIStringEncoding];
320
321   [self _appendTagName:@"base64"];
322   [self->string appendString:base64];
323   [self->string appendString:@"</base64>"];
324
325   [base64 release]; base64 = nil;
326 }
327
328 - (void)encodeBoolean:(BOOL)_number {
329   [self _encodeNumber:[NSNumberClass numberWithBool:_number] tagName:@"boolean"];
330 }
331 - (void)encodeInt:(int)_number {
332   [self _encodeNumber:[NSNumberClass numberWithInt:_number] tagName:@"int"];
333 }
334 - (void)encodeDouble:(double)_number {
335   [self _encodeNumber:[NSNumberClass numberWithDouble:_number] tagName:@"double"];
336 }
337
338 - (void)encodeString:(NSString *)_string {
339   [self _appendTagName:@"string"];
340   [self->string appendXmlRpcString:_string];
341   [self->string appendString:@"</string>"];
342 }
343
344 - (void)encodeDateTime:(NSDate *)_date {
345   static NSDictionary *attrs = nil;
346   NSCalendarDate *date;
347   NSString *s;
348   
349   if (attrs == nil) {
350     attrs = [[NSDictionary alloc] 
351               initWithObjectsAndKeys:@"GMT",@"timeZone",nil];
352   }
353   
354   /* convert parameter to GMT */
355   
356 #if LIB_FOUNDATION_LIBRARY
357   /* TODO: not sure whether lF handles reference-date correctly ... */
358   date = [[NSCalendarDate alloc] initWithTimeIntervalSince1970:
359                                    [_date timeIntervalSince1970]];
360 #else
361   date = [[NSCalendarDate alloc] initWithTimeIntervalSinceReferenceDate:
362                                    [_date timeIntervalSinceReferenceDate]];
363 #endif
364   [date setTimeZone:gmt];
365
366   /* format in XML-RPC date format */
367   
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]];
371   
372   [date release]; date = nil;
373   
374   [self _appendTagName:@"dateTime.iso8601" attributes:attrs];
375   [self->string appendString:s];
376   [self->string appendString:@"</dateTime.iso8601>"];
377   
378   [s release];
379 }
380
381 - (void)_appendMember:(id)_obj forKey:(NSString *)_key selector:(SEL)_sel {
382   [self _ensureStacks];
383   
384   if (![[self->objectHasStructStack lastObject] boolValue]) {
385     [self->objectHasStructStack removeLastObject];
386     [self->objectHasStructStack addObject:@"YES"];
387     [self _appendTagName:@"struct"];
388   }  
389   if (_obj != nil) {
390     [self->objectStack addObject:_obj];
391     [self->string appendString:@"<member><name>"];
392     [self->string appendString:_key];
393     [self->string appendString:@"</name><value>"];
394     
395     /* 
396       this does not work for int/double on OSX, since OSX doesn't coerce the
397       argument to the receivers parameter type
398     */
399     [self performSelector:_sel withObject:_obj];
400     
401     [self->string appendString:@"</value></member>"];
402     [self->objectStack removeLastObject];
403   }
404 }
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];
409   
410   if (![[self->objectHasStructStack lastObject] boolValue]) {
411     [self->objectHasStructStack removeLastObject];
412     [self->objectHasStructStack addObject:@"YES"];
413     [self _appendTagName:@"struct"];
414   }  
415   
416   [self->objectStack addObject:[NSNumberClass numberWithInt:_i]];
417   [self->string appendString:@"<member><name>"];
418   [self->string appendString:_key];
419   [self->string appendString:@"</name><value>"];
420   
421   m = (void *)[self methodForSelector:_sel];
422   m(self, _sel, _i);
423   
424   [self->string appendString:@"</value></member>"];
425   [self->objectStack removeLastObject];
426 }
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];
431   
432   if (![[self->objectHasStructStack lastObject] boolValue]) {
433     [self->objectHasStructStack removeLastObject];
434     [self->objectHasStructStack addObject:@"YES"];
435     [self _appendTagName:@"struct"];
436   }  
437   
438   [self->objectStack addObject:[NSNumberClass numberWithDouble:_d]];
439   [self->string appendString:@"<member><name>"];
440   [self->string appendString:_key];
441   [self->string appendString:@"</name><value>"];
442   
443   m = (void *)[self methodForSelector:_sel];
444   m(self, _sel, _d);
445   
446   [self->string appendString:@"</value></member>"];
447   [self->objectStack removeLastObject];
448 }
449
450 - (void)encodeStruct:(NSDictionary *)_struct forKey:(NSString *)_key {
451   [self _appendMember:_struct forKey:_key selector:@selector(encodeStruct:)];
452 }
453 - (void)encodeArray:(NSArray *)_array  forKey:(NSString *)_key {
454   [self _appendMember:_array forKey:_key selector:@selector(encodeArray:)];
455 }
456 - (void)encodeBase64:(NSData *)_data forKey:(NSString *)_key {
457   [self _appendMember:_data forKey:_key selector:@selector(encodeBase64:)];
458 }
459 - (void)encodeBoolean:(BOOL)_number forKey:(NSString *)_key {
460   [self _appendInt:(int)_number forKey:_key selector:@selector(encodeBoolean:)];
461 }
462 - (void)encodeInt:(int)_number forKey:(NSString *)_key {
463   [self _appendInt:_number forKey:_key selector:@selector(encodeInt:)];
464 }
465 - (void)encodeDouble:(double)_number forKey:(NSString *)_key {
466   [self _appendDouble:_number forKey:_key selector:@selector(encodeDouble:)];
467 }
468 - (void)encodeString:(NSString *)_string forKey:(NSString *)_key {
469   [self _appendMember:_string forKey:_key selector:@selector(encodeString:)];
470 }
471 - (void)encodeDateTime:(NSDate *)_date forKey:(NSString *)_key {
472   [self _appendMember:_date forKey:_key selector:@selector(encodeDateTime:)];
473 }
474 - (void)encodeObject:(id)_object forKey:(NSString *)_key {
475   [self _appendMember:_object forKey:_key selector:@selector(encodeObject:)];
476 }
477
478 @end /* XmlRpcEncoder */