summaryrefslogtreecommitdiff
path: root/lib/rexml/light/node.rb
blob: 5b7b95a7dcac8a4b1213cc18ecf5b33e43d2cf16 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
require 'rexml/xmltokens'
require 'rexml/light/node'

# Development model
# document = Node.new

# Add an element "foo" to the document
# foo = document << "foo"
# # Set attribute "attr" on foo
# foo["attr"] = "la"
# # Set another attribute in a different namespace
# foo["attr", "namespace"] = "too"
# # Swap foo into another namespace
# foo.namespace = "blah"
# # Add a couple of element nodes to foo
# foo << "a"
# foo << "b"
# # Access the children of foo in various ways
# a = foo[0]
# foo.each { |child|
#         #...
# }
# # Add text to foo
# # Add instruction
# # Add comment
# # Get the root of the document
# document == a.root
# # Write the document out
# puts document.to_s
module REXML
	module Light
		# Represents a tagged XML element.  Elements are characterized by
		# having children, attributes, and names, and can themselves be
		# children.
		class Node < Array
			alias :_old_get :[]
			alias :_old_put :[]=

			NAMESPLIT = /^(?:(#{XMLTokens::NCNAME_STR}):)?(#{XMLTokens::NCNAME_STR})/u
			# Create a new element.
			def initialize node=nil
				if node.kind_of? String
					node = [ :text, node ]
				elsif node.nil?
					node = [ :start_document, nil, nil ]
				end
				replace( node )
				_old_put( 1, 0, 1 )
				_old_put( 1, nil )
			end

			def size
				el!()
				super-4
			end

			def each( &block )
				el!()
				size.times { |x| yield( at(x+4) ) }
			end

			def name
				el!()
				at(2)
			end

			def name=( name_str, ns=nil )
				el!()
				pfx = ''
				pfx = "#{prefix(ns)}:" if ns
				_old_put(1, "#{pfx}#{name_str}")
			end

			def parent=( node )
				_old_put(1,node)
			end

			def local_name
				el!()
				namesplit
				@name
			end

			def local_name=( name_str )
				el!()
				_old_put( 1, "#@prefix:#{name_str}" )
			end

			def prefix( namespace=nil )
				el!()
				prefix_of( self, namespace )
			end

			def namespace( prefix=prefix() )
				el!()
				namespace_of( self, prefix )
			end

			def namespace=( namespace )
				el!()
				@prefix = prefix( namespace )
				pfx = ''
				pfx = "#@prefix:" if @prefix.size > 0
				_old_put(1, "#{pfx}#@name")
			end

			def []( reference, ns=nil )
				el!()
				if reference.kind_of? String
					pfx = ''
					pfx = "#{prefix(ns)}:" if ns
					at(3)["#{pfx}#{reference}"]
				elsif reference.kind_of? Range
					_old_get( Range.new(4+reference.begin, reference.end, reference.exclude_end?) )
				else
					_old_get( 4+reference )
				end
			end

			# Doesn't handle namespaces yet
			def []=( reference, ns, value=nil )
				el!()
				if reference.kind_of? String
					value = ns unless value
					at( 3 )[reference] = value
				elsif reference.kind_of? Range
					_old_put( Range.new(3+reference.begin, reference.end, reference.exclude_end?), ns )
				else
					if value
						_old_put( 4+reference, ns, value )
					else
						_old_put( 4+reference, ns )
					end
				end
			end

			# Append a child to this element, optionally under a provided namespace.
			# The namespace argument is ignored if the element argument is an Element
			# object.  Otherwise, the element argument is a string, the namespace (if
			# provided) is the namespace the element is created in.
			def << element
				if text?
					at(-1) << element
				else
					newnode = Node.new( element )
					newnode.parent = self
					self.push( newnode )
				end
				at(-1)
			end

			def node_type
				self[0]
			end

			def text=( foo )
				replace = at(4).kind_of? String ? 1 : 0
				self._old_put(4,replace, normalizefoo)
			end

			def root
				context = self
				context = context.at(1) while context.at(1)
			end

			def element?
				at(0) == :start_element
			end

			def has_name?( name, namespace = '' )
				el!()
				at(3) == name and namespace() == namespace
			end

			def children
				el!()
				self
			end

			def parent
				at(1)
			end

			def text?
				at(0) == :text
			end
			
			def to_s

			end

			def el!
				if text?()
					_old_put( 0, :start_element )
					push({})
				end
			end

			private

			def namesplit
				return if @name.defined?
				at(2) =~ NAMESPLIT
				@prefix = '' || $1
				@name = $2
			end

			def namespace_of( node, prefix=nil )
				if not prefix
					name = at(2)
					name =~ NAMESPLIT
					prefix = $1
				end
				to_find = 'xmlns'
				to_find = "xmlns:#{prefix}" if not prefix.nil?
				ns = at(3)[ to_find ]
				ns ? ns : namespace_of( @node[0], prefix )
			end

			def prefix_of( node, namespace=nil )
				if not namespace
					name = node.name
					name =~ NAMESPLIT
					$1
				else
					ns = at(3).find { |k,v| v == namespace }
					ns ? ns : prefix_of( node.parent, namespace )
				end
			end
		end
	end
end