You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1291 lines
53 KiB

#
# genxmlif, Release 0.9.0
# file: xmlifapi.py
#
# API (interface) classes for generic interface package
#
# history:
# 2007-06-29 rl created, classes extracted from xmlifbase.py
#
# Copyright (c) 2005-2008 by Roland Leuthe. All rights reserved.
#
# --------------------------------------------------------------------
# The generic XML interface is
#
# Copyright (c) 2005-2008 by Roland Leuthe
#
# By obtaining, using, and/or copying this software and/or its
# associated documentation, you agree that you have read, understood,
# and will comply with the following terms and conditions:
#
# Permission to use, copy, modify, and distribute this software and
# its associated documentation for any purpose and without fee is
# hereby granted, provided that the above copyright notice appears in
# all copies, and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of
# the author not be used in advertising or publicity
# pertaining to distribution of the software without specific, written
# prior permission.
#
# THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
# ABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
# OF THIS SOFTWARE.
# --------------------------------------------------------------------
__author__ = "Roland Leuthe <roland@leuthe-net.de>"
__date__ = "08. August 2008"
__version__ = "0.9.0"
import string
import os
import re
import copy
from types import TupleType, StringTypes
from xml.dom import EMPTY_PREFIX, EMPTY_NAMESPACE
from xmlifUtils import processWhitespaceAction, NsNameTupleFactory, splitQName, nsNameToQName, escapeCdata, escapeAttribute
########################################
# XML interface base class
# All not implemented methods have to be overloaded by the derived class!!
#
class XmlInterfaceBase:
"""XML interface base class.
All not implemented methods have to be overloaded by the derived class!!
"""
def __init__(self, verbose, useCaching, processXInclude):
"""Constructor of class XmlInterfaceBase.
Input parameter:
'verbose': 0 or 1: controls verbose print output for module genxmlif
'useCaching': 0 or 1: controls usage of caching for module genxmlif
'processXInclude': 0 or 1: controls XInclude processing during parsing
"""
self.verbose = verbose
self.useCaching = useCaching
self.processXInclude = processXInclude
# set default wrapper classes
self.setTreeWrapperClass (XmlTreeWrapper)
self.setElementWrapperClass (XmlElementWrapper)
def createXmlTree (self, namespace, xmlRootTagName, attributeDict={}, publicId=None, systemId=None):
"""Create a new XML TreeWrapper object (wrapper for DOM document or elementtree).
Input parameter:
'namespace': not yet handled (for future use)
'xmlRootTagName': specifies the tag name of the root element
'attributeDict': contains the attributes of the root node (optional)
'publicId': forwarded to contained DOM tree (unused for elementtree)
'systemId': forwarded to contained DOM tree (unused for elementtree)
Returns the created XML tree wrapper object.
Method has to be implemented by derived classes!
"""
raise NotImplementedError
def parse (self, filePath, baseUrl="", ownerDoc=None):
"""Call the XML parser for 'file'.
Input parameter:
'filePath': a file path or an URI
'baseUrl': if specified, it is used e.g. as base path for schema files referenced inside the XML file.
'ownerDoc': only used in case of 4DOM (forwarded to 4DOM parser).
Returns the respective XML tree wrapper object for the parsed XML file.
Method has to be implemented by derived classes!
"""
raise NotImplementedError
def parseString (self, text, baseUrl="", ownerDoc=None):
"""Call the XML parser for 'text'.
Input parameter:
'text': contains the XML string to be parsed
'baseUrl': if specified, it is used e.g. as base path for schema files referenced inside the XML string.
'ownerDoc': only used in case of 4DOM (forwarded to 4DOM parser).
Returns the respective XML tree wrapper object for the parsed XML 'text' string.
Method has to be implemented by derived classes!
"""
raise NotImplementedError
def setTreeWrapperClass (self, treeWrapperClass):
"""Set the tree wrapper class which shall be used by this interface.
Input parameter:
treeWrapperClass: tree wrapper class
"""
self.treeWrapperClass = treeWrapperClass
def setElementWrapperClass (self, elementWrapperClass):
"""Set the element wrapper classes which shall be used by this interface.
Input parameter:
elementWrapperClass: element wrapper class
"""
self.elementWrapperClass = elementWrapperClass
def getXmlIfType (self):
"""Retrieve the type of the XML interface."""
return self.xmlIfType
########################################
# Tree wrapper API (interface class)
#
class XmlTreeWrapper:
"""XML tree wrapper API.
Contains a DOM tree or an elementtree (depending on used XML parser)
"""
def __init__(self, xmlIf, tree, useCaching):
"""Constructor of wrapper class XmlTreeWrapper.
Input parameter:
'xmlIf': used XML interface class
'tree': DOM tree or elementtree which is wrapped by this object
'useCaching': 1 if caching shall be used inside genxmlif, otherwise 0
"""
self.xmlIf = xmlIf
self.__tree = tree
self.__useCaching = useCaching
def createElement (self, tupleOrLocalName, attributeDict=None, curNs=[]):
"""Create an ElementWrapper object.
Input parameter:
tupleOrLocalName: tag name of element node to be created
(tuple of namespace and localName or only localName if no namespace is used)
attributeDict: attributes for this elements
curNs: namespaces for scope of this element
Returns an ElementWrapper object containing the created element node.
"""
nsName = NsNameTupleFactory(tupleOrLocalName)
elementNode = self.__tree.xmlIfExtCreateElement(nsName, attributeDict, curNs)
return self.xmlIf.elementWrapperClass(elementNode, self, curNs)
def cloneTree (self):
"""Creates a copy of a whole XML DOM tree."""
rootElementWrapperCopy = self.getRootNode().cloneNode(deep=1)
treeWrapperCopy = self.__class__(self.xmlIf,
self.__tree.xmlIfExtCloneTree(rootElementWrapperCopy.element),
self.__useCaching)
for elementWrapper in rootElementWrapperCopy.getIterator():
elementWrapper.treeWrapper = treeWrapperCopy
return treeWrapperCopy
def getRootNode (self):
"""Retrieve the wrapper object of the root element of the contained XML tree.
Returns the ElementWrapper object of the root element.
"""
return self.__tree.xmlIfExtGetRootNode().xmlIfExtElementWrapper
def getTree (self):
"""Retrieve the contained XML tree.
Returns the contained XML tree object (internal DOM tree wrapper or elementtree).
"""
return self.__tree
def printTree (self, prettyPrint=0, printElementValue=1, encoding=None):
"""Return the string representation of the contained XML tree.
Input parameter:
'prettyPrint': aligns the columns of the attributes of childNodes
'printElementValue': controls if the lement values are printed or not.
Returns a string with the string representation of the whole XML tree.
"""
if not encoding:
encoding = "utf-8"
if encoding != "utf-8" and encoding != "us-ascii":
text = "<?xml version='1.0' encoding='%s'?>\n" % encoding
else:
text = ""
return text + self.getRootNode().printNode(deep=1, prettyPrint=prettyPrint, printElementValue=printElementValue, encoding=encoding)
def useCaching (self):
"""Return 1 if caching should be used for the contained XML tree."""
return self.__useCaching
def setExternalCacheUsage (self, used):
"""Set external cache usage for the whole tree
unlink commands are ignored if used by an external cache
Input parameter:
used: 0 or 1 (used by external cache)
"""
self.getRootNode().setExternalCacheUsage (used, deep=1)
def unlink (self):
"""Break circular references of the complete XML tree.
To be called if the XML tree is not longer used => garbage collection!
"""
self.getRootNode().unlink()
def __str__ (self):
"""Return the string representation of the contained XML tree."""
return self.printTree()
########################################
# Element wrapper API (interface class)
#
class XmlElementWrapper:
"""XML element wrapper API.
Contains a XML element node
All not implemented methods have to be overloaded by the derived class!!
"""
def __init__(self, element, treeWrapper, curNs=[], initAttrSeq=1):
"""Constructor of wrapper class XmlElementWrapper.
Input parameter:
element: XML element node which is wrapped by this object
treeWrapper: XML tree wrapper class the current element belongs to
curNs: namespaces for scope of this element
"""
self.element = element
self.element.xmlIfExtElementWrapper = self
self.treeWrapper = treeWrapper
self.nodeUsedByExternalCache = 0
if self.__useCaching():
self.__childrenCache = {}
self.__firstChildCache = {}
self.__qNameAttrCache = {}
self.baseUrl = None
self.absUrl = None
self.filePath = None
self.startLineNumber = None
self.endLineNumber = None
self.curNs = curNs[:]
self.attributeSequence = []
if initAttrSeq:
self.attributeSequence = self.getAttributeDict().keys()
def unlink (self):
"""Break circular references of this element and its children."""
for childWrapper in self.getChildren():
childWrapper.unlink()
if not self.isUsedByExternalCache():
self.element.xmlIfExtUnlink()
def cloneNode (self, deep, cloneCallback=None):
"""Create a copy of the current element wrapper.
The reference to the parent node is set to None!"""
elementCopy = self.element.xmlIfExtCloneNode()
elementWrapperCopy = self.__class__(elementCopy, self.treeWrapper, initAttrSeq=0)
elementWrapperCopy.treeWrapper = None
elementWrapperCopy.baseUrl = self.baseUrl
elementWrapperCopy.absUrl = self.absUrl
elementWrapperCopy.filePath = self.filePath
elementWrapperCopy.startLineNumber = self.startLineNumber
elementWrapperCopy.endLineNumber = self.endLineNumber
elementWrapperCopy.curNs = self.curNs[:]
elementWrapperCopy.attributeSequence = self.attributeSequence[:]
if cloneCallback: cloneCallback(elementWrapperCopy)
if deep:
for childElement in self.element.xmlIfExtGetChildren():
childWrapperElementCopy = childElement.xmlIfExtElementWrapper.cloneNode(deep, cloneCallback)
childWrapperElementCopy.element.xmlIfExtSetParentNode(elementWrapperCopy.element)
elementWrapperCopy.element.xmlIfExtAppendChild(childWrapperElementCopy.element)
return elementWrapperCopy
def clearNodeCache (self):
"""Clear all caches used by this element wrapper which contains element wrapper references."""
self.__clearChildrenCache()
def isUsedByExternalCache (self):
"""Check if this node is used by an external cache.
unlink commands are ignored if used by an external cache"""
return self.nodeUsedByExternalCache
def setExternalCacheUsage (self, used, deep=1):
"""Set external cache usage for this node and its children
unlink commands are ignored if used by an external cache
Input parameter:
used: 0 or 1 (used by external cache)
deep: 0 or 1: controls if the child elements are also marked as used by external cache
"""
self.nodeUsedByExternalCache = used
if deep:
for childWrapper in self.getChildren():
childWrapper.setExternalCacheUsage (used, deep)
##########################################################
# attributes of the current node can be accessed via key operator
def __getitem__(self, tupleOrAttrName):
"""Attributes of the contained element node can be accessed via key operator.
Input parameter:
tupleOrAttrName: name of the attribute (tuple of namespace and attributeName or only attributeName)
Returns the attribute value.
"""
attrValue = self.getAttribute (tupleOrAttrName)
if attrValue != None:
return attrValue
else:
raise AttributeError, "Attribute %s not found!" %(repr(tupleOrAttrName))
def __setitem__(self, tupleOrAttrName, attributeValue):
"""Attributes of the contained element node can be accessed via key operator.
Input parameter:
tupleOrAttrName: name of the attribute (tuple of namespace and attributeName or only attributeName)
attributeValue: attribute value to be set
"""
self.setAttribute (tupleOrAttrName, attributeValue)
#++++++++++++ methods concerning the tag name ++++++++++++++++++++++++
def getTagName (self):
"""Retrieve the (complete) tag name of the contained element node
Returns the (complete) tag name of the contained element node
"""
return self.element.xmlIfExtGetTagName()
def getLocalName (self):
"""Retrieve the local name (without namespace) of the contained element node
Returns the local name (without namespace) of the contained element node
"""
try:
return self.__localNameCache
except:
prefix, localName = splitQName (self.getTagName())
if self.__useCaching():
self.__localNameCache = localName
return localName
def getNamespaceURI (self):
"""Retrieve the namespace URI of the contained element node
Returns the namespace URI of the contained element node (None if no namespace is used).
"""
try:
return self.__nsUriCache
except:
prefix = self.element.xmlIfExtGetNamespaceURI()
if self.__useCaching():
self.__nsUriCache = prefix
return prefix
def getNsName (self):
"""Retrieve a tuple (namespace, localName) of the contained element node
Returns a tuple (namespace, localName) of the contained element node (namespace is None if no namespace is used).
"""
try:
return self.__nsNameCache
except:
nsName = NsNameTupleFactory( (self.getNamespaceURI(), self.getLocalName()) )
if self.__useCaching():
self.__nsNameCache = nsName
return nsName
def getQName (self):
"""Retrieve a string prefix and localName of the contained element node
Returns a string "prefix:localName" of the contained element node
"""
return self.nsName2QName(self.getNsName())
def getPrefix (self):
"""Retrieve the namespace prefix of the contained element node
Returns the namespace prefix of the contained element node (None if no namespace is used).
"""
return self.getNsPrefix(self.getNsName())
#++++++++++++ methods concerning print support ++++++++++++++++++++++++
def __str__ (self):
"""Retrieve the textual representation of the contained element node."""
return self.printNode()
def printNode (self, indent="", deep=0, prettyPrint=0, attrMaxLengthDict={}, printElementValue=1, encoding=None):
"""Retrieve the textual representation of the contained element node.
Input parameter:
indent: indentation to be used for string representation
deep: 0 or 1: controls if the child element nodes are also printed
prettyPrint: aligns the columns of the attributes of childNodes
attrMaxLengthDict: dictionary containing the length of the attribute values (used for prettyprint)
printElementValue: 0 or 1: controls if the element value is printed
Returns the string representation
"""
patternXmlTagShort = '''\
%(indent)s<%(qName)s%(attributeString)s/>%(tailText)s%(lf)s'''
patternXmlTagLong = '''\
%(indent)s<%(qName)s%(attributeString)s>%(elementValueString)s\
%(lf)s%(subTreeString)s\
%(indent)s</%(qName)s>%(tailText)s%(lf)s'''
subTreeStringList = []
tailText = ""
addIndent = ""
lf = ""
if deep:
childAttrMaxLengthDict = {}
if prettyPrint:
for childNode in self.getChildren():
childNode.__updateAttrMaxLengthDict(childAttrMaxLengthDict)
lf = "\n"
addIndent = " "
for childNode in self.getChildren():
subTreeStringList.append (childNode.printNode(indent + addIndent, deep, prettyPrint, childAttrMaxLengthDict, printElementValue))
tailText = escapeCdata(self.element.xmlIfExtGetElementTailText(), encoding)
attributeStringList = []
for attrName in self.getAttributeList():
attrValue = escapeAttribute(self.getAttribute(attrName), encoding)
if prettyPrint:
try:
align = attrMaxLengthDict[attrName]
except:
align = len(attrValue)
else:
align = len(attrValue)
qName = self.nsName2QName(attrName)
attributeStringList.append (' %s="%s"%*s' %(qName, attrValue, align - len(attrValue), ""))
attributeString = string.join (attributeStringList, "")
qName = self.getQName()
if printElementValue:
if deep:
elementValueString = escapeCdata(self.element.xmlIfExtGetElementText(), encoding)
else:
elementValueString = escapeCdata(self.getElementValue(ignoreEmtpyStringFragments=1), encoding)
else:
elementValueString = ""
if subTreeStringList == [] and elementValueString == "":
printPattern = patternXmlTagShort
else:
if subTreeStringList != []:
subTreeString = string.join (subTreeStringList, "")
else:
subTreeString = ""
printPattern = patternXmlTagLong
return printPattern % vars()
#++++++++++++ methods concerning the parent of the current node ++++++++++++++++++++++++
def getParentNode (self):
"""Retrieve the ElementWrapper object of the parent element node.
Returns the ElementWrapper object of the parent element node.
"""
parent = self.element.xmlIfExtGetParentNode()
if parent != None:
return parent.xmlIfExtElementWrapper
else:
return None
#++++++++++++ methods concerning the children of the current node ++++++++++++++++++++++++
def getChildren (self, tagFilter=None):
"""Retrieve the ElementWrapper objects of the children element nodes.
Input parameter:
tagFilter: retrieve only the children with this tag name ('*' or None returns all children)
Returns all children of this element node which match 'tagFilter' (list)
"""
if tagFilter in (None, '*', (None, '*')):
children = self.element.xmlIfExtGetChildren()
elif tagFilter[1] == '*':
# handle (namespace, '*')
children = filter(lambda child:child.xmlIfExtElementWrapper.getNamespaceURI() == tagFilter[0],
self.element.xmlIfExtGetChildren())
else:
nsNameFilter = NsNameTupleFactory(tagFilter)
try:
children = self.__childrenCache[nsNameFilter]
except:
children = self.element.xmlIfExtGetChildren(nsNameFilter)
if self.__useCaching():
self.__childrenCache[nsNameFilter] = children
return map(lambda child: child.xmlIfExtElementWrapper, children)
def getChildrenNS (self, namespaceURI, tagFilter=None):
"""Retrieve the ElementWrapper objects of the children element nodes using a namespace.
Input parameter:
namespaceURI: the namespace URI of the children or None
tagFilter: retrieve only the children with this localName ('*' or None returns all children)
Returns all children of this element node which match 'namespaceURI' and 'tagFilter' (list)
"""
return self.getChildren((namespaceURI, tagFilter))
def getChildrenWithKey (self, tagFilter=None, keyAttr=None, keyValue=None):
"""Retrieve the ElementWrapper objects of the children element nodes.
Input parameter:
tagFilter: retrieve only the children with this tag name ('*' or None returns all children)
keyAttr: name of the key attribute
keyValue: value of the key
Returns all children of this element node which match 'tagFilter' (list)
"""
children = self.getChildren(tagFilter)
return filter(lambda child:child[keyAttr]==keyValue, children)
def getFirstChild (self, tagFilter=None):
"""Retrieve the ElementWrapper objects of the first child element node.
Input parameter:
tagFilter: retrieve only the first child with this tag name ('*' or None: no filter)
Returns the first child of this element node which match 'tagFilter'
or None if no suitable child element was found
"""
if tagFilter in (None, '*', (None, '*')):
element = self.element.xmlIfExtGetFirstChild()
elif tagFilter[1] == '*':
# handle (namespace, '*')
children = filter(lambda child:child.xmlIfExtElementWrapper.getNamespaceURI() == tagFilter[0],
self.element.xmlIfExtGetChildren())
try:
element = children[0]
except:
element = None
else:
nsNameFilter = NsNameTupleFactory(tagFilter)
try:
element = self.__firstChildCache[nsNameFilter]
except:
element = self.element.xmlIfExtGetFirstChild(nsNameFilter)
if self.__useCaching():
self.__firstChildCache[nsNameFilter] = element
if element != None:
return element.xmlIfExtElementWrapper
else:
return None
def getFirstChildNS (self, namespaceURI, tagFilter=None):
"""Retrieve the ElementWrapper objects of the first child element node using a namespace.
Input parameter:
namespaceURI: the namespace URI of the children or None
tagFilter: retrieve only the first child with this localName ('*' or None: no filter)
Returns the first child of this element node which match 'namespaceURI' and 'tagFilter'
or None if no suitable child element was found
"""
return self.getFirstChild ((namespaceURI, tagFilter))
def getFirstChildWithKey (self, tagFilter=None, keyAttr=None, keyValue=None):
"""Retrieve the ElementWrapper objects of the children element nodes.
Input parameter:
tagFilter: retrieve only the children with this tag name ('*' or None returns all children)
keyAttr: name of the key attribute
keyValue: value of the key
Returns all children of this element node which match 'tagFilter' (list)
"""
children = self.getChildren(tagFilter)
childrenWithKey = filter(lambda child:child[keyAttr]==keyValue, children)
if childrenWithKey != []:
return childrenWithKey[0]
else:
return None
def getElementsByTagName (self, tagFilter=None):
"""Retrieve all descendant ElementWrapper object of current node whose tag name match 'tagFilter'.
Input parameter:
tagFilter: retrieve only the children with this tag name ('*' or None returns all descendants)
Returns all descendants of this element node which match 'tagFilter' (list)
"""
if tagFilter in (None, '*', (None, '*'), (None, None)):
descendants = self.element.xmlIfExtGetElementsByTagName()
elif tagFilter[1] == '*':
# handle (namespace, '*')
descendants = filter(lambda desc:desc.xmlIfExtElementWrapper.getNamespaceURI() == tagFilter[0],
self.element.xmlIfExtGetElementsByTagName())
else:
nsNameFilter = NsNameTupleFactory(tagFilter)
descendants = self.element.xmlIfExtGetElementsByTagName(nsNameFilter)
return map(lambda descendant: descendant.xmlIfExtElementWrapper, descendants)
def getElementsByTagNameNS (self, namespaceURI, tagFilter=None):
"""Retrieve all descendant ElementWrapper object of current node whose tag name match 'namespaceURI' and 'tagFilter'.
Input parameter:
namespaceURI: the namespace URI of the descendants or None
tagFilter: retrieve only the descendants with this localName ('*' or None returns all descendants)
Returns all descendants of this element node which match 'namespaceURI' and 'tagFilter' (list)
"""
return self.getElementsByTagName((namespaceURI, tagFilter))
def getIterator (self, tagFilter=None):
"""Creates a tree iterator. The iterator loops over this element
and all subelements, in document order, and returns all elements
whose tag name match 'tagFilter'.
Input parameter:
tagFilter: retrieve only the children with this tag name ('*' or None returns all descendants)
Returns all element nodes which match 'tagFilter' (list)
"""
if tagFilter in (None, '*', (None, '*'), (None, None)):
matchingElements = self.element.xmlIfExtGetIterator()
elif tagFilter[1] == '*':
# handle (namespace, '*')
matchingElements = filter(lambda desc:desc.xmlIfExtElementWrapper.getNamespaceURI() == tagFilter[0],
self.element.xmlIfExtGetIterator())
else:
nsNameFilter = NsNameTupleFactory(tagFilter)
matchingElements = self.element.xmlIfExtGetIterator(nsNameFilter)
return map(lambda e: e.xmlIfExtElementWrapper, matchingElements)
def appendChild (self, tupleOrLocalNameOrElement, attributeDict={}):
"""Append an element node to the children of the current node.
Input parameter:
tupleOrLocalNameOrElement: (namespace, localName) or tagName or ElementWrapper object of the new child
attributeDict: attribute dictionary containing the attributes of the new child (optional)
If not an ElementWrapper object is given, a new ElementWrapper object is created with tupleOrLocalName
Returns the ElementWrapper object of the new child.
"""
if not isinstance(tupleOrLocalNameOrElement, self.__class__):
childElementWrapper = self.__createElement (tupleOrLocalNameOrElement, attributeDict)
else:
childElementWrapper = tupleOrLocalNameOrElement
self.element.xmlIfExtAppendChild (childElementWrapper.element)
self.__clearChildrenCache(childElementWrapper.getNsName())
return childElementWrapper
def insertBefore (self, tupleOrLocalNameOrElement, refChild, attributeDict={}):
"""Insert an child element node before the given reference child of the current node.
Input parameter:
tupleOrLocalNameOrElement: (namespace, localName) or tagName or ElementWrapper object of the new child
refChild: reference child ElementWrapper object
attributeDict: attribute dictionary containing the attributes of the new child (optional)
If not an ElementWrapper object is given, a new ElementWrapper object is created with tupleOrLocalName
Returns the ElementWrapper object of the new child.
"""
if not isinstance(tupleOrLocalNameOrElement, self.__class__):
childElementWrapper = self.__createElement (tupleOrLocalNameOrElement, attributeDict)
else:
childElementWrapper = tupleOrLocalNameOrElement
if refChild == None:
self.appendChild (childElementWrapper)
else:
self.element.xmlIfExtInsertBefore(childElementWrapper.element, refChild.element)
self.__clearChildrenCache(childElementWrapper.getNsName())
return childElementWrapper
def removeChild (self, childElementWrapper):
"""Remove the given child element node from the children of the current node.
Input parameter:
childElementWrapper: ElementWrapper object to be removed
"""
self.element.xmlIfExtRemoveChild(childElementWrapper.element)
self.__clearChildrenCache(childElementWrapper.getNsName())
def insertSubtree (self, refChildWrapper, subTreeWrapper, insertSubTreeRootNode=1):
"""Insert the given subtree before 'refChildWrapper' ('refChildWrapper' is not removed!)
Input parameter:
refChildWrapper: reference child ElementWrapper object
subTreeWrapper: subtree wrapper object which contains the subtree to be inserted
insertSubTreeRootNode: if 1, root node of subtree is inserted into parent tree, otherwise not
"""
if refChildWrapper != None:
self.element.xmlIfExtInsertSubtree (refChildWrapper.element, subTreeWrapper.getTree(), insertSubTreeRootNode)
else:
self.element.xmlIfExtInsertSubtree (None, subTreeWrapper.getTree(), insertSubTreeRootNode)
self.__clearChildrenCache()
def replaceChildBySubtree (self, childElementWrapper, subTreeWrapper, insertSubTreeRootNode=1):
"""Replace child element node by XML subtree (e.g. expanding included XML files)
Input parameter:
childElementWrapper: ElementWrapper object to be replaced
subTreeWrapper: XML subtree wrapper object to be inserted
insertSubTreeRootNode: if 1, root node of subtree is inserted into parent tree, otherwise not
"""
self.insertSubtree (childElementWrapper, subTreeWrapper, insertSubTreeRootNode)
self.removeChild(childElementWrapper)
#++++++++++++ methods concerning the attributes of the current node ++++++++++++++++++++++++
def getAttributeDict (self):
"""Retrieve a dictionary containing all attributes of the current element node.
Returns a dictionary (copy) containing all attributes of the current element node.
"""
return self.element.xmlIfExtGetAttributeDict()
def getAttributeList (self):
"""Retrieve a list containing all attributes of the current element node
in the sequence specified in the input XML file.
Returns a list (copy) containing all attributes of the current element node
in the sequence specified in the input XML file (TODO: does currently not work for 4DOM/pyXML interface).
"""
attrList = map(lambda a: NsNameTupleFactory(a), self.attributeSequence)
return attrList
def getAttribute (self, tupleOrAttrName):
"""Retrieve an attribute value of the current element node.
Input parameter:
tupleOrAttrName: tuple '(namespace, attributeName)' or 'attributeName' if no namespace is used
Returns the value of the specified attribute.
"""
nsName = NsNameTupleFactory(tupleOrAttrName)
return self.element.xmlIfExtGetAttribute(nsName)
def getAttributeOrDefault (self, tupleOrAttrName, defaultValue):
"""Retrieve an attribute value of the current element node or the given default value if the attribute doesn't exist.
Input parameter:
tupleOrAttrName: tuple '(namespace, attributeName)' or 'attributeName' if no namespace is used
Returns the value of the specified attribute or the given default value if the attribute doesn't exist.
"""
attributeValue = self.getAttribute (tupleOrAttrName)
if attributeValue == None:
attributeValue = defaultValue
return attributeValue
def getQNameAttribute (self, tupleOrAttrName):
"""Retrieve a QName attribute value of the current element node.
Input parameter:
tupleOrAttrName: tuple '(namespace, attributeName)' or 'attributeName' if no namespace is used
Returns the value of the specified QName attribute as tuple (namespace, localName),
i.e. the prefix is converted into the corresponding namespace value.
"""
nsNameAttrName = NsNameTupleFactory(tupleOrAttrName)
try:
return self.__qNameAttrCache[nsNameAttrName]
except:
qNameValue = self.getAttribute (nsNameAttrName)
nsNameValue = self.qName2NsName(qNameValue, useDefaultNs=1)
if self.__useCaching():
self.__qNameAttrCache[nsNameAttrName] = nsNameValue
return nsNameValue
def hasAttribute (self, tupleOrAttrName):
"""Checks if the requested attribute exist for the current element node.
Returns 1 if the attribute exists, otherwise 0.
"""
nsName = NsNameTupleFactory(tupleOrAttrName)
attrValue = self.element.xmlIfExtGetAttribute(nsName)
if attrValue != None:
return 1
else:
return 0
def setAttribute (self, tupleOrAttrName, attributeValue):
"""Sets an attribute value of the current element node.
If the attribute does not yet exist, it will be created.
Input parameter:
tupleOrAttrName: tuple '(namespace, attributeName)' or 'attributeName' if no namespace is used
attributeValue: attribute value to be set
"""
if not isinstance(attributeValue, StringTypes):
raise TypeError, "%s (attribute %s) must be a string!" %(repr(attributeValue), repr(tupleOrAttrName))
nsNameAttrName = NsNameTupleFactory(tupleOrAttrName)
if nsNameAttrName not in self.attributeSequence:
self.attributeSequence.append(nsNameAttrName)
if self.__useCaching():
if self.__qNameAttrCache.has_key(nsNameAttrName):
del self.__qNameAttrCache[nsNameAttrName]
self.element.xmlIfExtSetAttribute(nsNameAttrName, attributeValue, self.getCurrentNamespaces())
def setAttributeDefault (self, tupleOrAttrName, defaultValue):
"""Create attribute and set value to default if it does not yet exist for the current element node.
If the attribute is already existing nothing is done.
Input parameter:
tupleOrAttrName: tuple '(namespace, attributeName)' or 'attributeName' if no namespace is used
defaultValue: default attribute value to be set
"""
if not self.hasAttribute(tupleOrAttrName):
self.setAttribute(tupleOrAttrName, defaultValue)
def removeAttribute (self, tupleOrAttrName):
"""Removes an attribute from the current element node.
No exception is raised if there is no matching attribute.
Input parameter:
tupleOrAttrName: tuple '(namespace, attributeName)' or 'attributeName' if no namespace is used
"""
nsNameAttrName = NsNameTupleFactory(tupleOrAttrName)
if self.__useCaching():
if self.__qNameAttrCache.has_key(nsNameAttrName):
del self.__qNameAttrCache[nsNameAttrName]
self.element.xmlIfExtRemoveAttribute(nsNameAttrName)
def processWsAttribute (self, tupleOrAttrName, wsAction):
"""Process white space action for the specified attribute according to requested 'wsAction'.
Input parameter:
tupleOrAttrName: tuple '(namespace, attributeName)' or 'attributeName' if no namespace is used
wsAction: 'collapse': substitute multiple whitespace characters by a single ' '
'replace': substitute each whitespace characters by a single ' '
"""
attributeValue = self.getAttribute(tupleOrAttrName)
newValue = processWhitespaceAction (attributeValue, wsAction)
if newValue != attributeValue:
self.setAttribute(tupleOrAttrName, newValue)
return newValue
#++++++++++++ methods concerning the content of the current node ++++++++++++++++++++++++
def getElementValue (self, ignoreEmtpyStringFragments=0):
"""Retrieve the content of the current element node.
Returns the content of the current element node as string.
The content of multiple text nodes / CDATA nodes are concatenated to one string.
Input parameter:
ignoreEmtpyStringFragments: if 1, text nodes containing only whitespaces are ignored
"""
return "".join (self.getElementValueFragments(ignoreEmtpyStringFragments))
def getElementValueFragments (self, ignoreEmtpyStringFragments=0):
"""Retrieve the content of the current element node as value fragment list.
Returns the content of the current element node as list of string fragments.
Each list element represents one text nodes / CDATA node.
Input parameter:
ignoreEmtpyStringFragments: if 1, text nodes containing only whitespaces are ignored
Method has to be implemented by derived classes!
"""
return self.element.xmlIfExtGetElementValueFragments (ignoreEmtpyStringFragments)
def setElementValue (self, elementValue):
"""Set the content of the current element node.
Input parameter:
elementValue: string containing the new element value
If multiple text nodes / CDATA nodes are existing, 'elementValue' is set
for the first text node / CDATA node. All other text nodes /CDATA nodes are set to ''.
"""
self.element.xmlIfExtSetElementValue(elementValue)
def processWsElementValue (self, wsAction):
"""Process white space action for the content of the current element node according to requested 'wsAction'.
Input parameter:
wsAction: 'collapse': substitute multiple whitespace characters by a single ' '
'replace': substitute each whitespace characters by a single ' '
"""
self.element.xmlIfExtProcessWsElementValue(wsAction)
return self.getElementValue()
#++++++++++++ methods concerning the info about the current node in the XML file ++++++++++++++++++++
def getStartLineNumber (self):
"""Retrieve the start line number of the current element node.
Returns the start line number of the current element node in the XML file
"""
return self.startLineNumber
def getEndLineNumber (self):
"""Retrieve the end line number of the current element node.
Returns the end line number of the current element node in the XML file
"""
return self.endLineNumber
def getAbsUrl (self):
"""Retrieve the absolute URL of the XML file the current element node belongs to.
Returns the absolute URL of the XML file the current element node belongs to.
"""
return self.absUrl
def getBaseUrl (self):
"""Retrieve the base URL of the XML file the current element node belongs to.
Returns the base URL of the XML file the current element node belongs to.
"""
return self.baseUrl
def getFilePath (self):
"""Retrieve the file path of the XML file the current element node belongs to.
Returns the file path of the XML file the current element node belongs to.
"""
return self.filePath
def getLocation (self, end=0, fullpath=0):
"""Retrieve a string containing file name and line number of the current element node.
Input parameter:
end: 1 if end line number shall be shown, 0 for start line number
fullpath: 1 if the full path of the XML file shall be shown, 0 for only the file name
Returns a string containing file name and line number of the current element node.
(e.g. to be used for traces or error messages)
"""
lineMethod = (self.getStartLineNumber, self.getEndLineNumber)
pathFunc = (os.path.basename, os.path.abspath)
return "%s, %d" % (pathFunc[fullpath](self.getFilePath()), lineMethod[end]())
#++++++++++++ miscellaneous methods concerning namespaces ++++++++++++++++++++
def getCurrentNamespaces (self):
"""Retrieve the namespace prefixes visible for the current element node
Returns a list of the namespace prefixes visible for the current node.
"""
return self.curNs
def qName2NsName (self, qName, useDefaultNs):
"""Convert a qName 'prefix:localName' to a tuple '(namespace, localName)'.
Input parameter:
qName: qName to be converted
useDefaultNs: 1 if default namespace shall be used
Returns the corresponding tuple '(namespace, localName)' for 'qName'.
"""
if qName != None:
qNamePrefix, qNameLocalName = splitQName (qName)
for prefix, namespaceURI in self.getCurrentNamespaces():
if qNamePrefix == prefix:
if prefix != EMPTY_PREFIX or useDefaultNs:
nsName = (namespaceURI, qNameLocalName)
break
else:
if qNamePrefix == None:
nsName = (EMPTY_NAMESPACE, qNameLocalName)
else:
raise ValueError, "Namespace prefix '%s' not bound to a namespace!" % (qNamePrefix)
else:
nsName = (None, None)
return NsNameTupleFactory(nsName)
def nsName2QName (self, nsLocalName):
"""Convert a tuple '(namespace, localName)' to a string 'prefix:localName'
Input parameter:
nsLocalName: tuple '(namespace, localName)' to be converted
Returns the corresponding string 'prefix:localName' for 'nsLocalName'.
"""
qName = nsNameToQName (nsLocalName, self.getCurrentNamespaces())
if qName == "xmlns:None": qName = "xmlns"
return qName
def getNamespace (self, qName):
"""Retrieve namespace for a qName 'prefix:localName'.
Input parameter:
qName: qName 'prefix:localName'
Returns the corresponding namespace for the prefix of 'qName'.
"""
if qName != None:
qNamePrefix, qNameLocalName = splitQName (qName)
for prefix, namespaceURI in self.getCurrentNamespaces():
if qNamePrefix == prefix:
namespace = namespaceURI
break
else:
if qNamePrefix == None:
namespace = EMPTY_NAMESPACE
else:
raise LookupError, "Namespace for QName '%s' not found!" % (qName)
else:
namespace = EMPTY_NAMESPACE
return namespace
def getNsPrefix (self, nsLocalName):
"""Retrieve prefix for a tuple '(namespace, localName)'.
Input parameter:
nsLocalName: tuple '(namespace, localName)'
Returns the corresponding prefix for the namespace of 'nsLocalName'.
"""
ns = nsLocalName[0]
for prefix, namespace in self.getCurrentNamespaces():
if ns == namespace:
return prefix
else:
if ns == None:
return None
else:
raise LookupError, "Prefix for namespaceURI '%s' not found!" % (ns)
#++++++++++++ limited XPath support ++++++++++++++++++++
def getXPath (self, xPath, namespaceRef=None, useDefaultNs=1, attrIgnoreList=[]):
"""Retrieve node list or attribute list for specified XPath
Input parameter:
xPath: string containing xPath specification
namespaceRef: scope for namespaces (default is own element node)
useDefaultNs: 1, if default namespace shall be used if no prefix is available
attrIgnoreList: list of attributes to be ignored if wildcard is specified for attributes
Returns all nodes which match xPath specification or
list of attribute values if xPath specifies an attribute
"""
return self.getXPathList(xPath, namespaceRef, useDefaultNs, attrIgnoreList)[0]
def getXPathList (self, xPath, namespaceRef=None, useDefaultNs=1, attrIgnoreList=[]):
"""Retrieve node list or attribute list for specified XPath
Input parameter:
xPath: string containing xPath specification
namespaceRef: scope for namespaces (default is own element node)
useDefaultNs: 1, if default namespace shall be used if no prefix is available
attrIgnoreList: list of attributes to be ignored if wildcard is specified for attributes
Returns tuple (completeChildList, attrNodeList, attrNsNameFirst).
completeChildList: contains all child node which match xPath specification or
list of attribute values if xPath specifies an attribute
attrNodeList: contains all child nodes where the specified attribute was found
attrNsNameFirst: contains the name of the first attribute which was found
TODO: Re-design namespace and attribute handling of this method
"""
reChild = re.compile('child *::')
reAttribute = re.compile('attribute *::')
if namespaceRef == None: namespaceRef = self
xPath = reChild.sub('./', xPath)
xPath = reAttribute.sub('@', xPath)
xPathList = string.split (xPath, "|")
completeChildDict = {}
completeChildList = []
attrNodeList = []
attrNsNameFirst = None
for xRelPath in xPathList:
xRelPath = string.strip(xRelPath)
descendantOrSelf = 0
if xRelPath[:3] == ".//":
descendantOrSelf = 1
xRelPath = xRelPath[3:]
xPathLocalStepList = string.split (xRelPath, "/")
childList = [self, ]
for localStep in xPathLocalStepList:
localStep = string.strip(localStep)
stepChildList = []
if localStep == "":
raise IOError ("Invalid xPath '%s'!" %(xRelPath))
elif localStep == ".":
continue
elif localStep[0] == '@':
if len(localStep) == 1:
raise ValueError ("Attribute name is missing in xPath!")
if descendantOrSelf:
childList = self.getElementsByTagName()
attrName = localStep[1:]
for childNode in childList:
if attrName == '*':
attrNodeList.append (childNode)
attrDict = childNode.getAttributeDict()
for attrIgnore in attrIgnoreList:
if attrDict.has_key(attrIgnore):
del attrDict[attrIgnore]
stepChildList.extend(attrDict.values())
try:
attrNsNameFirst = attrDict.keys()[0]
except:
pass
else:
attrNsName = namespaceRef.qName2NsName (attrName, useDefaultNs=0)
if attrNsName[1] == '*':
for attr in childNode.getAttributeDict().keys():
if attr[0] == attrNsName[0]:
if attrNodeList == []:
attrNsNameFirst = attrNsName
attrNodeList.append (childNode)
stepChildList.append (childNode.getAttribute(attr))
elif childNode.hasAttribute(attrNsName):
if attrNodeList == []:
attrNsNameFirst = attrNsName
attrNodeList.append (childNode)
stepChildList.append (childNode.getAttribute(attrNsName))
childList = stepChildList
else:
nsLocalName = namespaceRef.qName2NsName (localStep, useDefaultNs=useDefaultNs)
if descendantOrSelf:
descendantOrSelf = 0
if localStep == "*":
stepChildList = self.getElementsByTagName()
else:
stepChildList = self.getElementsByTagName(nsLocalName)
else:
for childNode in childList:
if localStep == "*":
stepChildList.extend (childNode.getChildren())
else:
stepChildList.extend (childNode.getChildrenNS(nsLocalName[0], nsLocalName[1]))
childList = stepChildList
# filter duplicated childs
for child in childList:
try:
childKey = child.element
except:
childKey = child
if not completeChildDict.has_key(childKey):
completeChildList.append(child)
completeChildDict[childKey] = 1
return completeChildList, attrNodeList, attrNsNameFirst
###############################################################
# PRIVATE methods
###############################################################
def __createElement (self, tupleOrLocalName, attributeDict):
"""Create a new ElementWrapper object.
Input parameter:
tupleOrLocalName: tuple '(namespace, localName)' or 'localName' if no namespace is used
attributeDict: dictionary which contains the attributes and their values of the element node to be created
Returns the created ElementWrapper object
"""
childElementWrapper = self.treeWrapper.createElement (tupleOrLocalName, attributeDict, self.curNs[:]) # TODO: when to be adapted???)
childElementWrapper.element.xmlIfExtSetParentNode(self.element)
return childElementWrapper
def __updateAttrMaxLengthDict (self, attrMaxLengthDict):
"""Update dictionary which contains the maximum length of node attributes.
Used for pretty print to align the attributes of child nodes.
attrMaxLengthDict is in/out parameter.
"""
for attrName, attrValue in self.getAttributeDict().items():
attrLength = len(attrValue)
if not attrMaxLengthDict.has_key(attrName):
attrMaxLengthDict[attrName] = attrLength
else:
attrMaxLengthDict[attrName] = max(attrMaxLengthDict[attrName], attrLength)
def __clearChildrenCache (self, childNsName=None):
"""Clear children cache.
"""
if self.__useCaching():
if childNsName != None:
if self.__childrenCache.has_key(childNsName):
del self.__childrenCache[childNsName]
if self.__firstChildCache.has_key(childNsName):
del self.__firstChildCache[childNsName]
else:
self.__childrenCache.clear()
self.__firstChildCache.clear()
def __useCaching(self):
return self.treeWrapper.useCaching()