feedtools 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +11 -0
- data/lib/feed_tools.rb +2496 -810
- data/lib/feed_tools/vendor/builder.rb +2 -0
- data/lib/feed_tools/vendor/builder/blankslate.rb +2 -0
- data/lib/feed_tools/vendor/builder/xmlbase.rb +2 -1
- data/lib/feed_tools/vendor/builder/xmlevents.rb +2 -0
- data/lib/feed_tools/vendor/builder/xmlmarkup.rb +4 -2
- data/lib/feed_tools/vendor/htree.rb +97 -0
- data/lib/feed_tools/vendor/htree/container.rb +10 -0
- data/lib/feed_tools/vendor/htree/context.rb +67 -0
- data/lib/feed_tools/vendor/htree/display.rb +27 -0
- data/lib/feed_tools/vendor/htree/doc.rb +149 -0
- data/lib/feed_tools/vendor/htree/elem.rb +262 -0
- data/lib/feed_tools/vendor/htree/encoder.rb +163 -0
- data/lib/feed_tools/vendor/htree/equality.rb +218 -0
- data/lib/feed_tools/vendor/htree/extract_text.rb +37 -0
- data/lib/feed_tools/vendor/htree/fstr.rb +33 -0
- data/lib/feed_tools/vendor/htree/gencode.rb +97 -0
- data/lib/feed_tools/vendor/htree/htmlinfo.rb +672 -0
- data/lib/feed_tools/vendor/htree/inspect.rb +108 -0
- data/lib/feed_tools/vendor/htree/leaf.rb +94 -0
- data/lib/feed_tools/vendor/htree/loc.rb +367 -0
- data/lib/feed_tools/vendor/htree/modules.rb +48 -0
- data/lib/feed_tools/vendor/htree/name.rb +124 -0
- data/lib/feed_tools/vendor/htree/output.rb +207 -0
- data/lib/feed_tools/vendor/htree/parse.rb +407 -0
- data/lib/feed_tools/vendor/htree/raw_string.rb +124 -0
- data/lib/feed_tools/vendor/htree/regexp-util.rb +15 -0
- data/lib/feed_tools/vendor/htree/rexml.rb +130 -0
- data/lib/feed_tools/vendor/htree/scan.rb +166 -0
- data/lib/feed_tools/vendor/htree/tag.rb +111 -0
- data/lib/feed_tools/vendor/htree/template.rb +909 -0
- data/lib/feed_tools/vendor/htree/text.rb +115 -0
- data/lib/feed_tools/vendor/htree/traverse.rb +465 -0
- data/rakefile +1 -1
- data/test/rss_test.rb +97 -0
- metadata +30 -1
@@ -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,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:
|