93 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			GDScript
		
	
	
	
	
	
			
		
		
	
	
			93 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			GDScript
		
	
	
	
	
	
| 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 = 0) -> String:
 | |
|         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 = 0) -> String:
 | |
|         return "XmlText \"%s\"" % self.data
 | |
| 
 | |
| ## Wrapper over XMLParser for TCP stream oriented XML data.
 | |
| # todo: Save namespaces.
 | |
| 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)
 | |
| 
 | |
|             else:
 | |
|                 push_warning("Node type unimplemented and ignored")
 | |
| 
 | |
|         return self._element_stack.size() == 0
 |