#!/d/Bin/Python/python.exe
"""

Essentially, a wrapper around an Element Node of the DOM, that helps in making
the usage of DOM a bit simpler... nothing complicated, just a set of 'macros'

The system tries to use the expat parser whenever it can, because it is faster. However,
not all distributions may have it, so it falls back on a Sax2 parser which is slower
but written in Python (afaik), ie, it is available everywhere.

You should install U{PyXML<http://pyxml.sourceforge.net/>} for this set of tools. (One of the
features (essential in my view) that is implemented in the full version is XPath that is
not part of the 'basic' distribution of Python.)

@author: U{Ivan Herman<a href="http://www.ivan-herman.net">}
@license: This software is available for use under the 
U{W3C Software License<http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231>}
"""
#
# $Date: 2011-02-13 09:10:16 $
#
import sys
from xml.dom.ext import PrettyPrint, Print
from xml.dom     import Node
from xml.dom     import Document
from xml.dom     import DocumentType
from StringIO    import StringIO
#from xml         import xpath
from xml.dom     import NotFoundErr


# try to get expat first. If not, get fall back
try :
	from xml.dom.ext.reader import PyExpat
	Parser = PyExpat.Reader
except ImportError :
	from xml.dom.ext.reader import Sax2
	Parser = Sax2.Reader
	import warnings
	warnings.warn("Expat parser could not be loaded, using Sax2. Might slow down things...")

######################################################################################
XHTMLVersions = {
"Strict" :       ("-//W3C//DTD XHTML 1.0 Strict//EN"      ,"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"),
"strict" :       ("-//W3C//DTD XHTML 1.0 Strict//EN"      ,"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"),
"transitional" : ("-//W3C//DTD XHTML 1.0 Transitional//EN","http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"),
"Transitional" : ("-//W3C//DTD XHTML 1.0 Transitional//EN","http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"),
"Frameset" :     ("-//W3C//DTD XHTML 1.0 Frameset//EN"    ,"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"),
"frameset" :     ("-//W3C//DTD XHTML 1.0 Frameset//EN"    ,"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"),
"rdfa" :         ("-//W3C//DTD XHTML+RDFa 1.0//EN"    	  ,"http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd"),
"RDFa" :         ("-//W3C//DTD XHTML+RDFa 1.0//EN"    	  ,"http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd")
}

XHTML_strict       = "strict"
XHTML_transitional = "transitional"
XHTML_frameset     = "frameset"

# Sigh. This should be used for a proper printout: these elements should stay inline. But I could not get
# it to work with PyXML :-(
_XHTML_INLINE = ['tt', 'i', 'b', 'big', 'small', 'em', 'strong', 'dfn', 'code', 'samp', 'kbd', 'var', 'cite', 
'abbr', 'acronym', 'a', 'img', 'object', 'applet', 'script', 'map', 'q', 'sub', 'sup' 'span', 'bdo', 'input', 
'select', 'textarea', 'label', 'button']

# Pretty arbitrary list, things that I use a lot...
svgns   = "http://www.w3.org/2000/svg"
xlinkns = "http://www.w3.org/1999/xlink"
xhtmlns = "http://www.w3.org/1999/xhtml"

######################################################################################
from xml.dom.ext.Printer import PrintVisitor

class _PrintFragmentVisitor(PrintVisitor) :
	"""
	A subclass of PyXML's PrintVisitor class that does not print a prolog. Used to
	print out an XML Fragment
	"""
	def __init__(self, stream, encoding):
		PrintVisitor.__init__(self,stream,encoding,indent="    ")
		
	def visitProlog(self) :
		return
	
def _printFragment(root, stream=sys.stdout, encoding='UTF-8'):
	"""
	Programming by example: a copy of the PyXML routine to print a fragment (ie, no processing instructions, 
	for example), using the L{_PrintFragmentVisitor} class.
	"""
	if not hasattr(root, "nodeType"):
		return
	from xml.dom.ext import Printer, SeekNss
	nss_hints = SeekNss(root)
	visitor = _PrintFragmentVisitor(stream, encoding)
	Printer.PrintWalker(visitor, root).run()
	stream.write('\n')
	return

