rexslt 0.1.4 → 0.3.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.
- data/lib/rexslt.rb +259 -107
- metadata +4 -4
data/lib/rexslt.rb
CHANGED
@@ -1,128 +1,115 @@
|
|
1
|
-
|
1
|
+
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
# file: rexslt.rb
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
def initialize(xsl, xml)
|
8
|
-
|
9
|
-
doc_xml = Document.new(xml)
|
10
|
-
doc_xsl = Document.new(xsl)
|
11
|
-
|
12
|
-
@doc = Document.new('<root/>')
|
13
|
-
indent = ''
|
14
|
-
previous_indent = indent.clone
|
15
|
-
@xsl_methods = [:apply_templates, :value_of, :element, :if, :choose, :when, :copy_of, :attribute]
|
5
|
+
require 'rexle'
|
6
|
+
require 'rxfhelper'
|
16
7
|
|
17
|
-
h = XPath.match(doc_xsl.root, "xsl:output/attribute::*").inject({})\
|
18
|
-
{|r,x| r.merge(x.name.to_sym => x.value)}
|
19
|
-
|
20
|
-
@indent = true if h and h[:indent] == 'yes'
|
21
8
|
|
22
|
-
|
23
|
-
|
24
|
-
|
9
|
+
class Rexle::Element
|
10
|
+
def to_xpath(option=nil)
|
11
|
+
def attribute_scan(node)
|
12
|
+
result = ''
|
13
|
+
attr = %w(id class).detect {|x| node.attributes.has_key? x}
|
14
|
+
if attr then
|
15
|
+
value = node.attribute[attr]
|
16
|
+
result = "[@%s='%s']" % [attr, value]
|
17
|
+
end
|
18
|
+
result
|
25
19
|
end
|
26
20
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
21
|
+
def doc_scan(node, option=nil)
|
22
|
+
name = node.name
|
23
|
+
attribute = option == :no_cond ? '' : attribute_scan(node)
|
24
|
+
result = doc_scan(node.parent,option) unless node.root === node.doc_root
|
25
|
+
[result, name.to_s + attribute]
|
31
26
|
end
|
27
|
+
|
28
|
+
doc_scan(self, option).flatten.compact.join('/')
|
32
29
|
end
|
30
|
+
end
|
31
|
+
|
33
32
|
|
33
|
+
class Rexslt
|
34
|
+
|
35
|
+
def initialize(xsl, xml)
|
36
|
+
super()
|
37
|
+
xslt_transform *[xsl, xml].map{|x| RXFHelper.new(x).to_s}
|
38
|
+
end
|
39
|
+
|
34
40
|
def to_s()
|
35
41
|
@doc.to_s[/<root>(.*)<\/root>/m,1]
|
36
42
|
end
|
37
43
|
|
38
44
|
def to_doc(); @doc; end
|
45
|
+
|
46
|
+
alias to_xml to_s
|
39
47
|
|
40
48
|
private
|
41
49
|
|
42
|
-
def
|
43
|
-
|
44
|
-
procs = {"REXML::Element" => :read_raw_element, "REXML::Text" => :read_raw_text}
|
45
|
-
template_node.each do |x|
|
46
|
-
method(procs[x.class.to_s]).call(element, x, doc_element, indent.clone, previous_indent)
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
# element: xml source element, x: xsl element, doc_element: current destination xml element
|
52
|
-
#
|
53
|
-
def read_raw_text(element, x, doc_element, raw_indent, previous_indent)
|
54
|
-
|
55
|
-
indent = @indent == true ? (raw_indent + ' ') : ''
|
56
|
-
doc_element.add(REXML::Text.new(indent + x.to_s, raw=true)) if x.to_s.strip.length > 0
|
57
|
-
end
|
58
|
-
|
59
|
-
def read_raw_element(element, x, doc_element, indent, previous_indent)
|
50
|
+
def apply_templates(element, x, doc_element, indent)
|
60
51
|
|
61
|
-
|
62
|
-
if @xsl_methods.include? method_name then
|
52
|
+
field = x.attributes[:select]
|
63
53
|
|
64
|
-
|
65
|
-
else
|
54
|
+
node = element.element field
|
66
55
|
|
67
|
-
|
68
|
-
|
69
|
-
new_element = Element.new x.name.to_s
|
56
|
+
return unless node
|
70
57
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
doc_element.add new_element
|
75
|
-
read_node(x, element, new_element, indent.clone, previous_indent.clone)
|
76
|
-
|
77
|
-
indent_after(element, x, doc_element, indent, previous_indent) if @indent == true
|
58
|
+
keypath = node.to_xpath :no_cond
|
59
|
+
matched_node = nil
|
78
60
|
|
79
|
-
|
80
|
-
doc_element.add Document.new(x.to_s)
|
61
|
+
raw_template = @templates.to_a.find do |raw_item, template|
|
81
62
|
|
63
|
+
item = raw_item.split('/')
|
64
|
+
|
65
|
+
if match? keypath, item then
|
66
|
+
matched_node = element.xpath field
|
67
|
+
true
|
68
|
+
else
|
69
|
+
child = item.pop
|
70
|
+
if item.length > 0 and match? keypath, item then
|
71
|
+
matched_node = node.xpath child
|
72
|
+
matched_node.any? ? true : false
|
73
|
+
end
|
82
74
|
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def apply_templates(element, x, doc_element, indent, previous_indent)
|
87
|
-
|
88
|
-
field = x.attribute('select').value.to_s
|
89
|
-
XPath.match(element, field).each do |x|
|
90
|
-
read_node @templates[field], x, doc_element, indent, previous_indent
|
91
75
|
end
|
92
|
-
end
|
93
76
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
77
|
+
if matched_node then
|
78
|
+
|
79
|
+
template_xpath, template = raw_template
|
80
|
+
|
81
|
+
matched_node.each do |child_node|
|
82
|
+
|
83
|
+
read_node template, child_node, doc_element, indent
|
84
|
+
end
|
85
|
+
end
|
99
86
|
|
100
87
|
end
|
101
|
-
|
102
|
-
def
|
103
|
-
|
104
|
-
|
105
|
-
new_element = Element.new name
|
106
|
-
doc_element.add new_element
|
107
|
-
read_node(x, element, new_element, indent, previous_indent)
|
88
|
+
|
89
|
+
def match?(keypath, path)
|
90
|
+
x = keypath.split('/').reverse.take path.length
|
91
|
+
x == path.reverse
|
108
92
|
end
|
109
93
|
|
110
|
-
|
94
|
+
|
95
|
+
def attribute(element, x, doc_element, indent)
|
111
96
|
|
112
|
-
|
113
|
-
|
114
|
-
|
97
|
+
name = x.attributes[:name]
|
98
|
+
value = x.text
|
99
|
+
doc_element.add_attribute name, value
|
115
100
|
end
|
116
101
|
|
117
|
-
def choose(element, x, doc_element, indent
|
102
|
+
def choose(element, x, doc_element, indent)
|
118
103
|
|
119
104
|
#get the when clause
|
120
105
|
|
121
|
-
r =
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
106
|
+
r = x.xpath("xsl:when").map do |xsl_node|
|
107
|
+
|
108
|
+
condition = xsl_node.attributes[:test]
|
109
|
+
node = element.element condition
|
110
|
+
|
111
|
+
if node and node == true
|
112
|
+
read_node(xsl_node, element, doc_element, indent)
|
126
113
|
true
|
127
114
|
else
|
128
115
|
false
|
@@ -130,48 +117,213 @@ class Rexslt
|
|
130
117
|
end
|
131
118
|
|
132
119
|
unless r.any? then
|
133
|
-
node =
|
134
|
-
read_node(node, element, doc_element, indent
|
120
|
+
node = x.element 'xsl:otherwise'
|
121
|
+
read_node(node, element, doc_element, indent) if node
|
135
122
|
end
|
136
123
|
|
137
124
|
end
|
138
125
|
|
139
|
-
def copy_of(element, x, doc_element, indent
|
126
|
+
def copy_of(element, x, doc_element, indent)
|
140
127
|
|
141
|
-
indent_element(element, x, doc_element, indent,
|
128
|
+
indent_element(element, x, doc_element, indent, indent - 1) do
|
142
129
|
doc_element.add element
|
143
130
|
end
|
144
131
|
|
145
132
|
end
|
146
133
|
|
147
|
-
def
|
134
|
+
def element(element, x, doc_element, indent)
|
135
|
+
|
136
|
+
indent_before(element, x, doc_element, indent + 1) if @indent == true
|
137
|
+
name = x.attributes[:name]
|
138
|
+
new_element = Rexle::Element.new name
|
139
|
+
doc_element.add new_element
|
140
|
+
read_node(x, element, new_element, indent)
|
141
|
+
indent_after(element, x, doc_element, indent) if @indent == true
|
142
|
+
end
|
143
|
+
|
144
|
+
def for_each(element, x, doc_element, indent)
|
145
|
+
|
146
|
+
xpath = x.attributes[:select]
|
147
|
+
nodes = element.match xpath
|
148
|
+
|
149
|
+
# check for sort
|
150
|
+
sort_node = x.element 'xsl:sort'
|
151
|
+
|
152
|
+
if sort_node then
|
153
|
+
|
154
|
+
sort_field = sort_node.attributes[:select]
|
155
|
+
order = sort_node.attributes[:order]
|
156
|
+
sort_node.parent.delete sort_node
|
157
|
+
|
158
|
+
nodes = nodes.sort_by do |node|
|
159
|
+
|
160
|
+
r = node.element sort_field
|
161
|
+
if r.is_a? Rexle::Element then
|
162
|
+
r.text
|
163
|
+
else
|
164
|
+
# it's a string
|
165
|
+
r
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
nodes.reverse! if order.downcase == 'descending'
|
170
|
+
end
|
171
|
+
|
172
|
+
nodes.each {|node| read_node(x, node, doc_element, indent)}
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
def if(element, x, doc_element, indent)
|
177
|
+
|
178
|
+
condition = x.attributes[:test]
|
179
|
+
node = element.element, condition
|
180
|
+
read_node(x, element, doc_element, indent) if node and node == true
|
181
|
+
end
|
182
|
+
|
183
|
+
def indent_before(element, x, doc_element, indent)
|
148
184
|
text = ''
|
149
|
-
unless doc_element.texts.empty? and doc_element.texts.last.nil? then
|
150
|
-
text = indent unless doc_element.texts.last.to_s[/^\n\s/m]
|
185
|
+
unless doc_element.texts.empty? and doc_element.texts.last.nil? then
|
186
|
+
text = ' ' * (indent - 1) unless doc_element.texts.last.to_s[/^\n\s/m]
|
151
187
|
else
|
152
|
-
text = "\n" + indent
|
188
|
+
text = "\n" + ' ' * (indent - 1)
|
153
189
|
end
|
154
|
-
|
155
|
-
|
190
|
+
text = ' ' if text.empty?
|
191
|
+
|
192
|
+
doc_element.add_text text if text
|
156
193
|
end
|
157
194
|
|
158
|
-
def indent_after(element, x, doc_element,
|
195
|
+
def indent_after(element, x, doc_element, prev_indent)
|
159
196
|
if @indent == true then
|
160
|
-
doc_element.
|
197
|
+
doc_element.add_text "\n" + ' ' * (prev_indent > 0 ? prev_indent - 1 : prev_indent)
|
161
198
|
end
|
162
199
|
end
|
163
200
|
|
164
201
|
def indent_element(element, x, doc_element, indent, previous_indent)
|
165
|
-
indent_before(element, x, doc_element, indent
|
202
|
+
indent_before(element, x, doc_element, indent) if @indent == true
|
166
203
|
yield
|
167
|
-
indent_after(element, x, doc_element,
|
204
|
+
indent_after(element, x, doc_element, previous_indent) if @indent == true
|
205
|
+
end
|
206
|
+
|
207
|
+
def padding(element,raw_indent, x)
|
208
|
+
# if there is any other elements in doc_element don't check for an indent!!!!
|
209
|
+
#puts doc_element.elements.count
|
210
|
+
indent = 0
|
211
|
+
indent = raw_indent + 1 if element.texts.length <= 0
|
212
|
+
x.to_s.strip.length > 0 ? ' ' * indent : ''
|
213
|
+
end
|
214
|
+
|
215
|
+
def read_node(template_node, element, doc_element, indent)
|
216
|
+
|
217
|
+
procs = {"Rexle::Element" => :read_raw_element, "String" => :read_raw_text}
|
218
|
+
|
219
|
+
template_node.each do |x|
|
220
|
+
method(procs[x.class.to_s]).call(element, x, doc_element, indent)
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
224
|
+
|
225
|
+
# element: xml source element, x: xsl element, doc_element: current destination xml element
|
226
|
+
#
|
227
|
+
def read_raw_text(element, x, doc_element, raw_indent)
|
228
|
+
#val = @indent == true ? padding(doc_element, raw_indent, x) : ''
|
229
|
+
if x.to_s.strip.length > 0 then
|
230
|
+
val = x.to_s #
|
231
|
+
doc_element.add_element val
|
232
|
+
end
|
168
233
|
end
|
169
234
|
|
170
|
-
def
|
235
|
+
def read_raw_element(element, x, doc_element, indent)
|
171
236
|
|
172
|
-
|
173
|
-
|
174
|
-
|
237
|
+
method_name = x.name.gsub(/-/,'_').to_sym
|
238
|
+
|
239
|
+
if @xsl_methods.include? method_name then
|
240
|
+
method(method_name).call(element, x, doc_element, indent)
|
241
|
+
else
|
242
|
+
|
243
|
+
previous_indent = indent
|
244
|
+
la = x.name
|
245
|
+
|
246
|
+
|
247
|
+
if x.children.length > 0 then
|
248
|
+
|
249
|
+
new_indent = indent + 1
|
250
|
+
new_element = x.clone
|
251
|
+
|
252
|
+
indent_before(element, x, doc_element, new_indent) if @indent == true
|
253
|
+
|
254
|
+
doc_element.add new_element
|
255
|
+
|
256
|
+
read_node(x, element, new_element, new_indent)
|
257
|
+
indent_after(element, x, doc_element, previous_indent) if @indent == true
|
258
|
+
|
259
|
+
else
|
260
|
+
|
261
|
+
unless doc_element.children.length > 0
|
262
|
+
indent_before(element, x, doc_element, indent) if @indent == true
|
263
|
+
end
|
264
|
+
|
265
|
+
val = @indent == true ? ' ' + x.to_s : x.to_s
|
266
|
+
doc_element.add val
|
267
|
+
|
268
|
+
if @indent == true then
|
269
|
+
indent_after(element, x, doc_element, previous_indent)
|
270
|
+
end
|
271
|
+
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def text(element, x, doc_element, indent)
|
277
|
+
val = @indent == true ? padding(doc_element, indent, x) : ''
|
278
|
+
val += x.text
|
279
|
+
doc_element.add_element val
|
175
280
|
end
|
176
281
|
|
282
|
+
def value_of(element, x, doc_element, indent)
|
283
|
+
field = x.attributes[:select]
|
284
|
+
o = field == '.' ? element.text : element.text(field)
|
285
|
+
doc_element.add_element o.to_s
|
286
|
+
end
|
287
|
+
|
288
|
+
def xslt_transform(xsl, xml)
|
289
|
+
|
290
|
+
doc_xml = Rexle.new xml
|
291
|
+
@doc_xsl = Rexle.new xsl
|
292
|
+
|
293
|
+
@doc = Rexle.new '<root/>'
|
294
|
+
indent = 0
|
295
|
+
|
296
|
+
previous_indent = 0
|
297
|
+
@xsl_methods = [:apply_templates, :value_of, :element, :if, :choose,
|
298
|
+
:when, :copy_of, :attribute, :for_each, :text]
|
299
|
+
|
300
|
+
strip_space = @doc_xsl.element "xsl:strip-space/attribute::elements"
|
301
|
+
if strip_space then
|
302
|
+
elements = strip_space.value
|
303
|
+
elements.split.each do |element|
|
304
|
+
nodes = doc_xml.root.xpath "//" + element + "[text()]"
|
305
|
+
a = nodes.select {|x| x.text.to_s.strip.empty?}
|
306
|
+
a.each {|node| node.parent.delete node}
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
h = @doc_xsl.xpath("xsl:output/attribute::*").inject({})\
|
311
|
+
{|r,x| r.merge(x.name.to_sym => x.value)}
|
312
|
+
|
313
|
+
@indent = true if h and h[:indent] == 'yes'
|
314
|
+
|
315
|
+
# fetch the templates
|
316
|
+
@templates = @doc_xsl.xpath('xsl:template').inject({}) do |r,x|
|
317
|
+
r.merge(x.attributes[:match] => x)
|
318
|
+
end
|
319
|
+
|
320
|
+
# using the 1st template
|
321
|
+
xpath = String.new @templates.to_a[0][0]
|
322
|
+
|
323
|
+
if doc_xml.root.name == xpath then
|
324
|
+
read_node(@templates.to_a[0][-1], doc_xml, @doc.root, indent)
|
325
|
+
end
|
326
|
+
|
327
|
+
end
|
328
|
+
|
177
329
|
end
|
metadata
CHANGED
@@ -2,15 +2,15 @@
|
|
2
2
|
name: rexslt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.3.0
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
8
|
-
|
7
|
+
authors:
|
8
|
+
- James Robertson
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
13
|
+
date: 2011-11-21 00:00:00 +00:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|