feedtools 0.1.0 → 0.2.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 (37) hide show
  1. data/CHANGELOG +11 -0
  2. data/lib/feed_tools.rb +2496 -810
  3. data/lib/feed_tools/vendor/builder.rb +2 -0
  4. data/lib/feed_tools/vendor/builder/blankslate.rb +2 -0
  5. data/lib/feed_tools/vendor/builder/xmlbase.rb +2 -1
  6. data/lib/feed_tools/vendor/builder/xmlevents.rb +2 -0
  7. data/lib/feed_tools/vendor/builder/xmlmarkup.rb +4 -2
  8. data/lib/feed_tools/vendor/htree.rb +97 -0
  9. data/lib/feed_tools/vendor/htree/container.rb +10 -0
  10. data/lib/feed_tools/vendor/htree/context.rb +67 -0
  11. data/lib/feed_tools/vendor/htree/display.rb +27 -0
  12. data/lib/feed_tools/vendor/htree/doc.rb +149 -0
  13. data/lib/feed_tools/vendor/htree/elem.rb +262 -0
  14. data/lib/feed_tools/vendor/htree/encoder.rb +163 -0
  15. data/lib/feed_tools/vendor/htree/equality.rb +218 -0
  16. data/lib/feed_tools/vendor/htree/extract_text.rb +37 -0
  17. data/lib/feed_tools/vendor/htree/fstr.rb +33 -0
  18. data/lib/feed_tools/vendor/htree/gencode.rb +97 -0
  19. data/lib/feed_tools/vendor/htree/htmlinfo.rb +672 -0
  20. data/lib/feed_tools/vendor/htree/inspect.rb +108 -0
  21. data/lib/feed_tools/vendor/htree/leaf.rb +94 -0
  22. data/lib/feed_tools/vendor/htree/loc.rb +367 -0
  23. data/lib/feed_tools/vendor/htree/modules.rb +48 -0
  24. data/lib/feed_tools/vendor/htree/name.rb +124 -0
  25. data/lib/feed_tools/vendor/htree/output.rb +207 -0
  26. data/lib/feed_tools/vendor/htree/parse.rb +407 -0
  27. data/lib/feed_tools/vendor/htree/raw_string.rb +124 -0
  28. data/lib/feed_tools/vendor/htree/regexp-util.rb +15 -0
  29. data/lib/feed_tools/vendor/htree/rexml.rb +130 -0
  30. data/lib/feed_tools/vendor/htree/scan.rb +166 -0
  31. data/lib/feed_tools/vendor/htree/tag.rb +111 -0
  32. data/lib/feed_tools/vendor/htree/template.rb +909 -0
  33. data/lib/feed_tools/vendor/htree/text.rb +115 -0
  34. data/lib/feed_tools/vendor/htree/traverse.rb +465 -0
  35. data/rakefile +1 -1
  36. data/test/rss_test.rb +97 -0
  37. metadata +30 -1
@@ -9,5 +9,7 @@
9
9
  # above copyright notice is included.
10
10
  #++
11
11
 
12
+ # :stopdoc:
12
13
  require 'builder/xmlmarkup'
13
14
  require 'builder/xmlevents'
15
+ # :startdoc:
@@ -8,6 +8,7 @@
8
8
  # above copyright notice is included.
9
9
  #++
10
10
 
11
+ # :stopdoc:
11
12
  module Builder #:nodoc:
12
13
 
13
14
  # BlankSlate provides an abstract base class with no predefined
@@ -51,3 +52,4 @@ class Object #:nodoc:
51
52
  end
52
53
  end
53
54
  end
55
+ # :startdoc:
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
-
2
+ # :stopdoc:
3
3
  require 'builder/blankslate'
4
4
 
5
5
  module Builder #:nodoc:
@@ -141,3 +141,4 @@ module Builder #:nodoc:
141
141
  end
142
142
  end
143
143
  end
144
+ # :startdoc:
@@ -9,6 +9,7 @@
9
9
  # above copyright notice is included.
