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.
Files changed (62) hide show
  1. data.tar.gz.sig +4 -0
  2. data/Makefile +20 -0
  3. data/Manifest +58 -0
  4. data/README +61 -0
  5. data/Rakefile +37 -0
  6. data/htree.gemspec +32 -0
  7. data/init.rb +1 -0
  8. data/install.rb +112 -0
  9. data/lib/htree.rb +97 -0
  10. data/lib/htree/container.rb +8 -0
  11. data/lib/htree/context.rb +69 -0
  12. data/lib/htree/display.rb +46 -0
  13. data/lib/htree/doc.rb +149 -0
  14. data/lib/htree/elem.rb +262 -0
  15. data/lib/htree/encoder.rb +217 -0
  16. data/lib/htree/equality.rb +219 -0
  17. data/lib/htree/extract_text.rb +37 -0
  18. data/lib/htree/fstr.rb +32 -0
  19. data/lib/htree/gencode.rb +193 -0
  20. data/lib/htree/htmlinfo.rb +672 -0
  21. data/lib/htree/inspect.rb +108 -0
  22. data/lib/htree/leaf.rb +92 -0
  23. data/lib/htree/loc.rb +369 -0
  24. data/lib/htree/modules.rb +49 -0
  25. data/lib/htree/name.rb +122 -0
  26. data/lib/htree/output.rb +212 -0
  27. data/lib/htree/parse.rb +410 -0
  28. data/lib/htree/raw_string.rb +127 -0
  29. data/lib/htree/regexp-util.rb +19 -0
  30. data/lib/htree/rexml.rb +131 -0
  31. data/lib/htree/scan.rb +176 -0
  32. data/lib/htree/tag.rb +113 -0
  33. data/lib/htree/template.rb +961 -0
  34. data/lib/htree/text.rb +115 -0
  35. data/lib/htree/traverse.rb +497 -0
  36. data/test-all.rb +5 -0
  37. data/test/assign.html +1 -0
  38. data/test/template.html +4 -0
  39. data/test/test-attr.rb +67 -0
  40. data/test/test-charset.rb +79 -0
  41. data/test/test-context.rb +29 -0
  42. data/test/test-display_xml.rb +45 -0
  43. data/test/test-elem-new.rb +101 -0
  44. data/test/test-encoder.rb +53 -0
  45. data/test/test-equality.rb +55 -0
  46. data/test/test-extract_text.rb +18 -0
  47. data/test/test-gencode.rb +27 -0
  48. data/test/test-leaf.rb +25 -0
  49. data/test/test-loc.rb +60 -0
  50. data/test/test-namespace.rb +147 -0
  51. data/test/test-output.rb +133 -0
  52. data/test/test-parse.rb +115 -0
  53. data/test/test-raw_string.rb +17 -0
  54. data/test/test-rexml.rb +70 -0
  55. data/test/test-scan.rb +153 -0
  56. data/test/test-security.rb +37 -0
  57. data/test/test-subnode.rb +142 -0
  58. data/test/test-template.rb +313 -0
  59. data/test/test-text.rb +43 -0
  60. data/test/test-traverse.rb +69 -0
  61. metadata +166 -0
  62. 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
@@ -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
@@ -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