htree 0.7.0

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