######################################################################################

class ElementNode :
	"""
	Wrapper around an Element Node to make programs a bit easier to develop. Most of the Node
	methods and attributes are reproduced, and some utility type facilities are added...
	
	A number of methods are identical to the DOM Nodes and are just brought to this class, too
	
	The documentation of the Class methods are based on the DOM of W3C, so details are not given here.
	See, for example, the U{PyXML Documentation<http://pyxml.sourceforge.net/topics/howto/xml-howto.html>}
	especially the U{DOM Tree Manipulation<http://pyxml.sourceforge.net/topics/howto/node20.html>}
	documentation.
	
	Some of the DOM Node methods and attributes are used verbatim (eg, 'parentNode','firstChild','lastChild',
	'previousSibling','nextSibling') though all of them return an instance of ElementNode. A number of
	methods are identical to the DOM methods, except that they accept DOM Nodes I{or} ElementNode
	instances as parameters.
	"""
	def __init__(self,node) :
		"""
		@param node: an Element Node in PyXML
		"""
		self.node  = node
		self.xmlns = None

	def appendChild(self,newChild) :
		"""
		Append a child. Maps to the corresponding DOM method
		
		@param newChild: the new child
		@type newChild: PyXML DOM Node or an ElementNode instance
		"""
		try :
			self.node.appendChild(newChild.node)
		except :
			# this is simply a node in its original form
			self.node.appendChild(newChild)
			
	def removeChild(self,oldChild) :
		"""
		Remove a child. Maps to the corresponding DOM method
		
		@param oldChild: the old child
		@type oldChild: PyXML DOM Node or an ElementNode instance
		"""
		try :
			self.node.removeChild(oldChild.node)
		except :
			# this is simply a node in its original form
			self.node.removeChild(oldChild)

	def replaceChild(self,newChild,oldChild) :
		"""
		Replace a child. Maps to the corresponding DOM method
		
		@param newChild: the old child
		@type newChild: PyXML DOM Node or an ElementNode instance
		@param oldChild: the new child
		@type oldChild: PyXML DOM Node or an ElementNode instance
		"""
		try :
			realNewChild = newChild.node
		except :
			realNewChild = newChild
		try :
			realOldChild = oldChild.node
		except :
			realOldChild = oldChild			
		self.node.replaceChild(realNewChild,realOldChild)

	def insertBefore(self,newChild,refChild) :
		"""
		Insert before a child. Maps to the corresponding DOM method
		
		@param newChild: the old child
		@type newChild: PyXML DOM Node or an ElementNode instance
		@param refChild: the reference child
		@type refChild: PyXML DOM Node or an ElementNode instance
		"""
		try :
			realNewChild = newChild.node
		except :
			realNewChild = newChild
		try :
			realOldChild = refChild.node
		except :
			realOldChild = refChild			
		self.node.insertBefore(realNewChild,realOldChild)

	def hasChildNode(self) :
		"""
		Has a child? Maps to the corresponding DOM method.
		
		@return: Has a child?
		@rtype: Boolean
		"""
		return self.node.firstChild != None

	def hasAttribute(self,attrName) :
		"""
		Has a specific attribute? Maps to the corresponding DOM method.
		
		@param attrName: attribute
		@type attrName: string
		@return: Has a specific attribute? 
		@rtype: Boolean
		"""
		return self.node.hasAttribute(attrName)

	##
	# Has a specific attribute in a namespace? Maps to the corresponding DOM method
	# @param ns namespace
	# @param attrName the name of the attribute
	# @defreturn Boolean
	def hasAttributeNS(self,ns,attrName) :
		"""
		Has a specific attribute in a namespace? Maps to the corresponding DOM method.
		
		@param ns: namespace
		@type ns: string
		@param attrName: attribute
		@type attrName: string
		@return: Has a specific attribute? 
		@rtype: Boolean
		"""
		return self.node.hasAttributeNS(ns,attrName)
		
	def getAttribute(self,attrName) :
		"""Get attribute. Maps to the corresponding DOM method
		
		@param attrName: the name of the attribute
		@type attrName: string
		@return: string
		"""
		return self.node.getAttribute(attrName)

	def getAttributeNS(self,ns,attrName) :
		"""Get attribute in namespace. Maps to the corresponding DOM method
		
		@param ns: namespace
		@type ns: string
		@param attrName: the name of the attribute
		@type attrName: string
		@return: string
		"""
		return self.node.getAttributeNS(ns,attrName)

	def isAttributeValue(self,name,value) :
		"""Is the attribute of a specific value?
		
		@param name: the name of the attribute
		@type name: string
		@param value: the possible value of the attribute
		@type value: string
		@return: comparison result; if the attribute is not defined, returns False
		@rtype: boolean
		"""
		if self.hasAttribute(name) :
			return self.getAttribute(name) == value
		else :
			return False

	def isAttributeValueNS(self,ns,name,value) :
		"""Is the attribute with a namespace of a specific value?
		
		@param ns: namespace
		@type ns: string
		@param name: the name of the attribute
		@type name: string
		@param value: the possible value of the attribute
		@type value: string
		@return: comparison result; if the attribute is not defined, returns False
		@rtype: boolean
		"""
		if self.hasAttributeNS(ns,name) :
			return self.getAttributeNS(ns,name) == value
		else :
			return False
		
	def setAttribute(self,attrName,attrValue) :
		"""Set attribute. Maps to the corresponding DOM method
		
		@param attrName: the name of the attribute
		@type attrName: string
		@param attrValue: the possible value of the attribute
		@type attrValue: string
		"""
		self.node.setAttribute(attrName,"%s" % attrValue)

	def setAttributeNS(self,ns,attrName,attrValue) :
		"""Set attribute with namespace. Maps to the corresponding DOM method
		
		@param ns: namespace
		@type ns: string
		@param attrName: the name of the attribute
		@type attrName: string
		@param attrValue: the possible value of the attribute
		@type attrValue: string
		"""
		self.node.setAttributeNS(ns,attrName,"%s" % attrValue)
		
	##
	# Set a series of attributes through keyword arguments.
	# @param attrs dictionary of the attributes. Each will be converted to strings on the fly.
	def setAttributes(self,**attrs) :
		"""Set an arbitrary number of attributes, through keyword arguments
		
		@param attrs: keyword set for attributes
		"""
		for key in attrs.keys() :
			self.node.setAttribute(key,"%s" % attrs[key])

	def setAttributesNS(self,ns,**attrs) :
		"""Set an arbitrary number of attributes, through keyword arguments, and all in a specific (and identical)
		namespace.
		
		@param ns: namespace
		@type ns: string
		@param attrs: keyword set for attributes
		"""
		for key in attrs.keys() :
			self.node.setAttributeNS(ns,key,"%s" % attrs[key])
			
	_justCopy = ['parentNode','firstChild','lastChild','previousSibling','nextSibling']	
	def __getattr__(self,name) :
		try :
			if name in self._justCopy :
				#retval = self.node.__dict__[name]
				retval = self.node.__getattr__(name)
				if retval == None :
					return None
				else :
					return ElementNode(retval)
			elif name == 'childNodes' :
				def lambd(node) :
					if node.nodeType == Node.ELEMENT_NODE :
						return ElementNode(node)
					else :
						return node
				return map(lambd,self.node.childNodes)
			else :
				return self.node.__getattr__(name)
		except AttributeError:
			raise AttributeError("Class ElementNode has no attribute '" + name + "'")
		
	#############################################################################	
	# This is, in fact, a Document level utility, but wrapped around here...
	#
	##
	# Get elements by tag name <em>in the whole document</em>. This is, in fact,
	# a Document level utility, but it returns instances of ElementNode-s. For a 
	# more complex way of returning nodes, see {@linkplain #ElementNode.getNodes_Xpath the XPath interface}.
	# @param name element name
	# @return list of ElementNode instances
	def getElementsByTagName(self,name) :
		"""
		Get elements by tag name I{in the whole document}. This is, in fact,
		a Document level utility. This is almost identical to the 'real' DOM method, except that the
		resulting nodes are all wrapped into an ElementNode before returning them back to the caller
		
		@param name: namespace
		@type name: string
		@return: list of ElementNode instances
		"""
		tags = self.node.ownerDocument.getElementsByTagName(name)
		return map(lambda t: ElementNode(t),tags)

	#############################################################################
	# Bringing XPath in...
	#def getNodes_Xpath(self,xpathExpression,fromDocument = False) :
	#	"""Gets a number of nodes through an XPath expression. By default, the context
	#	is the current node. If fromDocument is True, the context is the full document.
	#	It returns a list of nodes wrapped into ElementNode class instances for Element
	#	Nodes
	#	
	#	@param xpathExpression: XPath 1.0 expression
	#	@type xpathExpression: string
	#	@param fromDocument: if True, the search starts at the Document level, and the current node otherwise
	#	@type fromDocument: Boolean
	#	@return: list of ElementNode instances
	#	"""
	#	if fromDocument == False :
	#		nodes = xpath.Evaluate(xpathExpression,self.node)
	#	else :
	#		nodes = xpath.Evaluate(xpathExpression,self.node.ownerDocument)
	#	def lambd(node) :
	#		if node.nodeType == Node.ELEMENT_NODE :
	#			return ElementNode(node)
	#		else :
	#			return node
	#	return map(lambd,nodes)

	#############################################################################	
	# These are additional utilities
	#
	def appendString(self,string) :
		"""Append a string to the current node (creating a Text Node on the fly). The
		string is encoded into utf-8 if it is not a unicode object already.
		
		@param string: the string to be added
		@type string: unicode or string
		"""
		try :
			st = string.encode('utf-8')
		except :
			st = string
		self.node.appendChild(self.node.ownerDocument.createTextNode(st))

	def insertStringBefore(self,string,refElement) :
		"""Insert a string before a reference element (creating a Text Node on the fly). The
		string is encoded into utf-8 if it is not a unicode object already.
		
		@param string: the string to be added
		@type string: unicode or string
		@param refElement: reference Node
		@type refElement: PyXML Node or ElementNode
		"""
		try :
			st = string.encode('utf-8')
		except :
			st = string
		nd = self.node.ownerDocument.createTextNode(st)
		try :
			self.node.insertBefore(nd,refElement.node)
		except :
			self.node.insertBefore(nd,refElement)

	def appendComment(self,comment) :
		"""Append a comment to the current element (ie, create a comment node child with the
	    given content). 
		@param comment: the text to be added
		@type comment: string
		"""
		self.node.appendChild(self.node.ownerDocument.createComment(comment))
		
	def insertCommentBefore(self,string,refElement) :
		"""Insert a comment before a reference element (ie, create a comment node child with the
	    given content). 
		@param string: the text to be added
		@type string: string
		@param refElement: reference Node
		@type refElement: PyXML Node or ElementNode
		"""
		nd = self.node.ownerDocument.createComment(string)
		try :
			self.node.insertBefore(nd,refElement.node)
		except :
			self.node.insertBefore(nd,refElement)

	def appendCDATA(self,string) :
		"""Append a CDATA section to the current element (ie, create a CDATA node child with the
	    given content). 
		@param string: the text to be added
		@type string: string
		"""
		self.node.appendChild(self.node.ownerDocument.createCDATASection(string))

	def insertCDATABefore(self,string,refElement) :
		"""Insert a CDATA section before a reference element (ie, create a CDATA node child with the
	    given content). 
		@param string: the text to be added
		@type string: string
		@param refElement: reference Node
		@type refElement: PyXML Node or ElementNode
		"""
		cdata = self.node.ownerDocument.createCDATASection(string)
		try :
			self.node.insertBefore(cdata,refElement.node)
		except :
			self.node.insertBefore(cdata,refElement)

	def appendElement(self,nodeName,**attrs) :
		"""Create and append an element of name nodeName. Returns the wrapped node. Attributes of the 
		new element may be set through an optional set of keywords.
		
		@param nodeName: name of the node
		@type nodeName: string
		@param attrs: name value pairs for attributes
		@return: new element
		@rtype: ElementNode
		"""
		retval = self.node.ownerDocument.createElement(nodeName)
		self.node.appendChild(retval)
		for key in attrs.keys() :
			retval.setAttribute(key,"%s" % attrs[key])
		return ElementNode(retval)

	def appendElementNS(self,ns,nodeName,**attrs) :
		"""Create and append an element of name nodeName in a namespace. Returns the wrapped node. Attributes of the 
		new element may be set through an optional set of keywords.
		
		@param ns: namespace
		@type ns: string
		@param nodeName: name of the node
		@type nodeName: string
		@param attrs: name value pairs for attributes
		@return: new element
		@rtype: ElementNode
		"""
		retval = self.node.ownerDocument.createElementNS(ns,nodeName)
		self.node.appendChild(retval)
		for key in attrs.keys() :
			retval.setAttribute(key,"%s" % attrs[key])
		return ElementNode(retval)
				
	def insertElementBefore(self,nodeName,refElement,**attrs) :
		"""Insert an element before a reference element. Returns the inserted node. Attributes of the 
		new element may be set through an optional set of keywords.
		
		@param nodeName: name of the node
		@type nodeName: string
		@param refElement: reference element
		@type refElement: PyXML Node or ElementNode
		@param attrs: name value pairs for attributes
		@return: new element
		@rtype: ElementNode
		"""
		retval = self.node.ownerDocument.createElement(nodeName)
		for key in attrs.keys() :
			retval.setAttribute(key,"%s" % attrs[key])
		try :
			self.node.insertBefore(retval,refElement.node)
		except :
			self.node.insertBefore(retval,refElement)
		return ElementNode(retval)

	def appendProcessingInstruction(self,target,data,top=True) :
		"""Append a Processing Instruction either to the current element (if top = False),
		or on the top level of the document. The latter is default.
		
		@param target: target of the PI
		@param data: data of the PI
		@param top: whether to put the PI to the top or the current element.
		@type top: Boolean
		"""
		pi = self.node.ownerDocument.createProcessingInstruction(target,data)
		if top == False :
			self.node.appendChild(pi)
			return pi
		else :
			for c in self.node.ownerDocument.childNodes :
				if c.nodeType == Node.ELEMENT_NODE :
					self.node.ownerDocument.insertBefore(pi,c)
					return pi
		
	def insertXMLFragmentBefore(self,xmlstr,refElement) :
		"""xmlstr is an xml fragment, stored as a Python String. The fragment is parsed and the 
		resulting subtree is added as a child before refElement.
		
		@param xmlstr: an XML Fragment
		@type xmlstr: string or unicode (if the latter, it will be encoded in utf-8)
		@param refElement: reference element
		@type refElement: PyXML Node or ElementNode		
		"""
		# turn the xml fragment into real nodes
		if type(xmlstr) == unicode :
			inp = xmlstr.encode('utf-8')
		else :
			inp = xmlstr		
		top = parse(StringIO(inp))
		# and copy it...
		self.insertCopyBefore(top,refElement)
		
	def appendXMLFragment(self,xmlstr) :
		"""xmlstr is an xml fragment, stored as a Python String. The fragment is parsed and the 
		resulting subtree is appended as a child.
		
		@param xmlstr: an XML Fragment
		@type xmlstr: string or unicode (if the latter, it will be encoded in utf-8)
		"""
		if type(xmlstr) == unicode :
			inp = xmlstr.encode('utf-8')
		else :
			inp = xmlstr		
		
		# turn the xml fragment into real nodes
		top = parse(StringIO(inp))
		# and copy it...
		self.appendCopy(top)
	
	def setLanguage(self,code,html=False) :
		"""Set the language of the element. 'en' is skipped as default.
		
		@param code: language code
		@param html: if True, then the 'lang' attribute is also set, not only the xml:lang
		@type html: Boolean
		
		"""
		if code == "en" :
			return
		self.node.setAttribute("xml:lang",code)
		if html :
			self.node.setAttribute("lang",code)

	def getText(self,deep = False):
		"""Get the text information from the node. All text nodes are collected, concatenated, and stripped.
		There is no 'recursive' search by default, but setting 'deep' to True does this.
		the result is encoded in utf-8.
		
		@param deep: whether to recurse or not
		@type deep: Boolean
		@return string
		
		"""
		rc = ""
		for node in self.node.childNodes:
			if node.nodeType == Node.TEXT_NODE:
				if isinstance(node.nodeValue,unicode) :
					rc = rc + node.nodeValue.encode('utf-8')
				else :
					rc = rc + node.nodeValue
			elif node.nodeType == Node.ELEMENT_NODE and deep == True :
				rc = rc + ElementNode(node).getText(True)
		return rc.strip()
		
	def cloneNode(self,deep,newOwner=None) :
		"""Clone a node: maps on the PyXML method, but returns an ElementNode instance.
		
		@param deep: deep or shallow clone
		@param newOwner: see the corresponding PyXML method
		@return ElementNode
		"""
		cloned = self.node.cloneNode(deep,newOwner)
		return ElementNode(cloned)
					
	def insertCopyBefore(self,target,refElement,deep=True,attributeFilter=None) :
		"""
		Insert a tree before a reference Element. If the target's document is identical, this falls back on a simple insert.
		Otherwise, new elements are created (possibly recursively) with the same names, and
		attached to the current node.
		
		@param target: target node to copy
		@type target: ElementNode
		@param refElement: reference element
		@type refElement: PyXML Node or ElementNode		
		@param deep: deep or shallow clone
		@type deep: Boolean
		@param attributeFilter: if not None, this should be a method, invoked with attribute names
		and values and should return True or False. In the latter case the attributes are ignored when
		copying.
		"""
		if self.node.ownerDocument == target.ownerDocument :
			self.insertBefore(target,refElement)
		else :
			if target.nodeType == Node.ELEMENT_NODE :
				newNode = self.insertElementBefore(target.nodeName,refElement)
				# copy all the attributes with a filter
				if attributeFilter != None :
					for t in target.attributes :
						(name,value) = attributeFilter(t.name,t.value)
						newNode.setAttribute(name,value)
				else :
					for t in target.attributes :
						newNode.setAttribute(t.name,t.value)
				# if copy must be deep, then go recursively from here
				if deep :
					map(lambda c: newNode.appendCopy(c,deep,attributeFilter),target.childNodes)
			elif target.nodeType == Node.TEXT_NODE :
				txtvalue = target.data
				self.insertStringBefore(txtvalue,refElement)
			elif target.nodeType == Node.CDATA_SECTION_NODE :
				cdvalue = target.data
				self.insertCDATABefore(cdvalue,refElement)

	def appendCopy(self,target,deep=True,attributeFilter=None) :
		"""
		Append a tree. If the target's document is identical, this falls back on a simple append.
		Otherwise, new elements are created (possibly recursively) with the same names, and
		attached to the current node.
		
		@param target: target node to copy
		@type target: ElementNode
		@param deep: deep or shallow clone
		@type deep: Boolean
		@param attributeFilter: if not None, this should be a method, invoked with attribute names
		and values and should return True or False. In the latter case the attributes are ignored when
		copying.
		"""
		if self.node.ownerDocument == target.ownerDocument :
			self.appendChild(target)
		else :
			if target.nodeType == Node.ELEMENT_NODE :
				newNode = self.appendElement(target.nodeName)
				# copy all the attributes with a filter
				if attributeFilter != None :
					for t in target.attributes :
						(name,value) = attributeFilter(t.name,t.value)
						newNode.setAttribute(name,value)
				else :
					for t in target.attributes :
						newNode.setAttribute(t.name,t.value)
				# if copy must be deep, then go recursively from here
				if deep :
					map(lambda c: newNode.appendCopy(c,deep,attributeFilter),target.childNodes)
			elif target.nodeType == Node.TEXT_NODE :
				txtvalue = target.data
				self.appendString(txtvalue)
			elif target.nodeType == Node.CDATA_SECTION_NODE :
				cdvalue = target.data
				self.appendCDATA(cdvalue)

		

	#####################################################################################
	def prettyPrint(self, outp=sys.stdout, encoding="UTF-8", indent='  ',doctype = None,inlines=None) :
		"""
		Pretty Print the full document
		
		@param outp: Output stream. Might be a python file object, or a string; in the second case
		a file is created on the file
		@param encoding: character encoding of the file
		@param indent: string used for indentation
		@param doctype: Document type to be added; if not None, should be a tuple with the public and system ID-s
		@param inlines: an array of element names that are 'inline', ie, there should be no new lines surrounding
		them (remark: it does not seem to work properly...)
		"""
		if isinstance(outp,basestring) :
			stream = file(outp,"w")
		else :
			stream = outp
		if doctype  :
			dt = self.node.ownerDocument._get_doctype()
			(dt._publicId,dt._systemId) = doctype
		PrettyPrint(self.node.ownerDocument,stream,encoding,indent,inlines)
		if isinstance(outp,basestring) :
			stream.close()

	def plainPrint(self, outp=sys.stdout, encoding="UTF-8",doctype = None) :
		"""
		Plain Print the full document
		
		@param outp: Output stream. Might be a python file object, or a string; in the second case
		a file is created on the file
		@param encoding: character encoding of the file
		@param doctype: Document type to be added; if not None, should be a tuple with the public and system ID-s
		"""
		if isinstance(outp,basestring) :
			stream = file(outp,"w")
		else :
			stream = outp			
		if doctype :
			dt = self.node.ownerDocument._get_doctype()
			(dt._publicId,dt._systemId) = doctype			
		Print(self.node.ownerDocument,stream,encoding)
		if isinstance(outp,basestring) :
			stream.close()

	def printFragment(self, outp=sys.stdout, encoding="UTF-8") :
		"""
		Print XML fragment the full document. The difference is that no xml instruction is printed (ie,
		the result can be included in other XML documents).
		
		@param outp: Output stream. Might be a python file object, or a string; in the second case
		a file is created on the file
		@param encoding: character encoding of the file
		"""
		if isinstance(outp,basestring) :
			stream = file(outp,"w")
		else :
			stream = outp			
		_printFragment(self.node.ownerDocument,stream,encoding)
		if isinstance(outp,basestring) :
			stream.close()

	def XHTMLprettyPrint(self, outp=sys.stdout, encoding="UTF-8", indent='  ', version = XHTML_transitional) :
		"""
		Pretty Print the full document in XHTML
		@param outp: Output stream. Might be a python file object, or a string; in the second case
		a file is created on the file
		@param encoding: character encoding of the file
		@param indent: string used for indentation
		@param version: can be 'strict', 'transitional', or 'frameset'
		"""
		if not version in XHTMLVersions.keys() :
			raise NotFoundErr("Invalid XHTML version %s" % version)
		self.prettyPrint(outp,encoding,indent,XHTMLVersions[version],_XHTML_INLINE)

	def XHTMLplainPrint(self, outp=sys.stdout, encoding="UTF-8", version = XHTML_transitional ) :
		"""
		Plain Print the full document in XHTML
		@param outp: Output stream. Might be a python file object, or a string; in the second case
		a file is created on the file
		@param encoding: character encoding of the file
		@param version: can be 'strict', 'transitional', or 'frameset'
		"""
		if not version in XHTMLVersions.keys() :
			raise NotFoundErr("Invalid XHTML version %s" % version)
		self.plainPrint(outp,encoding,XHTMLVersions[version])

	####################################################################################
	# Strictly speaking these aren't really DOM functions but they occur so often
	# for the objects I handle, that I decided to put them here....
	# Utilities...
	##
	# Is the class attribute set to a specific value?
	# @param searchedClass query value for the 'class' attribute
	# @defreturn Boolean
	def isOfClass(self,searchedClass) :
		"""Is the class attribute set to a specific value?
		A class specification may list several classes, check if something is among those...
		
		@param searchedClass: class name
		@rtype: Boolean 
		
		"""
		if self.hasAttribute("class") :
			return searchedClass in self.getAttribute("class").split()
		else :
			return False
	
	def _getStyleValue(self,style,prop) :
		"""Parse a style attribute value for a property 'prop'. returns either the value or None
		
		@param style: style string
		@param prop: property to look for
		@return: value of property or None		
		"""
		props = style.split(';')
		for pr in props :
			prs = pr.split(':')
			try :
				if prop == prs[0].strip() :
					return prs[1].strip()
			except :
				continue
		return None
		
	def getStyleValue(self,prop) :
		"""See if node has a 'style' attribute and parse it for a property 'prop'.
		returns either the value or None.
		
		@param prop: property to look for
		@return: value of property or None
		"""
		if self.hasAttribute("style") :
			return self._getStyleValue(self.getAttribute("style"),prop)
		else :
			return None
		
	def isStyleValue(self,prop,value) :
		"""
		Is a style property set to a specific value? The possible 'style' attribute
		is parsed for a property and the values are compared
		@param prop: style property name
		@param value: style property value
		@rtype: Boolean
		"""
		val = self.getStyleValue(prop)
		if val :
			return (val == value)
		else :
			return False
			
	def addToStyle(self,prop,value) :
		"""
		Append a new style property name/value to a style attribute (possibly creating it on the fly)
		
		@param prop: style property name
		@param value: style property value
		"""
		if self.hasAttribute("style") :
			self.setAttribute("style",self.getAttribute("style") + ("; %s:%s" % (prop,value)))
		else :
			self.setAttribute("style","%s:%s" % (prop,value))
			
	####################################################################################	
	def __str__(self) :
		return "[ElementNode] " + self.node.__repr__()

	def __repr__(self) :
		return "<ElementNode wrapper around: " + self.node.__repr__() + ">"


