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