1 # $Id: xml.rb,v 1.8 2012/04/17 02:49:40 machan Exp $
2
3 require 'tmdoc/tmstd'
4 require 'tmdoc/tmstd/treeable'
5 require 'tmdoc/tmstd/lsm'
6
7
8 module TmStd
9
10 module Xml
11 XML_DECL = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>'
12 XINCLUDE_XMLNS = 'http://www.w3.org/2001/XInclude'
13
14
15 module Abstraction
16
17 class Unit < TmStd::Lsm::Product::Abstract
18 include TmStd::Treeable
19
20
21 def to_s
22 raise TmStd::Exception::SubclassResponsibility
23 end
24
25
26 def __print_tree__(
27 io, depth, in_verbatim, rejector, selector, do_sort, comparator
28 )
29 Assertion.kind_of depth, Integer
30 Assertion.boolean in_verbatim
31 Assertion.nil rejector, "Not implemented"
32 Assertion.nil selector, "Not implemented"
33 Assertion.boolean do_sort
34 Assertion.nil comparator, "Not implemented"
35
36 Assertion.assert ! do_sort, "Not implemented"
37
38 if in_verbatim
39 io << self.to_s
40 else
41 __print_tree_indent__(io, depth)
42
43 io << self.to_s << "\n"
44 end
45
46 nil
47 end
48
49
50 def __print_tree_indent__(io, depth)
51 Assertion.kind_of depth, Integer
52
53 if depth > 0
54 io << "\t" * depth
55 end
56
57 nil
58 end
59 private :__print_tree_indent__
60 end
61
62
63
64 class TextUnit < Unit
65 def each(&block); end # Nothing to do.
66 def children; EMPTY_SEQ_OF_UNIT; end # Empty sequence.
67 def empty?; true; end # Anytime empty.
68
69
70 def to_s
71 raise TmStd::Exception::SubclassResponsibility
72 end
73 end
74
75
76
77 class Identifier < TmStd::Lsm::Product::Abstract
78 attr_reader :name, :xmlns
79 attr_reader :hash
80
81
82 def initialize(name, xmlns = nil)
83 Assertion.kind_of name, Symbol
84 Assertion.opt_kind_of xmlns, Symbol
85
86 @name = name
87 @xmlns = xmlns
88
89 @name.freeze
90 @xmlns.freeze
91
92 @hash = self.to_s.hash
93 end
94
95
96 def to_s
97 str = if @xmlns
98 format("%s:%s", @xmlns.to_s, @name.to_s)
99 else
100 @name.to_s
101 end
102
103 Assertion.kind_of str, String
104 end
105
106
107 def eql?(other)
108 Assertion.kind_of other, self.class
109
110 result = @name == other.name && @xmlns == other.xmlns
111
112 Assertion.boolean result
113 end
114
115
116 def freeze_equality!
117 nil # Nop, already frozen.
118 end
119 end
120
121
122 end
123
124
125
126 class Text < Abstraction::TextUnit
127 attr_reader :text
128
129
130 def initialize(str)
131 Assertion.kind_of str, String
132
133 super()
134
135 @text = str
136 end
137
138
139 def to_s
140 str = @text.dup
141 str.gsub!(/&/, '&')
142 str.gsub!(/\"/, '"')
143 str.gsub!(/</, '<')
144 str.gsub!(/>/, '>')
145
146 Assertion.kind_of str, String
147 end
148 end
149
150
151
152 class RawString < String; end
153
154
155
156 class RawText < Abstraction::TextUnit
157 attr_reader :text
158
159
160 def initialize(str)
161 Assertion.kind_of str, String
162
163 super()
164
165 @text = RawString.new str
166 end
167
168
169 def to_s
170 Assertion.kind_of @text, String
171 end
172 end
173
174
175
176 class Tag < Abstraction::Identifier; end
177
178
179
180 module Attribute
181
182 class Key < Abstraction::Identifier; end
183
184
185
186 class Map < TmStd::Lsm::Collection::Map::Abstract
187 LSM_DOMAIN_CLASS = Attribute::Key
188 LSM_RANGE_CLASS = String
189
190
191 def to_s
192 str = self.map { |key, value|
193 format(
194 "%s=\"%s\"",
195
196 key.to_s,
197
198 case value
199 when RawString
200 value
201 when String
202 val = value.dup
203
204 val.gsub!(/%/, '%%')
205 val.gsub!(/ /, '%20')
206 val.gsub!(/\"/, '%22')
207 val.gsub!(/#/, '%23')
208 val.gsub!(/&/, '%26')
209 val.gsub!(/\//, '%2f')
210 val.gsub!(/:/, '%3a')
211 val.gsub!(/</, '%3c')
212 val.gsub!(/>/, '%3e')
213
214 val
215 else
216 Assertion.abort(
217 "Unexpected value: '%s' (%s)",
218 value.to_s, value.class.to_s
219 )
220 end
221 )
222 }.join(' ')
223
224 Assertion.kind_of str, String
225 end
226 end
227
228 EMPTY_MAP = Map.new
229
230 end
231
232
233
234
235 class SeqOfUnit < TmStd::Lsm::Collection::Sequence::Abstract
236 LSM_ELEMENT_CLASS = Abstraction::Unit
237 end
238
239 EMPTY_SEQ_OF_UNIT = SeqOfUnit.new
240
241
242
243 class Element < Abstraction::Unit
244 include Enumerable
245
246 attr_reader :tag,
247 :xmlns,
248 :attribute_map,
249 :seq_of_unit
250
251
252 alias :attributes :attribute_map
253 alias :children :seq_of_unit
254
255
256 def initialize(tag, attrs = {}, opts = {}, &block)
257 Assertion.kind_of tag, Symbol
258 Assertion.kind_of attrs, Hash
259 Assertion.kind_of opts, Hash
260
261 super()
262
263 @tag = tag
264 @xmlns = nil
265 @in_verbatim = false
266
267 for key, val in opts
268 Assertion.kind_of key, Symbol
269
270 case key
271 when :xmlns
272 Assertion.kind_of val, Symbol
273
274 @xmlns = val
275 when :in_verbatim
276 Assertion.boolean val
277
278 @in_verbatim = val
279 else
280 Assertion.abort "Unknown option: %s", key
281 end
282 end
283
284 @attribute_map =
285 unless attrs.empty?
286 Attribute::Map.new(
287 attrs.inject({}) { |hash_of_attr, pair|
288 Assertion.tuple_of pair, [Object, String]
289 key, val = pair
290
291 hash_key =
292 case key
293 when Symbol
294 Attribute::Key.new(key)
295 when Attribute::Key
296 key
297 else
298 Assertion.abort(
299 "Expected Symbol or Key, " +
300 "but attribute key <%s> was a <%s>",
301 key.to_s, key.class.to_s
302 )
303 end
304
305 hash_of_attr.merge(hash_key => val) {
306 Assertion.abort(
307 "Duplicated attribute key: %s", key.to_s
308 )
309 }
310 }
311 )
312 else
313 Attribute::EMPTY_MAP
314 end
315
316 @seq_of_unit = if block
317 array_of_unit = block.call
318 Assertion.kind_of array_of_unit, Array
319
320 unless array_of_unit.empty?
321 SeqOfUnit.new(array_of_unit)
322 else
323 EMPTY_SEQ_OF_UNIT
324 end
325 else
326 EMPTY_SEQ_OF_UNIT
327 end
328 end
329
330
331 def in_verbatim?
332 Assertion.boolean @in_verbatim
333 end
334
335
336 def empty?
337 Assertion.boolean self.children.empty?
338 end
339
340
341 def each(&block)
342 self.children.each(&block)
343
344 nil
345 end
346
347
348 def to_s(has_child = false)
349 str = format("<%s%s%s",
350 if self.xmlns
351 format "%s:%s", self.xmlns.to_s, self.tag.to_s
352 else
353 self.tag.to_s
354 end,
355
356 if self.attributes.empty?
357 ''
358 else
359 ' ' + self.attributes.to_s
360 end,
361
362 if has_child
363 '>'
364 else
365 '/>'
366 end
367 )
368
369 Assertion.kind_of str, String
370 end
371
372
373 def __print_tree__(
374 io, depth, in_verbatim, rejector, selector, do_sort, comparator
375 )
376 Assertion.kind_of depth, Integer
377 Assertion.boolean in_verbatim
378 Assertion.nil rejector, "Not implemented"
379 Assertion.nil selector, "Not implemented"
380 Assertion.boolean do_sort
381 Assertion.nil comparator, "Not implemented"
382
383 Assertion.assert ! do_sort, "Not implemented"
384
385 has_child = (! self.empty?)
386
387 unless in_verbatim
388 __print_tree_indent__(io, depth)
389 end
390
391 self.__print_tree_enter__(io, has_child)
392
393 if has_child
394 unless in_verbatim || self.in_verbatim?
395 io << "\n"
396 end
397
398 for child in self
399 Assertion.kind_of(child, Abstraction::Unit,
400 "The child of %s '%s' isn't XML unit",
401 self.class,
402 self.to_s
403 )
404
405 child.__print_tree__(
406 io, depth + 1, in_verbatim || self.in_verbatim?,
407 rejector, selector, do_sort, comparator
408 )
409 end
410
411 unless in_verbatim || self.in_verbatim?
412 __print_tree_indent__(io, depth)
413 end
414 end
415
416 self.__print_tree_leave__(io, has_child)
417
418 unless in_verbatim
419 io << "\n"
420 end
421
422 nil
423 end
424
425
426 def __print_tree_enter__(io, has_child)
427 io << self.to_s(has_child)
428
429 nil
430 end
431
432
433 def __print_tree_leave__(io, has_child)
434 if has_child
435 io.printf("</%s>",
436 if self.xmlns
437 format "%s:%s", self.xmlns, self.tag
438 else
439 self.tag
440 end
441 )
442 end
443
444 nil
445 end
446 end
447
448
449
450 class Document < Element
451 attr_reader :doc_type
452
453
454 def initialize(tag, doc_type, attrs = {}, opts = {}, &block)
455 Assertion.kind_of tag, Symbol
456 Assertion.kind_of doc_type, String
457 Assertion.kind_of attrs, Hash
458 Assertion.kind_of opts, Hash
459
460 super(tag, attrs, opts, &block)
461
462 @doc_type = doc_type
463 end
464
465
466 def __print_tree_enter__(io, has_child)
467 io.printf "%s\n\n%s\n\n\n", XML_DECL, self.doc_type
468
469 super
470
471 nil
472 end
473 end
474
475
476
477 class Include < Element
478 def initialize(path)
479 Assertion.kind_of path, String
480
481 super(
482 :include,
483 {
484 Attribute::Key.new(:xi, :xmlns) =>
485 RawString.new(XINCLUDE_XMLNS),
486 :href => RawString.new(path)
487 },
488 :xmlns => :xi
489 )
490 end
491 end
492
493 end # TmStd::Xml
494
495 end # TmStd