simcha-mappum 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,61 @@
1
+ class SOAP::Mapping::Object
2
+ def id
3
+ self[XSD::QName.new(nil, "id")]
4
+ end
5
+ def type
6
+ self[XSD::QName.new(nil, "type")]
7
+ end
8
+
9
+ #
10
+ # XmlAny element is equal to the other xmlAny element when it
11
+ # has same elements and attributes regardles of ordering of
12
+ # elements and attributes.
13
+ #
14
+ def == other
15
+ return false if other.class != self.class
16
+ return false if @__xmlele - other.__xmlele == []
17
+ return false if @__xmlattr != other.__xmlattr
18
+ return true
19
+ end
20
+ end
21
+
22
+ class OpenXmlObject < SOAP::Mapping::Object
23
+ def method_missing(sym, *args, &block)
24
+ if sym.to_s[-1..-1] == "=" then
25
+ if sym.to_s[0..7] == "xmlattr_"
26
+ #attribute
27
+ name = sym.to_s[8..-2]
28
+ __add_xmlattr_from_method(name, args[0])
29
+ else
30
+ #element
31
+ __add_xmlele_from_method(sym.to_s[0..-2], args[0])
32
+ end
33
+ else
34
+ super(sym, *args, &block)
35
+ end
36
+ end
37
+ def id=(value)
38
+ __add_xmlele_from_method("id", value)
39
+ end
40
+ def type=(value)
41
+ __add_xmlele_from_method("type",value)
42
+ end
43
+ private
44
+ def __add_xmlattr_from_method(name, value)
45
+ @__xmlattr[XSD::QName.new(nil, name)] = value
46
+ self.instance_eval <<-EOS
47
+ def xmlattr_#{name}
48
+ @__xmlattr[XSD::QName.new(nil, #{name})]
49
+ end
50
+
51
+ def xmlattr_#{name}=(value)
52
+ @__xmlattr[XSD::QName.new(nil, #{name})] = value
53
+ end
54
+ EOS
55
+ end
56
+ def __add_xmlele_from_method(name, value)
57
+ Thread.current[:SOAPMapping] ||= {}
58
+ Thread.current[:SOAPMapping][:SafeMethodName] ||= {}
59
+ __add_xmlele_value(XSD::QName.new(nil, name),value)
60
+ end
61
+ end
@@ -0,0 +1,129 @@
1
+ # TODO docs
2
+ require 'set'
3
+ require 'mappum'
4
+ require 'ostruct'
5
+
6
+ module Mappum
7
+ class OpenStruct < OpenStruct
8
+ def type(*attr)
9
+ method_missing(:type, *attr)
10
+ end
11
+ def id(*attr)
12
+ method_missing(:id, *attr)
13
+ end
14
+ end
15
+ class MapMissingException < RuntimeError
16
+ attr_accessor :from
17
+ def initialize(from, msg=nil)
18
+ msg ||= "Map for class \"#{from.class}\" not found!"
19
+ super(msg)
20
+ @from = from
21
+ end
22
+ end
23
+ class RubyTransform
24
+ attr_accessor :map_catalogue
25
+
26
+ def initialize(map_catalogue = nil, default_struct_class=nil)
27
+ @map_catalogue = map_catalogue if map_catalogue.kind_of?(Mappum::Map)
28
+ @map_catalogue ||= Mappum.catalogue(map_catalogue)
29
+ @default_struct_class = default_struct_class
30
+ @default_struct_class ||= Mappum::OpenStruct;
31
+ end
32
+ def get(object, field)
33
+ if field.nil?
34
+ return object
35
+ elsif not object.kind_of?(@default_struct_class) or
36
+ object.respond_to?(field)
37
+ return object.send(field)
38
+ else
39
+ #for open structures field will be defined later
40
+ return nil
41
+ end
42
+ end
43
+ def transform(from, map=nil, to=nil)
44
+
45
+ map ||= @map_catalogue[from.class]
46
+
47
+ raise MapMissingException.new(from) if map.nil?
48
+
49
+ to ||= map.to.clazz.new unless map.to.clazz.nil? or map.to.clazz.kind_of?(Symbol)
50
+
51
+ all_nils = true
52
+
53
+ map.maps.each do |sm|
54
+ from_value, to_value = nil, nil
55
+
56
+ if sm.from.respond_to?(:name)
57
+ from_value = get(from, sm.from.name)
58
+ else
59
+ from_value = sm.from.value
60
+ end
61
+
62
+ if sm.maps.empty?
63
+ to_value = from_value
64
+ elsif not from_value.nil?
65
+ if from_value.instance_of?(Array)
66
+ sm_v = sm.clone
67
+ sm_v.from.is_array = false
68
+ sm_v.to.is_array = false
69
+ to_value = from_value.collect{|v| transform(v, sm_v)}
70
+ else
71
+ to ||= @default_struct_class.new
72
+ v_to = nil
73
+ #array values are assigned after return
74
+ v_to = get(to, sm.to.name) unless sm.to.is_array and not sm.from.is_array
75
+ to_value = transform(from_value, sm, v_to)
76
+ end
77
+
78
+ end
79
+ unless sm.func.nil? or (not sm.func_on_nil? and to_value.nil?)
80
+ to_value = sm.func.call(to_value)
81
+ end
82
+ unless sm.from.func.nil? or to_value.nil?
83
+ to_value = to_value.instance_eval(sm.from.func)
84
+ end
85
+ unless sm.dict.nil?
86
+ to_value = sm.dict[to_value]
87
+ end
88
+ if sm.to.is_array and not sm.from.is_array
89
+ to_array = get(to,sm.to.name)
90
+ to_array ||= []
91
+ to_array << to_value
92
+
93
+ if to_array.empty? and sm.strip_empty?
94
+ to_array = nil
95
+ end
96
+
97
+ all_nils = false unless to_array.nil?
98
+
99
+ if sm.to.name.nil?
100
+ to = to_array
101
+ else
102
+ to ||= @default_struct_class.new
103
+ to.send("#{sm.to.name}=", to_array) unless to_array.nil?
104
+ end
105
+ else
106
+
107
+ if to_value.respond_to?(:empty?) and to_value.empty? and sm.strip_empty?
108
+ to_value = nil
109
+ end
110
+
111
+ all_nils = false unless to_value.nil?
112
+
113
+ if sm.to.name.nil?
114
+ to ||= to_value
115
+ elsif
116
+ to ||= @default_struct_class.new
117
+
118
+ to.send("#{sm.to.name}=", to_value) unless to_value.nil?
119
+ end
120
+ end
121
+
122
+ end
123
+ if all_nils and map.strip_empty?
124
+ return nil
125
+ end
126
+ return to
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,298 @@
1
+ require 'mappum/ruby_transform'
2
+ require 'rubygems'
3
+ gem 'soap4r'
4
+ require 'soap/marshal'
5
+ require 'xsd/mapping'
6
+ require 'wsdl/xmlSchema/xsd2ruby'
7
+ require 'mappum/open_xml_object'
8
+ require 'tmpdir'
9
+
10
+ class XSD::Mapping::Mapper
11
+ attr_reader :registry
12
+ def self.inherited(klass)
13
+ @@mapper_classes ||= []
14
+ @@mapper_classes << klass
15
+ end
16
+ def self.find_mapper_for_class(klass)
17
+ klass = const_get(klass) if klass.instance_of?(Symbol)
18
+ ret_maper=nil
19
+ #FIXME add cache
20
+ mappers.each do |mapper|
21
+ begin
22
+ sch = mapper.registry.schema_definition_from_class klass
23
+ ret_maper = mapper unless sch.nil?
24
+ rescue NoMethodError
25
+ end
26
+ end
27
+ return ret_maper
28
+ end
29
+ def self.get_qname_from_class(klass)
30
+ begin
31
+ klass = const_get(klass) if klass.instance_of?(Symbol)
32
+ rescue NameError
33
+ return nil
34
+ end
35
+ ret_qname = nil
36
+ #FIXME add cache
37
+ mappers.each do |mapper|
38
+ begin
39
+ sch = mapper.registry.schema_definition_from_class klass
40
+ ret_qname = sch.elename unless sch.nil?
41
+ rescue NoMethodError
42
+ end
43
+ end
44
+ return ret_qname
45
+
46
+ end
47
+ def self.find_mapper_for_type(qname)
48
+ ret_maper=nil
49
+ #FIXME add cache
50
+ mappers.each do |mapper|
51
+ begin
52
+ sch = mapper.registry.schema_definition_from_elename qname
53
+ ret_maper = mapper unless sch.nil?
54
+ rescue NoMethodError
55
+ end
56
+ end
57
+ return ret_maper
58
+ end
59
+ def obj2soap(obj, elename = nil, io = nil)
60
+ opt = MAPPING_OPT.dup
61
+ unless elename
62
+ if definition = @registry.elename_schema_definition_from_class(obj.class)
63
+ elename = definition.elename
64
+ opt[:root_type_hint] = false
65
+ end
66
+ end
67
+ elename = SOAP::Mapping.to_qname(elename) if elename
68
+ soap = SOAP::Mapping.obj2soap(obj, @registry, elename, opt)
69
+ if soap.elename.nil? or soap.elename == XSD::QName::EMPTY
70
+ soap.elename =
71
+ XSD::QName.new(nil, SOAP::Mapping.name2elename(obj.class.to_s))
72
+ end
73
+ return soap
74
+ end
75
+
76
+ private
77
+ def self.mappers
78
+ @@mappers ||= []
79
+ @@mapper_classes ||= []
80
+ if @@mapper_classes.size > @@mappers.size
81
+ @@mappers = @@mapper_classes.collect{|k|k.new()}
82
+ end
83
+ @@mappers
84
+ end
85
+ end
86
+
87
+ module Mappum
88
+ class XmlTransform
89
+ def initialize(map_catalogue = nil)
90
+ @default_mapper = XSD::Mapping::Mapper.new(SOAP::Mapping::LiteralRegistry.new)
91
+ @ruby_transform = RubyXmlTransform.new(map_catalogue, OpenXmlObject)
92
+ end
93
+ def transform(from_xml, map=nil, from_qname=nil, to_qname=nil, handle_soap=true)
94
+ soap = false
95
+
96
+ parser = SOAP::Parser.new(XSD::Mapping::Mapper::MAPPING_OPT)
97
+ preparsed = parser.parse(from_xml)
98
+
99
+ if from_qname.nil?
100
+ from_qname = preparsed.elename
101
+ end
102
+
103
+ if handle_soap and from_qname == XSD::QName.new("http://schemas.xmlsoap.org/soap/envelope/","Envelope")
104
+ soap = true
105
+ #for soap remove envelope
106
+ preparsed = preparsed.body.root_node
107
+ from_qname = preparsed.elename
108
+ end
109
+
110
+
111
+ from_mapper = XSD::Mapping::Mapper.find_mapper_for_type(from_qname)
112
+ if from_mapper.nil?
113
+ from_mapper = @default_mapper
114
+ end
115
+
116
+ begin
117
+ parsed =SOAP::Mapping.soap2obj(preparsed, from_mapper.registry, nil)
118
+ rescue NoMethodError => e
119
+ raise ParsingFailedException.new("Parsing failed for xml with root element: #{from_qname}")
120
+ end
121
+
122
+ map ||= @ruby_transform.map_catalogue[from_qname]
123
+ map ||= @ruby_transform.map_catalogue[from_qname.name.to_sym]
124
+ if not map.nil? and not map.kind_of?(Map)
125
+ map = @ruby_transform.map_catalogue[map.to_sym]
126
+ end
127
+
128
+ begin
129
+ transformed = @ruby_transform.transform(parsed, map)
130
+ rescue MapMissingException => e
131
+ if e.from == parsed
132
+ raise MapMissingException.new(e.from,"Map for element \"#{from_qname}\" not found!")
133
+ else
134
+ raise e
135
+ end
136
+ end
137
+
138
+ to_mapper = XSD::Mapping::Mapper.find_mapper_for_class(transformed.class)
139
+ if to_mapper.nil?
140
+ to_mapper = @default_mapper
141
+ end
142
+
143
+ if transformed.kind_of?(OpenXmlObject) and to_qname.nil? and not map.nil?
144
+ to = map.to
145
+ if to.clazz.kind_of?(XSD::QName)
146
+ to_qname = to.clazz
147
+ else
148
+ to_qname = XSD::QName.new(nil, to.clazz.to_s)
149
+ end
150
+ end
151
+ to_preparsed = to_mapper.obj2soap(transformed,to_qname)
152
+ if soap == true
153
+ to_preparsed = SOAP::SOAPEnvelope.new(SOAP::SOAPHeader.new, SOAP::SOAPBody.new(to_preparsed))
154
+ end
155
+ generator = SOAP::Generator.new(XSD::Mapping::Mapper::MAPPING_OPT)
156
+ to_xml = generator.generate(to_preparsed, nil)
157
+ return to_xml
158
+ end
159
+ end
160
+ class RubyXmlTransform < RubyTransform
161
+ def initialize(*args)
162
+ super(*args)
163
+ end
164
+ def get(object, field)
165
+ begin
166
+ super(object, field)
167
+ rescue NoMethodError
168
+ super(object, XSD::CodeGen::GenSupport.safemethodname(field.to_s).to_sym)
169
+ end
170
+ end
171
+ end
172
+ # Class supporting loading working directory of the layout:
173
+ #
174
+ # schema/ - Directory containing xsd files
175
+ #
176
+ # map/ - directory containing Mappum maps
177
+ #
178
+ class WorkdirLoader
179
+ def initialize(schema_path = "schema", map_dir="maps", basedir=nil)
180
+ @schema_path = schema_path
181
+ @basedir = basedir
182
+ @basedir ||= Dir.mktmpdir
183
+ @map_dir = map_dir
184
+ @mapper_scripts = []
185
+ end
186
+ def generate_and_require
187
+ generate_classes
188
+ require_all
189
+ end
190
+ def require_all
191
+ require_schemas
192
+ require_maps
193
+ end
194
+ # Generate classes from xsd files in shema_path (defaults to schema).
195
+ # Files containt in subdirectories are generated to modules where module name is
196
+ # optaind from folder name.
197
+ # Generated classes are saved to basedir (defaults to tmp)
198
+ def generate_classes(schema_path=nil, module_path=nil)
199
+ class_dir = @basedir
200
+ modname = module_path
201
+ unless module_path.nil?
202
+ class_dir = File.join(class_dir, module_path)
203
+ modname = modname.gsub(File::SEPARATOR, "::")
204
+ modname = modname.gsub(/^[a-z]|\s+[a-z]|\:\:+[a-z]/) { |a| a.upcase }
205
+ end
206
+
207
+ Dir.mkdir(class_dir) unless File.exist? class_dir
208
+
209
+
210
+
211
+ schema_path ||= @schema_path
212
+
213
+ Dir.foreach(schema_path) do |file_name|
214
+ full_name = File.join(schema_path,file_name)
215
+ #when file is a directory
216
+ #generate classes in module (recursive)
217
+ if File.directory?(full_name) and not file_name == "." and not file_name == ".."
218
+ #make directory for future class files
219
+ module_pth = file_name
220
+ module_pth = File.join(module_path, module_pth) unless module_path.nil?
221
+
222
+ generate_classes(full_name, module_pth)
223
+ # for XSD files generate classes using XSD2Ruby
224
+ elsif /.*.xsd/ =~ file_name
225
+ run_xsd2ruby(full_name, file_name, module_path, modname)
226
+ @mapper_scripts << class_dir + File::SEPARATOR + file_name[0..-5] + "_mapper.rb"
227
+ end
228
+ end
229
+ end
230
+ def require_schemas
231
+ $:.unshift @basedir
232
+ @mapper_scripts.each do |script|
233
+ require script
234
+ end
235
+ end
236
+ def require_maps
237
+ $:.unshift @map_dir
238
+ Dir.foreach(@map_dir) do |file_name|
239
+ if /.*.rb/ =~ file_name
240
+ require File.join(@map_dir, file_name)
241
+ end
242
+ end
243
+ end
244
+ private
245
+ def run_xsd2ruby(full_name, file_name, module_path, modname)
246
+ worker = WSDL::XMLSchema::XSD2Ruby.new
247
+ worker.location = full_name
248
+ worker.basedir = @basedir
249
+
250
+ classdef = file_name[0..-5]
251
+ classdef = File.join(module_path,classdef) unless module_path.nil?
252
+
253
+ opt = {"classdef" => classdef, "mapping_registry" => "", "mapper" => "", "force" => ""}
254
+ opt["module_path"] = modname unless modname.nil?
255
+ worker.opt.update(opt)
256
+ worker.run
257
+ end
258
+ end
259
+ class XmlSupport
260
+ #choose parser
261
+ begin
262
+ gem 'libxml-ruby'
263
+ require 'libxml'
264
+ @@parser = :libxml
265
+ rescue Gem::LoadError
266
+ require 'rexml/parsers/sax2parser'
267
+ @@parser = :rexml
268
+ end
269
+ #
270
+ # Get target namespace from given xsd file
271
+ #
272
+ def self.get_target_ns(xsd_file)
273
+ return get_target_ns_libxml(xsd_file) if @@parser == :libxml
274
+ return get_target_ns_rexml(xsd_file)
275
+ end
276
+ def self.get_target_ns_rexml(xsd_file)
277
+ reader = REXML::Parsers::SAX2Parser.new(File.new(xsd_file))
278
+ namespace = nil
279
+ #FIXME: quit after root
280
+ reader.listen(:start_element) do |uri, localname, qname, attributes|
281
+ namespace ||= attributes["targetNamespace"]
282
+ end
283
+
284
+ reader.parse
285
+ return namespace
286
+ end
287
+ def self.get_target_ns_libxml(xsd_file)
288
+ reader = LibXML::XML::Reader.file(xsd_file)
289
+ reader.read
290
+ reader.move_to_attribute("targetNamespace")
291
+ namespace = reader.value
292
+ reader.close
293
+ return namespace
294
+ end
295
+ end
296
+ class ParsingFailedException < RuntimeError
297
+ end
298
+ end