##############################################################################
#
# Copyright (c) 2001 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""DOM implementation in StructuredText: read-only methods
"""
# Node type codes
# ---------------
ELEMENT_NODE = 1
ATTRIBUTE_NODE = 2
TEXT_NODE = 3
CDATA_SECTION_NODE = 4
ENTITY_REFERENCE_NODE = 5
ENTITY_NODE = 6
PROCESSING_INSTRUCTION_NODE = 7
COMMENT_NODE = 8
DOCUMENT_NODE = 9
DOCUMENT_TYPE_NODE = 10
DOCUMENT_FRAGMENT_NODE = 11
NOTATION_NODE = 12
# Exception codes
# ---------------
INDEX_SIZE_ERR = 1
DOMSTRING_SIZE_ERR = 2
HIERARCHY_REQUEST_ERR = 3
WRONG_DOCUMENT_ERR = 4
INVALID_CHARACTER_ERR = 5
NO_DATA_ALLOWED_ERR = 6
NO_MODIFICATION_ALLOWED_ERR = 7
NOT_FOUND_ERR = 8
NOT_SUPPORTED_ERR = 9
INUSE_ATTRIBUTE_ERR = 10
# Exceptions
# ----------
[docs]
class DOMException(Exception):
pass
[docs]
class IndexSizeException(DOMException):
code = INDEX_SIZE_ERR
[docs]
class DOMStringSizeException(DOMException):
code = DOMSTRING_SIZE_ERR
[docs]
class HierarchyRequestException(DOMException):
code = HIERARCHY_REQUEST_ERR
[docs]
class WrongDocumentException(DOMException):
code = WRONG_DOCUMENT_ERR
[docs]
class InvalidCharacterException(DOMException):
code = INVALID_CHARACTER_ERR
[docs]
class NoDataAllowedException(DOMException):
code = NO_DATA_ALLOWED_ERR
[docs]
class NoModificationAllowedException(DOMException):
code = NO_MODIFICATION_ALLOWED_ERR
[docs]
class NotFoundException(DOMException):
code = NOT_FOUND_ERR
[docs]
class NotSupportedException(DOMException):
code = NOT_SUPPORTED_ERR
[docs]
class InUseAttributeException(DOMException):
code = INUSE_ATTRIBUTE_ERR
# Node classes
# ------------
[docs]
class ParentNode:
"""
A node that can have children, or, more precisely, that implements
the child access methods of the DOM.
"""
[docs]
def getChildNodes(self, type=type, sts=str):
"""
Returns a NodeList that contains all children of this node.
If there are no children, this is a empty NodeList
"""
r = []
for n in self.getChildren():
if isinstance(n, sts):
n = TextNode(n)
r.append(n.__of__(self))
return NodeList(r)
[docs]
def getFirstChild(self, type=type, sts=str):
"""
The first child of this node. If there is no such node
this returns None
"""
raise NotImplementedError()
[docs]
def getLastChild(self, type=type, sts=str):
"""
The last child of this node. If there is no such node
this returns None.
"""
raise NotImplementedError()
[docs]
class NodeWrapper(ParentNode):
"""
This is an acquisition-like wrapper that provides parent access for
DOM sans circular references!
"""
def __init__(self, aq_self, aq_parent):
self.aq_self = aq_self
self.aq_parent = aq_parent
def __getattr__(self, name):
return getattr(self.aq_self, name)
[docs]
def getParentNode(self):
"""
The parent of this node. All nodes except Document
DocumentFragment and Attr may have a parent
"""
raise NotImplementedError()
def _getDOMIndex(self, children, getattr=getattr):
i = 0
self = self.aq_self
for child in children:
if getattr(child, 'aq_self', child) is self:
self._DOMIndex = i
return i
i = i + 1
[docs]
def getPreviousSibling(self):
"""
The node immediately preceding this node. If
there is no such node, this returns None.
"""
children = self.aq_parent.getChildren()
if not children: # pragma: no cover
return None
index = getattr(self, '_DOMIndex', None)
if index is None:
index = self._getDOMIndex(children)
if index is None:
return None # pragma: no cover
index = index - 1
if index < 0:
return None
try:
n = children[index]
except IndexError: # pragma: no cover
return None
else:
if isinstance(n, str):
n = TextNode(n)
n._DOMIndex = index
return n.__of__(self)
[docs]
def getNextSibling(self):
"""
The node immediately preceding this node. If
there is no such node, this returns None.
"""
children = self.aq_parent.getChildren()
if not children: # pragma: no cover
return None
index = getattr(self, '_DOMIndex', None)
if index is None: # pragma: no cover
index = self._getDOMIndex(children)
if index is None:
return None
index = index + 1
try:
n = children[index]
except IndexError: # pragma: no cover
return None
else:
if isinstance(n, str): # pragma: no cover
n = TextNode(n)
n._DOMIndex = index
return n.__of__(self)
[docs]
def getOwnerDocument(self):
"""
The Document object associated with this node, if any.
"""
raise NotImplementedError()
[docs]
class Node(ParentNode):
"""Node Interface
"""
# Get a DOM wrapper with a parent link
def __of__(self, parent):
return NodeWrapper(self, parent)
# DOM attributes
[docs]
def getNodeName(self):
"""The name of this node, depending on its type
"""
[docs]
def getNodeValue(self):
"""The value of this node, depending on its type
"""
[docs]
def getParentNode(self):
"""
The parent of this node. All nodes except Document
DocumentFragment and Attr may have a parent
"""
[docs]
def getChildren(self):
"""Get a Python sequence of children
"""
raise NotImplementedError()
[docs]
def getPreviousSibling(self):
"""
The node immediately preceding this node. If
there is no such node, this returns None.
"""
[docs]
def getNextSibling(self):
"""
The node immediately preceding this node. If
there is no such node, this returns None.
"""
[docs]
def getAttributes(self):
"""
Returns a NamedNodeMap containing the attributes
of this node (if it is an element) or None otherwise.
"""
[docs]
def getOwnerDocument(self):
"""The Document object associated with this node, if any.
"""
# DOM Methods
# -----------
[docs]
def hasChildNodes(self):
"""
Returns true if the node has any children, false
if it doesn't.
"""
raise NotImplementedError()
_NODE_TYPE = None
[docs]
def getNodeType(self):
"""A code representing the type of the node."""
return self._NODE_TYPE
[docs]
class TextNode(Node):
def __init__(self, str):
self._value = str
_NODE_TYPE = TEXT_NODE
[docs]
def getNodeName(self):
return '#text'
[docs]
def getNodeValue(self):
return self._value
[docs]
class Element(Node):
"""Element interface
"""
# Element Attributes
# ------------------
[docs]
def getTagName(self):
"""The name of the element"""
return self.__class__.__name__
getNodeName = getTagName
_NODE_TYPE = ELEMENT_NODE
[docs]
def getNodeValue(self):
r = []
for c in self.getChildren():
if not isinstance(c, str):
c = c.getNodeValue()
r.append(c)
return ''.join(r)
[docs]
def getParentNode(self):
"""
The parent of this node. All nodes except Document
DocumentFragment and Attr may have a parent
"""
# Element Methods
# ---------------
_attributes = ()
[docs]
def getAttribute(self, name):
"""Retrieves an attribute value by name."""
return getattr(self, name, None)
[docs]
def getAttributeNode(self, name):
""" Retrieves an Attr node by name or None if
there is no such attribute. """
if hasattr(self, name):
return Attr(name, getattr(self, name))
[docs]
def getAttributes(self):
d = {}
for a in self._attributes:
d[a] = getattr(self, a, '')
return NamedNodeMap(d)
[docs]
def getElementsByTagName(self, tagname):
"""
Returns a NodeList of all the Elements with a given tag
name in the order in which they would be encountered in a
preorder traversal of the Document tree. Parameter: tagname
The name of the tag to match (* = all tags). Return Value: A new
NodeList object containing all the matched Elements.
"""
raise NotImplementedError()
[docs]
class NodeList:
"""NodeList interface - Provides the abstraction of an ordered
collection of nodes.
Python extensions: can use sequence-style 'len', 'getitem', and
'for..in' constructs.
"""
def __init__(self, list=None):
self._data = list or []
def __getitem__(self, index, type=type, sts=str):
return self._data[index]
[docs]
def item(self, index):
"""Returns the index-th item in the collection
"""
raise NotImplementedError()
[docs]
def getLength(self):
"""The length of the NodeList
"""
return len(self._data)
__len__ = getLength
[docs]
class NamedNodeMap:
"""
NamedNodeMap interface - Is used to represent collections
of nodes that can be accessed by name. NamedNodeMaps are not
maintained in any particular order.
Python extensions: can use sequence-style 'len', 'getitem', and
'for..in' constructs, and mapping-style 'getitem'.
"""
def __init__(self, data=None):
self._data = data if data is not None else {}
[docs]
def item(self, index):
"""Returns the index-th item in the map.
This is arbitrary because maps have no order.
"""
raise NotImplementedError()
def __getitem__(self, key):
raise NotImplementedError()
[docs]
def getLength(self):
"""
The length of the NodeList
"""
raise NotImplementedError()
__len__ = getLength
[docs]
def getNamedItem(self, name):
"""
Retrieves a node specified by name. Parameters:
name Name of a node to retrieve. Return Value A Node (of any
type) with the specified name, or None if the specified name
did not identify any node in the map.
"""
raise NotImplementedError()
[docs]
class Attr(Node):
"""
Attr interface - The Attr interface represents an attriubte in an
Element object. Attr objects inherit the Node Interface
"""
def __init__(self, name, value, specified=1):
self.name = name
self.value = value
self.specified = specified
[docs]
def getNodeName(self):
"""
The name of this node, depending on its type
"""
raise NotImplementedError()
getName = getNodeName
[docs]
def getNodeValue(self):
"""
The value of this node, depending on its type
"""
raise NotImplementedError()
_NODE_TYPE = ATTRIBUTE_NODE
[docs]
def getSpecified(self):
"""
If this attribute was explicitly given a value in the
original document, this is true; otherwise, it is false.
"""
raise NotImplementedError()