tochie-facade/scenes/Xml.gd

98 lines
3.8 KiB
GDScript3
Raw Normal View History

2023-08-26 15:16:36 +00:00
extends Node
class XmlNode extends Reference:
func is_element() -> bool: return false
func is_text() -> bool: return false
class XmlElement extends XmlNode:
var node_name: String
var attributes: Dictionary # String to String
var children: Array # of XmlNode
func is_element() -> bool: return true
func take_named_child_element(p_node_name: String) -> XmlElement:
for idx in range(self.children.size()):
var child := self.children[idx] as XmlElement
if child != null and child.node_name == p_node_name:
self.children.remove(idx)
return child
return null
func as_string(level: int = 0) -> String:
2023-08-26 15:16:36 +00:00
var result = '\t'.repeat(level) + "XmlElement \"%s\"" % self.node_name
if self.attributes.size() > 0:
result += ", Attributes: " + String(self.attributes)
for child in self.children:
result += '\n' + '\t '.repeat(level) + child.as_string(level + 1)
return result
class XmlText extends XmlNode:
var data: String
func is_text() -> bool: return true
func as_string(_level: int = 0) -> String:
2023-08-26 15:16:36 +00:00
return "XmlText \"%s\"" % self.data
## Wrapper over XMLParser for TCP stream oriented XML data.
# todo: Save namespaces.
# todo: Ability to parse from partially arrived data, with saving for future resume.
2023-08-26 15:16:36 +00:00
class Parser extends Reference:
var root: XmlElement = null
var _element_stack: Array # of XmlElement
## Returns true if root element is closed, otherwise more tags are expected to come later.
func parse_a_bit(data): # -> bool or Error:
var error: int
var parser := XMLParser.new()
if data is String: data = data.to_utf8()
error = parser.open_buffer(data)
if error != OK:
push_error("Error opening a buffer for XML parsing")
return error
while parser.read() == OK:
if parser.get_node_type() == XMLParser.NODE_ELEMENT:
var element := XmlElement.new()
if self.root != null:
self._element_stack[self._element_stack.size() - 1].children.push_back(element)
else: self.root = element
if not parser.is_empty():
self._element_stack.push_back(element)
element.node_name = parser.get_node_name()
var attribute_count := parser.get_attribute_count()
for idx in range(attribute_count):
element.attributes[parser.get_attribute_name(idx)] = parser.get_attribute_value(idx)
elif parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
if self._element_stack.size() == 0:
push_error("Closing element </%s> closes nothing" % [parser.get_node_name()])
return ERR_PARSE_ERROR
var popped := self._element_stack.pop_back() as XmlElement
if popped.node_name != parser.get_node_name():
push_error("Element <%s> closes sooner than <%s>" % [parser.get_node_name(), popped.node_name])
return ERR_PARSE_ERROR
elif parser.get_node_type() == XMLParser.NODE_TEXT:
if self._element_stack.size() == 0:
push_error("Text node should be a child of an element")
return ERR_PARSE_ERROR
var text = XmlText.new()
text.data = parser.get_node_data()
self._element_stack[self._element_stack.size() - 1].children.push_back(text)
elif parser.get_node_type() == XMLParser.NODE_UNKNOWN:
## Needed for things like <?xml ?> version specification.
pass
2023-08-26 15:16:36 +00:00
else:
push_error("Node type unimplemented")
2023-08-26 15:16:36 +00:00
return self._element_stack.size() == 0