htree 0.7.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.
- data.tar.gz.sig +4 -0
- data/Makefile +20 -0
- data/Manifest +58 -0
- data/README +61 -0
- data/Rakefile +37 -0
- data/htree.gemspec +32 -0
- data/init.rb +1 -0
- data/install.rb +112 -0
- data/lib/htree.rb +97 -0
- data/lib/htree/container.rb +8 -0
- data/lib/htree/context.rb +69 -0
- data/lib/htree/display.rb +46 -0
- data/lib/htree/doc.rb +149 -0
- data/lib/htree/elem.rb +262 -0
- data/lib/htree/encoder.rb +217 -0
- data/lib/htree/equality.rb +219 -0
- data/lib/htree/extract_text.rb +37 -0
- data/lib/htree/fstr.rb +32 -0
- data/lib/htree/gencode.rb +193 -0
- data/lib/htree/htmlinfo.rb +672 -0
- data/lib/htree/inspect.rb +108 -0
- data/lib/htree/leaf.rb +92 -0
- data/lib/htree/loc.rb +369 -0
- data/lib/htree/modules.rb +49 -0
- data/lib/htree/name.rb +122 -0
- data/lib/htree/output.rb +212 -0
- data/lib/htree/parse.rb +410 -0
- data/lib/htree/raw_string.rb +127 -0
- data/lib/htree/regexp-util.rb +19 -0
- data/lib/htree/rexml.rb +131 -0
- data/lib/htree/scan.rb +176 -0
- data/lib/htree/tag.rb +113 -0
- data/lib/htree/template.rb +961 -0
- data/lib/htree/text.rb +115 -0
- data/lib/htree/traverse.rb +497 -0
- data/test-all.rb +5 -0
- data/test/assign.html +1 -0
- data/test/template.html +4 -0
- data/test/test-attr.rb +67 -0
- data/test/test-charset.rb +79 -0
- data/test/test-context.rb +29 -0
- data/test/test-display_xml.rb +45 -0
- data/test/test-elem-new.rb +101 -0
- data/test/test-encoder.rb +53 -0
- data/test/test-equality.rb +55 -0
- data/test/test-extract_text.rb +18 -0
- data/test/test-gencode.rb +27 -0
- data/test/test-leaf.rb +25 -0
- data/test/test-loc.rb +60 -0
- data/test/test-namespace.rb +147 -0
- data/test/test-output.rb +133 -0
- data/test/test-parse.rb +115 -0
- data/test/test-raw_string.rb +17 -0
- data/test/test-rexml.rb +70 -0
- data/test/test-scan.rb +153 -0
- data/test/test-security.rb +37 -0
- data/test/test-subnode.rb +142 -0
- data/test/test-template.rb +313 -0
- data/test/test-text.rb +43 -0
- data/test/test-traverse.rb +69 -0
- metadata +166 -0
- metadata.gz.sig +1 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'htree/output'
|
2
|
+
|
3
|
+
module HTree
|
4
|
+
module Node
|
5
|
+
# HTree::Node#display_xml prints the node as XML.
|
6
|
+
#
|
7
|
+
# The first optional argument, <i>out</i>,
|
8
|
+
# specifies output target.
|
9
|
+
# It should respond to <tt><<</tt>.
|
10
|
+
# If it is not specified, $stdout is used.
|
11
|
+
#
|
12
|
+
# The second optional argument, <i>encoding</i>,
|
13
|
+
# specifies output MIME charset (character encoding).
|
14
|
+
# If it is not specified, HTree::Encoder.internal_charset is used.
|
15
|
+
#
|
16
|
+
# HTree::Node#display_xml returns <i>out</i>.
|
17
|
+
def display_xml(out=$stdout, encoding=HTree::Encoder.internal_charset)
|
18
|
+
encoder = HTree::Encoder.new(encoding)
|
19
|
+
self.output(encoder, HTree::DefaultContext)
|
20
|
+
# don't call finish_with_xmldecl because self already has a xml decl.
|
21
|
+
out << encoder.finish
|
22
|
+
out
|
23
|
+
end
|
24
|
+
|
25
|
+
# HTree::Node#display_html prints the node as HTML.
|
26
|
+
#
|
27
|
+
# The first optional argument, <i>out</i>,
|
28
|
+
# specifies output target.
|
29
|
+
# It should respond to <tt><<</tt>.
|
30
|
+
# If it is not specified, $stdout is used.
|
31
|
+
#
|
32
|
+
# The second optional argument, <i>encoding</i>,
|
33
|
+
# specifies output MIME charset (character encoding).
|
34
|
+
# If it is not specified, HTree::Encoder.internal_charset is used.
|
35
|
+
#
|
36
|
+
# HTree::Node#display_html returns <i>out</i>.
|
37
|
+
def display_html(out=$stdout, encoding=HTree::Encoder.internal_charset)
|
38
|
+
encoder = HTree::Encoder.new(encoding)
|
39
|
+
encoder.html_output = true
|
40
|
+
self.output(encoder, HTree::HTMLContext)
|
41
|
+
out << encoder.finish
|
42
|
+
out
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
data/lib/htree/doc.rb
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'htree/modules'
|
2
|
+
require 'htree/container'
|
3
|
+
|
4
|
+
module HTree
|
5
|
+
class Doc
|
6
|
+
# :stopdoc:
|
7
|
+
class << self
|
8
|
+
alias new! new
|
9
|
+
end
|
10
|
+
# :startdoc:
|
11
|
+
|
12
|
+
# The arguments should be a sequence of follows.
|
13
|
+
# [String object] specified string is converted to HTree::Text.
|
14
|
+
# [HTree::Node object] used as a child.
|
15
|
+
# [HTree::Doc object]
|
16
|
+
# used as children.
|
17
|
+
# It is expanded except HTree::XMLDecl and HTree::DocType objects.
|
18
|
+
# [Array of String, HTree::Node and HTree::Doc] used as children.
|
19
|
+
#
|
20
|
+
def Doc.new(*args)
|
21
|
+
children = []
|
22
|
+
args.each {|arg|
|
23
|
+
arg = arg.to_node if HTree::Location === arg
|
24
|
+
case arg
|
25
|
+
when Array
|
26
|
+
arg.each {|a|
|
27
|
+
a = a.to_node if HTree::Location === a
|
28
|
+
case a
|
29
|
+
when HTree::Doc
|
30
|
+
children.concat(a.children.reject {|c|
|
31
|
+
HTree::XMLDecl === c || HTree::DocType === c
|
32
|
+
})
|
33
|
+
when HTree::Node
|
34
|
+
children << a
|
35
|
+
when String
|
36
|
+
children << Text.new(a)
|
37
|
+
else
|
38
|
+
raise TypeError, "unexpected argument: #{arg.inspect}"
|
39
|
+
end
|
40
|
+
}
|
41
|
+
when HTree::Doc
|
42
|
+
children.concat(arg.children.reject {|c|
|
43
|
+
HTree::XMLDecl === c || HTree::DocType === c
|
44
|
+
})
|
45
|
+
when HTree::Node
|
46
|
+
children << arg
|
47
|
+
when String
|
48
|
+
children << Text.new(arg)
|
49
|
+
else
|
50
|
+
raise TypeError, "unexpected argument: #{arg.inspect}"
|
51
|
+
end
|
52
|
+
}
|
53
|
+
new!(children)
|
54
|
+
end
|
55
|
+
|
56
|
+
def initialize(children=[]) # :notnew:
|
57
|
+
@children = children.dup.freeze
|
58
|
+
unless @children.all? {|c| c.kind_of?(HTree::Node) and !c.kind_of?(HTree::Doc) }
|
59
|
+
unacceptable = @children.reject {|c| c.kind_of?(HTree::Node) and !c.kind_of?(HTree::Doc) }
|
60
|
+
unacceptable = unacceptable.map {|uc| uc.inspect }.join(', ')
|
61
|
+
raise TypeError, "Unacceptable document child: #{unacceptable}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def get_subnode_internal(index) # :nodoc:
|
66
|
+
unless Integer === index
|
67
|
+
raise TypeError, "invalid index: #{index.inspect}"
|
68
|
+
end
|
69
|
+
if index < 0 || @children.length <= index
|
70
|
+
nil
|
71
|
+
else
|
72
|
+
@children[index]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# doc.subst_subnode(pairs) -> doc
|
77
|
+
#
|
78
|
+
# The argument _pairs_ should be a hash or an assocs.
|
79
|
+
# Its key should be an integer which means an index for children.
|
80
|
+
#
|
81
|
+
# Its value should be one of follows.
|
82
|
+
# [HTree::Node object] specified object is used as is.
|
83
|
+
# [String object] specified string is converted to HTree::Text
|
84
|
+
# [Array of above] specified HTree::Node and String is used in that order.
|
85
|
+
# [nil] delete corresponding node.
|
86
|
+
#
|
87
|
+
# d = HTree('<a/><b/><c/>')
|
88
|
+
# p d.subst_subnode({0=>HTree('<x/>'), 2=>HTree('<z/>')})
|
89
|
+
# p d.subst_subnode([[0,HTree('<x/>')], [2,HTree('<z/>')]])
|
90
|
+
# # =>
|
91
|
+
# #<HTree::Doc {emptyelem <x>} {emptyelem <b>} {emptyelem <z>}>
|
92
|
+
# #<HTree::Doc {emptyelem <x>} {emptyelem <b>} {emptyelem <z>}>
|
93
|
+
#
|
94
|
+
def subst_subnode(pairs)
|
95
|
+
hash = {}
|
96
|
+
pairs.each {|index, value|
|
97
|
+
unless Integer === index
|
98
|
+
raise TypeError, "invalid index: #{index.inspect}"
|
99
|
+
end
|
100
|
+
value = value.to_node if HTree::Location === value
|
101
|
+
case value
|
102
|
+
when Node
|
103
|
+
value = [value]
|
104
|
+
when String
|
105
|
+
value = [value]
|
106
|
+
when Array
|
107
|
+
value = value.dup
|
108
|
+
when nil
|
109
|
+
value = []
|
110
|
+
else
|
111
|
+
raise TypeError, "invalid value: #{value.inspect}"
|
112
|
+
end
|
113
|
+
value.map! {|v|
|
114
|
+
v = v.to_node if HTree::Location === v
|
115
|
+
case v
|
116
|
+
when Node
|
117
|
+
v
|
118
|
+
when String
|
119
|
+
Text.new(v)
|
120
|
+
else
|
121
|
+
raise TypeError, "invalid value: #{v.inspect}"
|
122
|
+
end
|
123
|
+
}
|
124
|
+
if !hash.include?(index)
|
125
|
+
hash[index] = []
|
126
|
+
end
|
127
|
+
hash[index].concat value
|
128
|
+
}
|
129
|
+
|
130
|
+
children_left = []
|
131
|
+
children = @children.dup
|
132
|
+
children_right = []
|
133
|
+
|
134
|
+
hash.keys.sort.each {|index|
|
135
|
+
value = hash[index]
|
136
|
+
if index < 0
|
137
|
+
children_left << value
|
138
|
+
elsif children.length <= index
|
139
|
+
children_right << value
|
140
|
+
else
|
141
|
+
children[index] = value
|
142
|
+
end
|
143
|
+
}
|
144
|
+
|
145
|
+
children = [children_left, children, children_right].flatten.compact
|
146
|
+
Doc.new(children)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
data/lib/htree/elem.rb
ADDED
@@ -0,0 +1,262 @@
|
|
1
|
+
require 'htree/modules'
|
2
|
+
require 'htree/tag'
|
3
|
+
require 'htree/context'
|
4
|
+
require 'htree/container'
|
5
|
+
|
6
|
+
module HTree
|
7
|
+
class Elem
|
8
|
+
# :stopdoc:
|
9
|
+
class << self
|
10
|
+
alias new! new
|
11
|
+
end
|
12
|
+
# :startdoc:
|
13
|
+
|
14
|
+
# The first argument _name_ should be an instance of String or HTree::Name.
|
15
|
+
#
|
16
|
+
# The rest of arguments should be a sequence of follows.
|
17
|
+
# [Hash object] used as attributes.
|
18
|
+
# [String object] specified string is converted to HTree::Text.
|
19
|
+
# [HTree::Node object] used as a child.
|
20
|
+
# [HTree::Doc object]
|
21
|
+
# used as children.
|
22
|
+
# It is expanded except HTree::XMLDecl and HTree::DocType objects.
|
23
|
+
# [Array of String, HTree::Node, HTree::Doc] used as children.
|
24
|
+
# [HTree::Context object]
|
25
|
+
# used as as context which represents XML namespaces.
|
26
|
+
# This should apper once at most.
|
27
|
+
#
|
28
|
+
# HTree::Location object is accepted just as HTree::Node.
|
29
|
+
#
|
30
|
+
# If the rest arguments consists only
|
31
|
+
# Hash and HTree::Context, empty element is created.
|
32
|
+
#
|
33
|
+
# p HTree::Elem.new("e").empty_element? # => true
|
34
|
+
# p HTree::Elem.new("e", []).empty_element? # => false
|
35
|
+
def Elem.new(name, *args)
|
36
|
+
attrs = []
|
37
|
+
children = []
|
38
|
+
context = nil
|
39
|
+
args.each {|arg|
|
40
|
+
arg = arg.to_node if HTree::Location === arg
|
41
|
+
case arg
|
42
|
+
when Context
|
43
|
+
raise ArgumentError, "multiple context" if context
|
44
|
+
context = arg
|
45
|
+
when Hash
|
46
|
+
arg.each {|k, v| attrs << [k, v] }
|
47
|
+
when Array
|
48
|
+
arg.each {|a|
|
49
|
+
a = a.to_node if HTree::Location === a
|
50
|
+
case a
|
51
|
+
when HTree::Doc
|
52
|
+
children.concat(a.children.reject {|c|
|
53
|
+
HTree::XMLDecl === c || HTree::DocType === c
|
54
|
+
})
|
55
|
+
when HTree::Node
|
56
|
+
children << a
|
57
|
+
when String
|
58
|
+
children << Text.new(a)
|
59
|
+
else
|
60
|
+
raise TypeError, "unexpected argument: #{arg.inspect}"
|
61
|
+
end
|
62
|
+
}
|
63
|
+
when HTree::Doc
|
64
|
+
children.concat(arg.children.reject {|c|
|
65
|
+
HTree::XMLDecl === c || HTree::DocType === c
|
66
|
+
})
|
67
|
+
when HTree::Node
|
68
|
+
children << arg
|
69
|
+
when String
|
70
|
+
children << Text.new(arg)
|
71
|
+
|
72
|
+
else
|
73
|
+
raise TypeError, "unexpected argument: #{arg.inspect}"
|
74
|
+
end
|
75
|
+
}
|
76
|
+
context ||= DefaultContext
|
77
|
+
if children.empty? && args.all? {|arg| Hash === arg || Context === arg }
|
78
|
+
children = nil
|
79
|
+
end
|
80
|
+
new!(STag.new(name, attrs, context), children)
|
81
|
+
end
|
82
|
+
|
83
|
+
def initialize(stag, children=nil, etag=nil) # :notnew:
|
84
|
+
unless stag.class == STag
|
85
|
+
raise TypeError, "HTree::STag expected: #{stag.inspect}"
|
86
|
+
end
|
87
|
+
unless !children || children.all? {|c| c.kind_of?(HTree::Node) and !c.kind_of?(HTree::Doc) }
|
88
|
+
unacceptable = children.reject {|c| c.kind_of?(HTree::Node) and !c.kind_of?(HTree::Doc) }
|
89
|
+
unacceptable = unacceptable.map {|uc| uc.inspect }.join(', ')
|
90
|
+
raise TypeError, "Unacceptable element child: #{unacceptable}"
|
91
|
+
end
|
92
|
+
unless !etag || etag.class == ETag
|
93
|
+
raise TypeError, "HTree::ETag expected: #{etag.inspect}"
|
94
|
+
end
|
95
|
+
@stag = stag
|
96
|
+
@children = (children ? children.dup : []).freeze
|
97
|
+
@empty = children == nil && etag == nil
|
98
|
+
@etag = etag
|
99
|
+
end
|
100
|
+
|
101
|
+
def context; @stag.context end
|
102
|
+
|
103
|
+
# +element_name+ returns the name of the element name as a Name object.
|
104
|
+
def element_name() @stag.element_name end
|
105
|
+
|
106
|
+
def empty_element?
|
107
|
+
@empty
|
108
|
+
end
|
109
|
+
|
110
|
+
def each_attribute(&block) # :yields: attr_name, attr_text
|
111
|
+
@stag.each_attribute(&block)
|
112
|
+
end
|
113
|
+
|
114
|
+
def get_subnode_internal(index) # :nodoc:
|
115
|
+
case index
|
116
|
+
when String
|
117
|
+
name = Name.parse_attribute_name(index, DefaultContext)
|
118
|
+
update_attribute_hash[name.universal_name]
|
119
|
+
when Name
|
120
|
+
update_attribute_hash[index.universal_name]
|
121
|
+
when Integer
|
122
|
+
if index < 0 || @children.length <= index
|
123
|
+
nil
|
124
|
+
else
|
125
|
+
@children[index]
|
126
|
+
end
|
127
|
+
else
|
128
|
+
raise TypeError, "invalid index: #{index.inspect}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# call-seq:
|
133
|
+
# elem.subst_subnode(pairs) -> elem
|
134
|
+
#
|
135
|
+
# The argument _pairs_ should be a hash or an assocs.
|
136
|
+
#
|
137
|
+
# The key of pairs should be one of following.
|
138
|
+
# [HTree::Name or String object] attribute name.
|
139
|
+
# [Integer object] child index.
|
140
|
+
#
|
141
|
+
# The value of pairs should be one of follows.
|
142
|
+
# [HTree::Node object] specified object is used as is.
|
143
|
+
# [String object] specified string is converted to HTree::Text
|
144
|
+
# [Array of above] specified HTree::Node and String is used in that order.
|
145
|
+
# [nil] delete corresponding node.
|
146
|
+
#
|
147
|
+
# e = HTree('<r><a/><b/><c/></r>').root
|
148
|
+
# p e.subst_subnode({0=>HTree('<x/>'), 2=>HTree('<z/>')})
|
149
|
+
# p e.subst_subnode([[0, HTree('<x/>')], [2,HTree('<z/>')]])
|
150
|
+
# # =>
|
151
|
+
# {elem <r> {emptyelem <x>} {emptyelem <b>} {emptyelem <z>}}
|
152
|
+
# {elem <r> {emptyelem <x>} {emptyelem <b>} {emptyelem <z>}}
|
153
|
+
#
|
154
|
+
def subst_subnode(pairs)
|
155
|
+
hash = {}
|
156
|
+
pairs.each {|index, value|
|
157
|
+
case index
|
158
|
+
when Name, Integer
|
159
|
+
when String
|
160
|
+
index = Name.parse_attribute_name(index, DefaultContext)
|
161
|
+
else
|
162
|
+
raise TypeError, "invalid index: #{index.inspect}"
|
163
|
+
end
|
164
|
+
value = value.to_node if HTree::Location === value
|
165
|
+
case value
|
166
|
+
when Node
|
167
|
+
value = [value]
|
168
|
+
when String
|
169
|
+
value = [value]
|
170
|
+
when Array
|
171
|
+
value = value.dup
|
172
|
+
when nil
|
173
|
+
value = []
|
174
|
+
else
|
175
|
+
raise TypeError, "invalid value: #{value.inspect}"
|
176
|
+
end
|
177
|
+
value.map! {|v|
|
178
|
+
v = v.to_node if HTree::Location === v
|
179
|
+
case v
|
180
|
+
when Node
|
181
|
+
v
|
182
|
+
when String
|
183
|
+
Text.new(v)
|
184
|
+
else
|
185
|
+
raise TypeError, "invalid value: #{v.inspect}"
|
186
|
+
end
|
187
|
+
}
|
188
|
+
if !hash.include?(index)
|
189
|
+
hash[index] = []
|
190
|
+
end
|
191
|
+
hash[index].concat value
|
192
|
+
}
|
193
|
+
|
194
|
+
attrs = []
|
195
|
+
@stag.attributes.each {|k, v|
|
196
|
+
if hash.include? k
|
197
|
+
v = hash[k]
|
198
|
+
if !v.empty?
|
199
|
+
attrs << {k=>Text.concat(*v)}
|
200
|
+
end
|
201
|
+
hash.delete k
|
202
|
+
else
|
203
|
+
attrs << {k=>v}
|
204
|
+
end
|
205
|
+
}
|
206
|
+
hash.keys.each {|k|
|
207
|
+
if Name === k
|
208
|
+
v = hash[k]
|
209
|
+
if !v.empty?
|
210
|
+
attrs << {k=>Text.concat(*v)}
|
211
|
+
end
|
212
|
+
hash.delete k
|
213
|
+
end
|
214
|
+
}
|
215
|
+
|
216
|
+
children_left = []
|
217
|
+
children = @children.dup
|
218
|
+
children_right = []
|
219
|
+
|
220
|
+
hash.keys.sort.each {|index|
|
221
|
+
value = hash[index]
|
222
|
+
if index < 0
|
223
|
+
children_left << value
|
224
|
+
elsif children.length <= index
|
225
|
+
children_right << value
|
226
|
+
else
|
227
|
+
children[index] = value
|
228
|
+
end
|
229
|
+
}
|
230
|
+
|
231
|
+
children = [children_left, children, children_right].flatten
|
232
|
+
|
233
|
+
if children.empty? && @empty
|
234
|
+
Elem.new(
|
235
|
+
@stag.element_name,
|
236
|
+
@stag.context,
|
237
|
+
*attrs)
|
238
|
+
else
|
239
|
+
Elem.new(
|
240
|
+
@stag.element_name,
|
241
|
+
@stag.context,
|
242
|
+
children,
|
243
|
+
*attrs)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
module Elem::Trav
|
249
|
+
private
|
250
|
+
def update_attribute_hash
|
251
|
+
if defined?(@attribute_hash)
|
252
|
+
@attribute_hash
|
253
|
+
else
|
254
|
+
h = {}
|
255
|
+
each_attribute {|name, text|
|
256
|
+
h[name.universal_name] = text
|
257
|
+
}
|
258
|
+
@attribute_hash = h
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|