# -*- coding: utf-8 -*-
"""
UTc! module: src.config.parser

Purpose
=======
 Provides commonly needed XML-DOM-parsing features for simple trees.
 
Legal
=====
 All code, unless otherwise indicated, is original, and subject to the
 terms of the GPLv3, which is provided in COPYING.
 
 (C) Neil Tallim, 2009
"""
import xml.dom.minidom

class Parser(object):
	"""
	Offers a simple interface for parsing arbitrary XML documents.
	"""
	_dom = None #: The DOM on which this object operates.
	
	def __init__(self, filepath):
		"""
		Opens an XML document.
		
		@type filepath: basestring
		@param filepath: The path to the XML document to be parsed.
		
		@raise Exception: If an error occurs while opening or parsing the
		    specified XML document.
		"""
		self._dom = xml.dom.minidom.parse(filepath)
		
	def getCollection(self, tree, path, target):
		"""
		Returns a list of all nodes of a specific type attached to a specified
		node.
		
		@type tree: node|None
		@param tree: The XML tree to travserse. If None, the root of the document
		    is used.
		@type path: basestring
		@param path: The relative path to the nodes to be enumerated.
		@type target: basestring
		@param target: The name of the elements to be enumerated.
		
		@rtype: list
		@return: The requested nodes.
		
		@raise Exception: If the specified path is invalid.
		"""
		(branch, path) = _popPath(path)
		if not branch: #End of path reached.
			return [node for node in tree.childNodes if node.nodeName == target]
		else:
			return self.getCollection((tree or self._dom).getElementsByTagName(branch)[0], path, target)
			
	def getRoot(self):
		"""
		Returns the node that represents the root of this XML document.
		
		@rtype: node
		@return: The XML document's root.
		"""
		return self._dom
		
	def readData(self, tree, path):
		"""
		Extracts a string containing the data in the specified text node.
		
		@type tree: node|None
		@param tree: The XML tree to travserse. If None, the root of the document
		    is used.
		@type path: basestring
		@param path: The relative path to the data to be extracted.
		
		@rtype: unicode
		@return: The requested text.
		
		@raise Exception: If the specified path is invalid.
		"""
		(branch, path) = _popPath(path)
		if not branch: #End of path reached.
			data = []
			nodes = [node for node in tree.childNodes if node.nodeType == node.TEXT_NODE]
			if nodes:
				for line in nodes[0].data.splitlines():
					if line:
						data.append(line.strip())
			return ' '.join(data)
		else:
			return self.readData((tree or self._dom).getElementsByTagName(branch)[0], path)
			
def _popPath(path):
	"""
	Determines the branch of the XML tree to traverse, given a 'x.y'-form path.
	
	@type path: basestring
	@param path: The logical path to the requested section of the XML tree.
	
	@rtype: tuple
	@return: The name of the element that denotes the branch to follow, plus any
	    remaining nodes that must be traversed, in path-form. A value of None for
	    the path indicates that no further traversal is needed, and a value of
	    None for the element indicates that the goal has been reached.
	"""
	if not path:
		return (None, None)
		
	fragments = path.split('.', 1)
	if len(fragments) == 1:
		return (fragments[0], None)
	return fragments
	