10
10
  #++
11
11
 
12
+ # :stopdoc:
12
13
  require 'builder/xmlmarkup'
13
14
 
14
15
  module Builder
@@ -61,3 +62,4 @@ module Builder
61
62
  end
62
63
 
63
64
  end
65
+ # :startdoc:
@@ -11,9 +11,10 @@
11
11
  # Provide a flexible and easy to use Builder for creating XML markup.
12
12
  # See XmlBuilder for usage details.
13
13
 
14
+ # :stopdoc:
14
15
  require 'builder/xmlbase'
15
16
 
16
- module Builder
17
+ module Builder #:nodoc:
17
18
 
18
19
  # Create XML markup easily. All (well, almost all) methods sent to
19
20
  # an XmlMarkup object will be translated to the equivalent XML
@@ -157,7 +158,7 @@ module Builder
157
158
  # xml.stong("text")
158
159
  # }
159
160
  #
160
- class XmlMarkup < XmlBase
161
+ class XmlMarkup < XmlBase #:nodoc:
161
162
 
162
163
  # Create an XML markup builder. Parameters are specified by an
163
164
  # option hash.
@@ -295,3 +296,4 @@ module Builder
295
296
  end
296
297
 
297
298
  end
299
+ # :startdoc:
@@ -0,0 +1,97 @@
1
+ #
2
+ # = htree.rb
3
+ #
4
+ # HTML/XML document tree
5
+ #
6
+ # Author:: Tanaka Akira <akr@m17n.org>
7
+ #
8
+ # == Features
9
+ #
10
+ # - Permissive unified HTML/XML parser
11
+ # - byte-to-byte round-tripping unparser
12
+ # - XML namespace support
13
+ # - Dedicated class for escaped string. This ease sanitization.
14
+ # - XHTML/XML generator
15
+ # - template engine: link:files/htree/template_rb.html
16
+ # - recursive template expansion
17
+ # - REXML tree generator: link:files/htree/rexml_rb.html
18
+ #
19
+ # == Example
20
+ #
21
+ # The following one-liner prints parsed tree object.
22
+ #
23
+ # % ruby -rhtree -e 'pp HTree(ARGF)' html-file
24
+ #
25
+ # The following two-line script convert HTML to XHTML.
26
+ #
27
+ # require 'htree'
28
+ # HTree(STDIN).display_xml
29
+ #
30
+ # The conversion method to REXML is provided as to_rexml.
31
+ #
32
+ # HTree(...).to_rexml
33
+ #
34
+ # == Module/Class Hierarchy
35
+ #
36
+ # * HTree
37
+ # * HTree::Name
38
+ # * HTree::Context
39
+ # * HTree::Location
40
+ # * HTree::Node
41
+ # * HTree::Doc
42
+ # * HTree::Elem
43
+ # * HTree::Text
44
+ # * HTree::XMLDecl
45
+ # * HTree::DocType
46
+ # * HTree::ProcIns
47
+ # * HTree::Comment
48
+ # * HTree::BogusETag
49
+ # * HTree::Error
50
+ #
51
+ # == Method Summary
52
+ #
53
+ # HTree provides following methods.
54
+ #
55
+ # - Parsing Methods
56
+ # - HTree(<i>html_string</i>) -> HTree::Doc
57
+ # - HTree.parse(<i>input</i>) -> HTree::Doc
58
+ #
59
+ # - Generation Methods
60
+ # - HTree::Node#display_xml -> STDOUT
61
+ # - HTree::Node#display_xml(<i>out</i>) -> <i>out</i>
62
+ # - HTree::Node#display_xml(<i>out</i>, <i>encoding</i>) -> <i>out</i>
63
+ # - HTree::Text#to_s -> String
64
+ #
65
+ # - Template Methods
66
+ # - HTree.expand_template{<i>template_string</i>} -> STDOUT
67
+ # - HTree.expand_template(<i>out</i>){<i>template_string</i>} -> <i>out</i>
68
+ # - HTree.expand_template(<i>out</i>, <i>encoding</i>){<i>template_string</i>} -> <i>out</i>
69
+ # - HTree.compile_template(<i>template_string</i>) -> Module
70
+ # - HTree{<i>template_string</i>} -> HTree::Doc
71
+ #
72
+ # - Traverse Methods
73
+ # - HTree::Elem#attributes -> Hash[HTree::Name -> HTree::Text]
74
+ # - HTree::Elem::Location#attributes -> Hash[HTree::Name -> HTree::Location]
75
+ #
76
+ # - Predicate Methods
77
+ # - HTree::Traverse#doc? -> true or false
78
+ # - HTree::Traverse#elem? -> true or false
79
+ # - HTree::Traverse#text? -> true or false
80
+ # - HTree::Traverse#xmldecl? -> true or false
81
+ # - HTree::Traverse#doctype? -> true or false
82
+ # - HTree::Traverse#procins? -> true or false
83
+ # - HTree::Traverse#comment? -> true or false
84
+ # - HTree::Traverse#bogusetag? -> true or false
85
+ #
86
+ # - REXML Tree Generator
87
+ # - HTree::Node#to_rexml -> REXML::Child
88
+
89
+ require 'htree/parse'
90
+ require 'htree/extract_text'
91
+ require 'htree/equality'
92
+ require 'htree/inspect'
93
+ require 'htree/display'
94
+ require 'htree/loc'
95
+ require 'htree/traverse'
96
+ require 'htree/template'
97
+ require 'htree/rexml'
@@ -0,0 +1,10 @@
1
+ # :stopdoc:
2
+ require 'htree/modules'
3
+
4
+ module HTree::Container # :nodoc:
5
+ # +children+ returns children nodes as an array.
6
+ def children
7
+ @children.dup
8
+ end
9
+ end
10
+ # :startdoc:
@@ -0,0 +1,67 @@
1
+ # :stopdoc:
2
+ module HTree
3
+ class Context
4
+ DefaultNamespaces = {'xml'=>'http://www.w3.org/XML/1998/namespace'}
5
+ DefaultNamespaces.default = ""
6
+ DefaultNamespaces.freeze
7
+
8
+ # The optional argument `namespaces' should be a hash or nil.
9
+ # HTree::DefaultNamespaces is used if nil is specified.
10
+ #
11
+ # If it is a hash, its key should be nil or a string.
12
+ # nil means default namespace.
13
+ # The string means some prefix which must not be empty.
14
+ #
15
+ # The hash value should be a string.
16
+ # The empty string "" means unbound namespace.
17
+ def initialize(namespaces=nil)
18
+ namespaces ||= DefaultNamespaces
19
+ namespaces.each_pair {|k, v|
20
+ check_namespace_prefix(k)
21
+ check_namespace_uri(v)
22
+ }
23
+ namespaces = namespaces.dup.freeze unless namespaces.frozen?
24
+ @namespaces = namespaces
25
+ end
26
+ attr_reader :namespaces
27
+
28
+ # return a namespace URI corresponding to _prefix_.
29
+ # It returns nil if _prefix_ is not defined.
30
+ def namespace_uri(prefix)
31
+ @namespaces[prefix]
32
+ end
33
+
34
+ # generate a new Context object which namespaces are substituted by
35
+ # a hash _declared_namespaces_.
36
+ def subst_namespaces(declared_namespaces)
37
+ namespaces = @namespaces.dup
38
+ declared_namespaces.each {|k, v|
39
+ check_namespace_prefix(k)
40
+ check_namespace_uri(v)
41
+ namespaces[k] = v
42
+ }
43
+ if namespaces == @namespaces
44
+ self
45
+ else
46
+ Context.new(namespaces)
47
+ end
48
+ end
49
+
50
+ private
51
+ def check_namespace_prefix(k)
52
+ unless (String === k && !k.empty?) || k == nil
53
+ raise ArgumentError, "invalid namespace prefix: #{k.inspect}"
54
+ end
55
+ end
56
+
57
+ def check_namespace_uri(v)
58
+ unless String === v
59
+ raise ArgumentError, "invalid namespace URI: #{v.inspect}"
60
+ end
61
+ end
62
+ end
63
+
64
+ DefaultContext = Context.new
65
+ HTMLContext = DefaultContext.subst_namespaces(nil=>"http://www.w3.org/1999/xhtml")
66
+ end
67
+ # :startdoc:
@@ -0,0 +1,27 @@
1
+ # :stopdoc:
2
+ require 'htree/output'
3
+
4
+ module HTree
5
+ module Node
6
+ # HTree::Node#display_xml prints the node as XML.
7
+ #
8
+ # The first optional argument, <i>out</i>,
9
+ # specifies output target.
10
+ # It should respond to <tt><<</tt>.
11
+ # If it is not specified, $stdout is used.
12
+ #
13
+ # The second optional argument, <i>encoding</i>,
14
+ # specifies output MIME charset (character encoding).
15
+ # If it is not specified, HTree::Encoder.internal_charset is used.
16
+ #
17
+ # HTree::Node#display_xml returns <i>out</i>.
18
+ def display_xml(out=$stdout, encoding=HTree::Encoder.internal_charset)
19
+ encoder = HTree::Encoder.new(encoding)
20
+ self.output(encoder, HTree::DefaultContext)
21
+ # don't call finish_with_xmldecl because self already has a xml decl.
22
+ out << encoder.finish
23
+ out
24
+ end
25
+ end
26
+ end
27
+ # :startdoc:
@@ -0,0 +1,149 @@
1
+ # :stopdoc:
2
+ require 'htree/modules'
3
+ require 'htree/container'
4
+
5
+ module HTree
6
+ class Doc
7
+ class << self
8
+ alias new! new
9
+ end
10
+
11
+ # The arguments should be a sequence of follows.
12
+ # [String object] specified string is converted to HTree::Text.
13
+ # [HTree::Node object] used as a child.
14
+ # [HTree::Doc object]
15
+ # used as children.
16
+ # It is expanded except HTree::XMLDecl and HTree::DocType objects.
17
+ # [Array of String, HTree::Node and HTree::Doc] used as children.
18
+ #
19
+ def Doc.new(*args)
20
+ children = []
21
+ args.each {|arg|
22
+ arg = arg.to_node if HTree::Location === arg
23
+ case arg
24
+ when Array
25
+ arg.each {|a|
26
+ a = a.to_node if HTree::Location === a
27
+ case a
28
+ when HTree::Doc
29
+ children.concat(a.children.reject {|c|
30
+ HTree::XMLDecl === c || HTree::DocType === c
31
+ })
32
+ when HTree::Node
33
+ children << a
34
+ when String
35
+ children << Text.new(a)
36
+ else
37
+ raise TypeError, "unexpected argument: #{arg.inspect}"
38
+ end
39
+ }
40
+ when HTree::Doc
41
+ children.concat(arg.children.reject {|c|
42
+ HTree::XMLDecl === c || HTree::DocType === c
43
+ })
44
+ when HTree::Node
45
+ children << arg
46
+ when String
47
+ children << Text.new(arg)
48
+ else
49
+ raise TypeError, "unexpected argument: #{arg.inspect}"
50
+ end
51
+ }
52
+ new!(children)
53
+ end
54
+
55
+ def initialize(children=[]) # :notnew:
56
+ @children = children.dup.freeze
57
+ unless @children.all? {|c| c.kind_of?(HTree::Node) and !c.kind_of?(HTree::Doc) }
58
+ unacceptable = @children.reject {|c| c.kind_of?(HTree::Node) and !c.kind_of?(HTree::Doc) }
59
+ unacceptable = unacceptable.map {|uc| uc.inspect }.join(', ')
60
+ raise TypeError, "Unacceptable document child: #{unacceptable}"
61
+ end
62
+ end
63
+
64
+ def get_subnode_internal(index) # :nodoc:
65
+ unless Integer === index
66
+ raise TypeError, "invalid index: #{index.inspect}"
67
+ end
68
+ if index < 0 || @children.length <= index
69
+ nil
70
+ else
71
+ @children[index]
72
+ end
73
+ end
74
+
75
+ # doc.subst_subnode(pairs) -> doc
76
+ #
77
+ # The argument _pairs_ should be a hash or an assocs.
78
+ # Its key should be an integer which means an index for children.
79
+ #
80
+ # Its value should be one of follows.
81
+ # [HTree::Node object] specified object is used as is.
82
+ # [String object] specified string is converted to HTree::Text
83
+ # [Array of above] specified HTree::Node and String is used in that order.
84
+ # [nil] delete corresponding node.
85
+ #
86
+ # d = HTree('<a/><b/><c/>')
87
+ # p d.subst_subnode({0=>HTree('<x/>'), 2=>HTree('<z/>')})
88
+ # p d.subst_subnode([[0,HTree('<x/>')], [2,HTree('<z/>')]])
89
+ # # =>
90
+ # #<HTree::Doc {emptyelem <x>} {emptyelem <b>} {emptyelem <z>}>
91
+ # #<HTree::Doc {emptyelem <x>} {emptyelem <b>} {emptyelem <z>}>
92
+ #
93
+ def subst_subnode(pairs)
94
+ hash = {}
95
+ pairs.each {|index, value|
96
+ unless Integer === index
97
+ raise TypeError, "invalid index: #{index.inspect}"
98
+ end
99
+ value = value.to_node if HTree::Location === value
100
+ case value
101
+ when Node
102
+ value = [value]
103
+ when String
104
+ value = [value]
105
+ when Array
106
+ value = value.dup
107
+ when nil
108
+ value = []
109
+ else
110
+ raise TypeError, "invalid value: #{value.inspect}"
111
+ end
112
+ value.map! {|v|
113
+ v = v.to_node if HTree::Location === v
114
+ case v
115
+ when Node
116
+ v
117
+ when String
118
+ Text.new(v)
119
+ else
120
+ raise TypeError, "invalid value: #{v.inspect}"
121
+ end
122
+ }
123
+ if !hash.include?(index)
124
+ hash[index] = []
125
+ end
126
+ hash[index].concat value
127
+ }
128
+
129
+ children_left = []
130
+ children = @children.dup
131
+ children_right = []
132
+
133
+ hash.keys.sort.each {|index|
134
+ value = hash[index]
135
+ if index < 0
136
+ children_left << value
137
+ elsif children.length <= index
138
+ children_right << value
139
+ else
140
+ children[index] = value
141
+ end
142
+ }
143
+
144
+ children = [children_left, children, children_right].flatten.compact
145
+ Doc.new(children)
146
+ end
147
+ end
148
+ end
149
+ # :startdoc:
@@ -0,0 +1,262 @@
1
+ # :stopdoc:
2
+ require 'htree/modules'
3
+ require 'htree/tag'
4
+ require 'htree/context'
5
+ require 'htree/container'
6
+
7
+ module HTree
8
+ class Elem
9
+ class << self
10
+ alias new! new
11
+ end
12
+
13
+ # The first argument _name_ should be an instance of String or HTree::Name.
14
+ #
15
+ # The rest of arguments should be a sequence of follows.
16
+ # [Hash object] used as attributes.
17
+ # [String object] specified string is converted to HTree::Text.
18
+ # [HTree::Node object] used as a child.
19
+ # [HTree::Doc object]
20
+ # used as children.
21
+ # It is expanded except HTree::XMLDecl and HTree::DocType objects.
22
+ # [Array of String, HTree::Node, HTree::Doc] used as children.
23
+ # [HTree::Context object]
24
+ # used as as context which represents XML namespaces.
25
+ # This should apper once at most.
26
+ #
27
+ # HTree::Location object is accepted just as HTree::Node.
28
+ #
29
+ # If the rest arguments consists only
30
+ # Hash and HTree::Context, empty element is created.
31
+ #
32
+ # p HTree::Elem.new("e").empty_element? # => true
33
+ # p HTree::Elem.new("e", []).empty_element? # => false
34
+ def Elem.new(name, *args)
35
+ attrs = []
36
+ children = []
37
+ context = nil
38
+ args.each {|arg|
39
+ arg = arg.to_node if HTree::Location === arg
40
+ case arg
41
+ when Context
42
+ raise ArgumentError, "multiple context" if context
43
+ context = arg
44
+ when Hash
45
+ arg.each {|k, v| attrs << [k, v] }
46
+ when Array
47
+ arg.each {|a|
48
+ a = a.to_node if HTree::Location === a
49
+ case a
50
+ when HTree::Doc
51
+ children.concat(a.children.reject {|c|
52
+ HTree::XMLDecl === c || HTree::DocType === c
53
+ })
54
+ when HTree::Node
55
+ children << a
56
+ when String
57
+ children << Text.new(a)
58
+ else
59
+ raise TypeError, "unexpected argument: #{arg.inspect}"
60
+ end
61
+ }
62
+ when HTree::Doc
63
+ children.concat(arg.children.reject {|c|
64
+ HTree::XMLDecl === c || HTree::DocType === c
65
+ })
66
+ when HTree::Node
67
+ children << arg
68
+ when String
69
+ children << Text.new(arg)
70
+
71
+ else
72
+ raise TypeError, "unexpected argument: #{arg.inspect}"
73
+ end
74
+ }
75
+ context ||= DefaultContext
76
+ if children.empty? && args.all? {|arg| Hash === arg || Context === arg }
77
+ children = nil
78
+ end
79
+ new!(STag.new(name, attrs, context), children)
80
+ end
81
+
82
+ def initialize(stag, children=nil, etag=nil) # :notnew:
83
+ unless stag.class == STag
84
+ raise TypeError, "HTree::STag expected: #{stag.inspect}"
85
+ end
86
+ unless !children || children.all? {|c| c.kind_of?(HTree::Node) and !c.kind_of?(HTree::Doc) }
87
+ unacceptable = children.reject {|c| c.kind_of?(HTree::Node) and !c.kind_of?(HTree::Doc) }
88
+ unacceptable = unacceptable.map {|uc| uc.inspect }.join(', ')
89
+ raise TypeError, "Unacceptable element child: #{unacceptable}"
90
+ end
91
+ unless !etag || etag.class == ETag
92
+ raise TypeError, "HTree::ETag expected: #{etag.inspect}"
93
+ end
94
+ @stag = stag
95
+ @children = (children ? children.dup : []).freeze
96
+ @empty = children == nil && etag == nil
97
+ @etag = etag
98
+ end
99
+
100
+ def context; @stag.context end
101
+
102
+ # +element_name+ returns the name of the element name as a Name object.
103
+ def element_name() @stag.element_name end
104
+
105
+ def empty_element?
106
+ @empty
107
+ end
108
+
109
+ def each_attribute(&block) # :yields: attr_name, attr_text
110
+ @stag.each_attribute(&block)
111
+ end
112
+
113
+ def get_subnode_internal(index) # :nodoc:
114
+ case index
115
+ when String
116
+ name = Name.parse_attribute_name(index, DefaultContext)
117
+ update_attribute_hash[name.universal_name]
118
+ when Name
119
+ update_attribute_hash[index.universal_name]
120
+ when Integer
121
+ if index < 0 || @children.length <= index
122
+ nil
123
+ else
124
+ @children[index]
125
+ end
126
+ else
127
+ raise TypeError, "invalid index: #{index.inspect}"
128
+ end
129
+ end
130
+
131
+ # call-seq:
132
+ # elem.subst_subnode(pairs) -> elem
133
+ #
134
+ # The argument _pairs_ should be a hash or an assocs.
135
+ #
136
+ # The key of pairs should be one of following.
137
+ # [HTree::Name or String object] attribute name.
138
+ # [Integer object] child index.
139
+ #
140
+ # The value of pairs should be one of follows.
141
+ # [HTree::Node object] specified object is used as is.
142
+ # [String object] specified string is converted to HTree::Text
143
+ # [Array of above] specified HTree::Node and String is used in that order.
144
+ # [nil] delete corresponding node.
145
+ #
146
+ # e = HTree('<r><a/><b/><c/></r>').root
147
+ # p e.subst_subnode({0=>HTree('<x/>'), 2=>HTree('<z/>')})
148
+ # p e.subst_subnode([[0, HTree('<x/>')], [2,HTree('<z/>')]])
149
+ # # =>
150
+ # {elem <r> {emptyelem <x>} {emptyelem <b>} {emptyelem <z>}}
151
+ # {elem <r> {emptyelem <x>} {emptyelem <b>} {emptyelem <z>}}
152
+ #
153
+ def subst_subnode(pairs)
154
+ hash = {}
155
+ pairs.each {|index, value|
156
+ case index
157
+ when Name, Integer
158
+ when String
159
+ index = Name.parse_attribute_name(index, DefaultContext)
160
+ else
161
+ raise TypeError, "invalid index: #{index.inspect}"
162
+ end
163
+ value = value.to_node if HTree::Location === value
164
+ case value
165
+ when Node
166
+ value = [value]
167
+ when String
168
+ value = [value]
169
+ when Array
170
+ value = value.dup
171
+ when nil
172
+ value = []
173
+ else
174
+ raise TypeError, "invalid value: #{value.inspect}"
175
+ end
176
+ value.map! {|v|
177
+ v = v.to_node if HTree::Location === v
178
+ case v
179
+ when Node
180
+ v
181
+ when String
182
+ Text.new(v)
183
+ else
184
+ raise TypeError, "invalid value: #{v.inspect}"
185
+ end
186
+ }
187
+ if !hash.include?(index)
188
+ hash[index] = []
189
+ end
190
+ hash[index].concat value
191
+ }
192
+
193
+ attrs = []
194
+ @stag.attributes.each {|k, v|
195
+ if hash.include? k
196
+ v = hash[k]
197
+ if !v.empty?
198
+ attrs << {k=>Text.concat(*v)}
199
+ end
200
+ hash.delete k
201
+ else
202
+ attrs << {k=>v}
203
+ end
204
+ }
205
+ hash.keys.each {|k|
206
+ if Name === k
207
+ v = hash[k]
208
+ if !v.empty?
209
+ attrs << {k=>Text.concat(*v)}
210
+ end
211
+ hash.delete k
212
+ end
213
+ }
214
+
215
+ children_left = []
216
+ children = @children.dup
217
+ children_right = []
218
+
219
+ hash.keys.sort.each {|index|
220
+ value = hash[index]
221
+ if index < 0
222
+ children_left << value
223
+ elsif children.length <= index
224
+ children_right << value
225
+ else
226
+ children[index] = value
227
+ end
228
+ }
229
+
230
+ children = [children_left, children, children_right].flatten
231
+
232
+ if children.empty? && @empty
233
+ Elem.new(
234
+ @stag.element_name,
235
+ @stag.context,
236
+ *attrs)
237
+ else
238
+ Elem.new(
239
+ @stag.element_name,
240
+ @stag.context,
241
+ children,
242
+ *attrs)
243
+ end
244
+ end
245
+ end
246
+
247
+ module Elem::Trav
248
+ private
249
+ def update_attribute_hash
250
+ if defined?(@attribute_hash)
251
+ @attribute_hash
252
+ else
253
+ h = {}
254
+ each_attribute {|name, text|
255
+ h[name.universal_name] = text
256
+ }
257
+ @attribute_hash = h
258
+ end
259
+ end
260
+ end
261
+ end
262
+ # :startdoc: