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.
Files changed (2) hide show
  1. data/lib/rexslt.rb +259 -107
  2. metadata +4 -4
data/lib/rexslt.rb CHANGED
@@ -1,128 +1,115 @@
1
- require 'rexml/document'
1
+ #!/usr/bin/env ruby
2
2
 
3
+ # file: rexslt.rb
3
4
 
4
- class Rexslt
5
- include REXML
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
- # fetch the templates
23
- @templates = XPath.match(doc_xsl.root, 'xsl:template').inject({}) do |r,x|
24
- r.merge(x.attribute('match').value => x)
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
- # using the 1st template
28
- xpath = String.new @templates.to_a[0][0]
29
- XPath.match(doc_xml, xpath).each_with_index do |x,i|
30
- read_node @templates.to_a[0][-1], x, @doc.root, indent.clone, previous_indent.clone
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 read_node(template_node, element, doc_element, indent, previous_indent)
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
- method_name = x.name.gsub(/-/,'_').to_sym
62
- if @xsl_methods.include? method_name then
52
+ field = x.attributes[:select]
63
53
 
64
- method(method_name).call(element, x, doc_element, indent, previous_indent)
65
- else
54
+ node = element.element field
66
55
 
67
- if x.has_elements? then
68
-
69
- new_element = Element.new x.name.to_s
56
+ return unless node
70
57
 
71
- previous_indent = indent.clone
72
- indent = indent_before(element, x, doc_element, indent, previous_indent) if @indent == true
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
- else
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
- def value_of(element, x, doc_element, indent, previous_indent)
95
-
96
- field = x.attribute('select').value.to_s
97
- o = element.text(field)
98
- doc_element.add REXML::Text.new(o.to_s)
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 element(element, x, doc_element, indent, previous_indent)
103
-
104
- name = x.attribute('name').value.to_s
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
- def if(element, x, doc_element, indent, previous_indent)
94
+
95
+ def attribute(element, x, doc_element, indent)
111
96
 
112
- condition = x.attribute('test').value.to_s
113
- node = XPath.first(element, condition)
114
- read_node(x, element, doc_element, indent, previous_indent) if node and node == true
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, previous_indent)
102
+ def choose(element, x, doc_element, indent)
118
103
 
119
104
  #get the when clause
120
105
 
121
- r = XPath.match(x, "xsl:when").map do |xsl_node|
122
- condition = xsl_node.attribute('test').value.to_s
123
- node = XPath.first(element, condition)
124
- if node and node == true
125
- read_node(xsl_node, element, doc_element, indent, previous_indent)
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 = XPath.first(x, 'xsl:otherwise')
134
- read_node(node, element, doc_element, indent, previous_indent) if node
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, previous_indent)
126
+ def copy_of(element, x, doc_element, indent)
140
127
 
141
- indent_element(element, x, doc_element, indent, previous_indent) do
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 indent_before(element, x, doc_element, indent, previous_indent)
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
- (doc_element.add REXML::Text.new text, raw=true) if text
155
- indent += ' '
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, indent, previous_indent)
195
+ def indent_after(element, x, doc_element, prev_indent)
159
196
  if @indent == true then
160
- doc_element.add REXML::Text.new "\n" + previous_indent[0..-3], raw=true
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, previous_indent) if @indent == true
202
+ indent_before(element, x, doc_element, indent) if @indent == true
166
203
  yield
167
- indent_after(element, x, doc_element, indent, previous_indent) if @indent == true
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 attribute(element, x, doc_element, indent, previous_indent)
235
+ def read_raw_element(element, x, doc_element, indent)
171
236
 
172
- name = x.attribute('name').value
173
- value = x.text.to_s
174
- doc_element.add_attribute name, value
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.1.4
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-04-13 00:00:00 +01:00
13
+ date: 2011-11-21 00:00:00 +00:00
14
14
  default_executable:
15
15
  dependencies: []
16
16