rexslt 0.1.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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