* io.c (rb_open_file): encoding in mode string was ignored if perm is
[ruby-svn.git] / lib / xmlrpc / create.rb
blob3c2bbd24e61ff9764d67c5493c2ce6a243a917c3
2 # Creates XML-RPC call/response documents
3
4 # Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
6 # $Id$
9 require "date"
10 require "xmlrpc/base64"
12 module XMLRPC
14   module XMLWriter
16     class Abstract
17       def ele(name, *children)
18         element(name, nil, *children)
19       end
21       def tag(name, txt)
22         element(name, nil, text(txt))
23       end
24     end
27     class Simple < Abstract
29       def document_to_str(doc)
30         doc
31       end
33       def document(*params)
34         params.join("")
35       end
37       def pi(name, *params)
38         "<?#{name} " + params.join(" ") + " ?>"
39       end
41       def element(name, attrs, *children)
42         raise "attributes not yet implemented" unless attrs.nil?
43         if children.empty?
44           "<#{name}/>" 
45         else
46           "<#{name}>" + children.join("") + "</#{name}>"
47         end
48       end
50       def text(txt)
51         cleaned = txt.dup
52         cleaned.gsub!(/&/, '&amp;')
53         cleaned.gsub!(/</, '&lt;')
54         cleaned.gsub!(/>/, '&gt;')
55         cleaned
56       end
58     end # class Simple
61     class XMLParser < Abstract
63       def initialize
64         require "xmltreebuilder"
65       end
67       def document_to_str(doc)
68         doc.to_s
69       end
71       def document(*params)
72         XML::SimpleTree::Document.new(*params) 
73       end
75       def pi(name, *params)
76         XML::SimpleTree::ProcessingInstruction.new(name, *params)
77       end
79       def element(name, attrs, *children)
80         XML::SimpleTree::Element.new(name, attrs, *children)
81       end
83       def text(txt)
84         XML::SimpleTree::Text.new(txt)
85       end
87     end # class XMLParser
89     Classes = [Simple, XMLParser]
91     # yields an instance of each installed XML writer
92     def self.each_installed_writer
93       XMLRPC::XMLWriter::Classes.each do |klass|
94         begin
95           yield klass.new
96         rescue LoadError
97         end
98       end
99     end
101   end # module XMLWriter
103   class Create
105     def initialize(xml_writer = nil)
106       @writer = xml_writer || Config::DEFAULT_WRITER.new
107     end
110     def methodCall(name, *params)
111       name = name.to_s
113       if name !~ /[a-zA-Z0-9_.:\/]+/
114         raise ArgumentError, "Wrong XML-RPC method-name"
115       end
117       parameter = params.collect do |param|
118         @writer.ele("param", conv2value(param))
119       end
121       tree = @writer.document(
122                @writer.pi("xml", 'version="1.0"'),
123                @writer.ele("methodCall",   
124                  @writer.tag("methodName", name),
125                  @writer.ele("params", *parameter)    
126                )
127              )
129       @writer.document_to_str(tree) + "\n"
130     end
134     #
135     # generates a XML-RPC methodResponse document
136     #
137     # if is_ret == false then the params array must
138     # contain only one element, which is a structure
139     # of a fault return-value.
140     # 
141     # if is_ret == true then a normal 
142     # return-value of all the given params is created.
143     #
144     def methodResponse(is_ret, *params)
146       if is_ret 
147         resp = params.collect do |param|
148           @writer.ele("param", conv2value(param))
149         end
150      
151         resp = [@writer.ele("params", *resp)]
152       else
153         if params.size != 1 or params[0] === XMLRPC::FaultException 
154           raise ArgumentError, "no valid fault-structure given"
155         end
156         resp = @writer.ele("fault", conv2value(params[0].to_h))
157       end
159         
160       tree = @writer.document(
161                @writer.pi("xml", 'version="1.0"'),
162                @writer.ele("methodResponse", resp) 
163              )
165       @writer.document_to_str(tree) + "\n"
166     end
170     #####################################
171     private
172     #####################################
174     #
175     # converts a Ruby object into
176     # a XML-RPC <value> tag
177     #
178     def conv2value(param)
180         val = case param
181         when Fixnum 
182           @writer.tag("i4", param.to_s)
184         when Bignum
185           if Config::ENABLE_BIGINT
186             @writer.tag("i4", param.to_s)
187           else
188             if param >= -(2**31) and param <= (2**31-1)
189               @writer.tag("i4", param.to_s)
190             else
191               raise "Bignum is too big! Must be signed 32-bit integer!"
192             end
193           end
194         when TrueClass, FalseClass
195           @writer.tag("boolean", param ? "1" : "0")
197         when Symbol 
198           @writer.tag("string", param.to_s)
200         when String 
201           @writer.tag("string", param)
203         when NilClass
204           if Config::ENABLE_NIL_CREATE
205             @writer.ele("nil")
206           else
207             raise "Wrong type NilClass. Not allowed!"
208           end
210         when Float
211           @writer.tag("double", param.to_s)
213         when Struct
214           h = param.members.collect do |key| 
215             value = param[key]
216             @writer.ele("member", 
217               @writer.tag("name", key.to_s),
218               conv2value(value) 
219             )
220           end
222           @writer.ele("struct", *h) 
224         when Hash
225           # TODO: can a Hash be empty?
226           
227           h = param.collect do |key, value|
228             @writer.ele("member", 
229               @writer.tag("name", key.to_s),
230               conv2value(value) 
231             )
232           end
234           @writer.ele("struct", *h) 
236         when Array
237           # TODO: can an Array be empty?
238           a = param.collect {|v| conv2value(v) }
239           
240           @writer.ele("array", 
241             @writer.ele("data", *a)
242           )
244         when Time, Date, ::DateTime
245           @writer.tag("dateTime.iso8601", param.strftime("%Y%m%dT%H:%M:%S"))  
247         when XMLRPC::DateTime
248           @writer.tag("dateTime.iso8601", 
249             format("%.4d%02d%02dT%02d:%02d:%02d", *param.to_a))
250    
251         when XMLRPC::Base64
252           @writer.tag("base64", param.encoded) 
254         else 
255           if Config::ENABLE_MARSHALLING and param.class.included_modules.include? XMLRPC::Marshallable
256             # convert Ruby object into Hash
257             ret = {"___class___" => param.class.name}
258             param.instance_variables.each {|v| 
259               name = v[1..-1]
260               val = param.instance_variable_get(v)
262               if val.nil?
263                 ret[name] = val if Config::ENABLE_NIL_CREATE
264               else
265                 ret[name] = val
266               end
267             }
268             return conv2value(ret)
269           else 
270             ok, pa = wrong_type(param)
271             if ok
272               return conv2value(pa)
273             else 
274               raise "Wrong type!"
275             end
276           end
277         end
278          
279         @writer.ele("value", val)
280     end
282     def wrong_type(value)
283       false
284     end
286     
287   end # class Create
289 end # module XMLRPC