##################################################################################################
def parse(inp) :
	"""Parse a file and return the first element Node child as a 'root' of the document.
	
	@param inp: a file object, a StringIO object, or a string/unicode; in the last case a file is opened with that name.
	@returns: top level node in the document
	@rtype: L{ElementNode<PyXMLUtils.ElementNode>}
	"""
	dom = _parse(inp)
	# dom's children include all kinds of other stuffs: document type node, comments, etc.
	# for practical purposes, we have to find the (only) element node; that is the top level
	# node in the document. That is the one we really care for
	for c in dom.childNodes :
		if c.nodeType == Node.ELEMENT_NODE :
			return ElementNode(c)
	# At some point a proper exception might be useful here...
	return None


##################################################################################################
# Get a dom from a file
def _parse(inp) :
	"""Get a dom from a file
	@param inp: string or a file object
	"""
	if isinstance(inp,basestring) :
		# ie, it is either a string or a unicode character
		# a file object has to be created
		inpf = file(inp)
	else :
		# it is either a file object already or a StringIO
		inpf = inp
	reader = Parser()
	return reader.fromStream(inpf)

##################################################################################################
def createDocument(element, publicId=None, systemId=None, entities=[], notations=[] ) :
	"""
	Create a new DOM with a root element. Returns the first child element as a 'root' of the document
	@param element: name of the root element
	@param publicId: Public ID for the document DTD
	@param systemId: System ID for the document DTD
	@param entities: entities for this document (see the U{PyXML documentation<http://pyxml.sourceforge.net/>} 
	for further details)
	@param notations: notations for this document (see the U{PyXML documentation<http://pyxml.sourceforge.net/>} 
	for further details)
	@returns: top level node in the document
	@rtype: L{ElementNode<PyXMLUtils.ElementNode>}
	"""
	doctype = DocumentType.DocumentType(element,entities,notations,publicId,systemId)
	dom = Document.Document(doctype)
	el = dom.createElement(element)
	dom.appendChild(el)
	return ElementNode(el)

##################################################################################################
if __name__ == '__main__' :
	dom = createDocument("top")
	a1 = dom.appendElement("a1")
	a1.appendString("namivan?")
	b1 = dom.appendElement("b1")
	b1.setAttribute("w","500")
	dom.printFragment("fragment.xml")
	dom.prettyPrint("print.xml")
	

