simcha-mappum 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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