@@ -62,7 +62,7 @@ function dom:starttag(tag)
62
62
_children = {}
63
63
}
64
64
65
- if self .root == nil then
65
+ if not self .root then
66
66
self .root = node
67
67
end
68
68
76
76
-- @param tag a {name, attrs} table
77
77
-- where name is the name of the tag and attrs
78
78
-- is a table containing the attributes of the tag
79
- function dom :endtag (tag , s )
79
+ function dom :endtag (tag )
80
80
-- Table representing the containing tag of the current tag
81
81
local prev = self ._stack [# self ._stack ]
82
82
@@ -86,6 +86,22 @@ function dom:endtag(tag, s)
86
86
87
87
table.remove (self ._stack )
88
88
self .current = self ._stack [# self ._stack ]
89
+ if not self .current then
90
+ local node = { _children = {}, _type = " ROOT" }
91
+ if self .decl then
92
+ table.insert (node ._children , self .decl )
93
+ self .decl = nil
94
+ end
95
+ if self .dtd then
96
+ table.insert (node ._children , self .dtd )
97
+ self .dtd = nil
98
+ end
99
+ if self .root then
100
+ table.insert (node ._children , self .root )
101
+ self .root = node
102
+ end
103
+ self .current = node
104
+ end
89
105
end
90
106
91
107
--- Parses a tag content.
@@ -127,27 +143,128 @@ end
127
143
-- where name is the name of the tag and attrs
128
144
-- is a table containing the attributes of the tag
129
145
function dom :decl (tag )
130
- if self .options .declNode then
131
- local node = { _type = " DECL" ,
132
- _name = tag .name ,
133
- _attr = tag .attrs ,
134
- }
135
- table.insert (self .current ._children , node )
136
- end
146
+ if self .options .declNode then
147
+ self .decl = { _type = " DECL" ,
148
+ _name = tag .name ,
149
+ _attr = tag .attrs ,
150
+ }
151
+ end
137
152
end
138
153
139
154
--- Parses a DTD tag.
140
- -- @param tag a {name, attrs } table
141
- -- where name is the name of the tag and attrs
155
+ -- @param tag a {name, value } table
156
+ -- where name is the name of the tag and value
142
157
-- is a table containing the attributes of the tag
143
158
function dom :dtd (tag )
144
- if self .options .dtdNode then
145
- local node = { _type = " DTD" ,
146
- _name = tag .name ,
147
- _attr = tag .attrs ,
148
- }
149
- table.insert (self .current ._children , node )
150
- end
159
+ if self .options .dtdNode then
160
+ self .dtd = { _type = " DTD" ,
161
+ _name = tag .name ,
162
+ _text = tag .value
163
+ }
164
+ end
165
+ end
166
+
167
+ --- XML escape characters for a TEXT node.
168
+ -- @param s a string
169
+ -- @return @p s XML escaped.
170
+ local function xmlEscape (s )
171
+ s = string.gsub (s , ' &' , ' &' )
172
+ s = string.gsub (s , ' <' , ' <' )
173
+ return string.gsub (s , ' >' , ' >' )
174
+ end
175
+
176
+ --- return a string of XML attributes
177
+ -- @param tab table with XML attribute pairs. key and value are supposed to be strings.
178
+ -- @return a string.
179
+ local function attrsToStr (tab )
180
+ if not tab then
181
+ return ' '
182
+ end
183
+ if type (tab ) == ' table' then
184
+ local s = ' '
185
+ for n ,v in pairs (tab ) do
186
+ -- determine a safe quote character
187
+ local val = tostring (v )
188
+ local found_single_quote = string.find (val , " '" )
189
+ local found_double_quote = string.find (val , ' "' )
190
+ local quot = ' "'
191
+ if found_single_quote and found_double_quote then
192
+ -- XML escape both quote characters
193
+ val = string.gsub (val , ' "' , ' "' )
194
+ val = string.gsub (val , " '" , ' '' )
195
+ elseif found_double_quote then
196
+ quot = " '"
197
+ end
198
+ s = ' ' .. tostring (n ) .. ' =' .. quot .. val .. quot
199
+ end
200
+ return s
201
+ end
202
+ return ' BUG:unknown type:' .. type (tab )
203
+ end
204
+
205
+ --- return a XML formatted string of @p node.
206
+ -- @param node a Node object (table) of the xml2lua DOM tree structure.
207
+ -- @return a string.
208
+ local function toXmlStr (node , indentLevel )
209
+ if not node then
210
+ return ' BUG:node==nil'
211
+ end
212
+ if not node ._type then
213
+ return ' BUG:node._type==nil'
214
+ end
215
+
216
+ local indent = ' '
217
+ for i = 0 , indentLevel + 1 , 1 do
218
+ indent = indent .. ' '
219
+ end
220
+
221
+ if node ._type == ' ROOT' then
222
+ local s = ' '
223
+ for i , n in pairs (node ._children ) do
224
+ s = s .. toXmlStr (n , indentLevel + 2 )
225
+ end
226
+ return s
227
+ elseif node ._type == ' ELEMENT' then
228
+ local s = indent .. ' <' .. node ._name .. attrsToStr (node ._attr )
229
+
230
+ -- check if ELEMENT has no children
231
+ if not node ._children or
232
+ # node ._children == 0 then
233
+ return s .. ' />\n '
234
+ end
235
+
236
+ s = s .. ' >\n '
237
+
238
+ for i , n in pairs (node ._children ) do
239
+ local xx = toXmlStr (n , indentLevel + 2 )
240
+ if not xx then
241
+ print (' BUG:xx==nil' )
242
+ else
243
+ s = s .. xx
244
+ end
245
+ end
246
+
247
+ return s .. indent .. ' </' .. node ._name .. ' >\n '
248
+
249
+ elseif node ._type == ' TEXT' then
250
+ return indent .. xmlEscape (node ._text ) .. ' \n '
251
+ elseif node ._type == ' COMMENT' then
252
+ return indent .. ' <!--' .. node ._text .. ' -->\n '
253
+ elseif node ._type == ' PI' then
254
+ return indent .. ' <?' .. node ._name .. ' ' .. node ._attr ._text .. ' ?>\n '
255
+ elseif node ._type == ' DECL' then
256
+ return indent .. ' <?' .. node ._name .. attrsToStr (node ._attr ) .. ' ?>\n '
257
+ elseif node ._type == ' DTD' then
258
+ return indent .. ' <!' .. node ._name .. ' ' .. node ._text .. ' >\n '
259
+ end
260
+ return ' BUG:unknown type:' .. tostring (node ._type )
261
+ end
262
+
263
+ --- create a string in XML format from the dom root object @p node.
264
+ -- @param node a root object, typically created with `dom` XML parser handler.
265
+ -- @return a string, XML formatted.
266
+ function dom :toXml (node )
267
+ return toXmlStr (node , - 4 )
151
268
end
152
269
153
270
--- Parses CDATA tag content.
0 commit comments