########################################################################
# $Header: /var/local/cvsroot/4Suite/Ft/Xml/XPath/ParsedNodeTest.py,v 1.11 2005/08/02 22:43:00 mbrown Exp $
"""
A parsed token that represents a node test.
Copyright 2004 Fourthought, Inc. (USA).
Detailed license and copyright information: http://4suite.org/COPYRIGHT
Project home, documentation, distributions: http://4suite.org/
"""
from xml.dom import Node
from Ft.Xml.XPath import RuntimeException
from Ft.Xml.XPath.XPathTypes import g_xpathRecognizedNodes
def ParsedNameTest(name):
if name == '*':
return PrincipalTypeTest()
index = name.find(':')
if index == -1:
return LocalNameTest(name)
elif name[index:] == ':*':
return NamespaceTest(name[:index])
return QualifiedNameTest(name[:index], name[index+1:])
def ParsedNodeTest(test, literal=None):
if literal:
if test != 'processing-instruction':
raise SyntaxError('Literal only allowed in processing-instruction')
return ProcessingInstructionNodeTest(literal)
return g_classMap[test]()
class NodeTestBase:
priority = -0.5
nodeType = None
def getQuickKey(self, namespaces):
"""
Returns a tuple that indicates the expected node type and, if
applicable, the expected name.
"""
return (self.nodeType, None)
def match(self, context, node, principalType=Node.ELEMENT_NODE):
"""
The principalType is discussed in section [2.3 Node Tests]
of the XPath 1.0 spec. Only attribute and namespace axes
differ from the default of elements.
"""
return node.nodeType == self.nodeType
def pprint(self, indent):
print indent + str(self)
def __str__(self):
return '<%s at %x: %s>' % (
self.__class__.__name__,
id(self),
repr(self),
)
class NodeTest(NodeTestBase):
def match(self, context, node, principalType=Node.ELEMENT_NODE):
return node.nodeType in g_xpathRecognizedNodes
def __repr__(self):
return 'node()'
class CommentNodeTest(NodeTestBase):
nodeType = Node.COMMENT_NODE
def __repr__(self):
return 'comment()'
class TextNodeTest(NodeTestBase):
nodeType = Node.TEXT_NODE
def __repr__(self):
return 'text()'
class ProcessingInstructionNodeTest(NodeTestBase):
nodeType = Node.PROCESSING_INSTRUCTION_NODE
def __init__(self, target=None):
if target:
self.priority = 0
if target[0] not in ['"', "'"]:
raise SyntaxError("Invalid literal: %s" % target)
self.target = target[1:-1]
else:
self.priority = -0.5
self.target = ''
def match(self, context, node, principalType=Node.ELEMENT_NODE):
if node.nodeType != self.nodeType:
return 0
if self.target:
return node.target == self.target
return 1
def __repr__(self):
if self.target:
target = repr(self.target)
else:
target = ''
return 'processing-instruction(%s)' % target
# Name tests
class PrincipalTypeTest(NodeTestBase):
nodeType = Node.ELEMENT_NODE
def match(self, context, node, principalType=Node.ELEMENT_NODE):
return node.nodeType == principalType
def __repr__(self):
return '*'
class LocalNameTest(NodeTestBase):
nodeType = Node.ELEMENT_NODE
def __init__(self, name):
self.priority = 0
self._name = name
def getQuickKey(self, namespaces):
return (self.nodeType, (None, self._name))
def match(self, context, node, principalType=Node.ELEMENT_NODE):
# NameTests do not use the default namespace, just as attributes
if node.nodeType == principalType and not node.namespaceURI:
return node.localName == self._name
return 0
def __repr__(self):
return self._name
class NamespaceTest(NodeTestBase):
nodeType = Node.ELEMENT_NODE
def __init__(self, prefix):
self.priority = -0.25
self._prefix = prefix
def getQuickKey(self, namespaces):
# By specifing a name of None, this test will fall into the 'general'
# category for the principal type
return (self.nodeType, None)
def match(self, context, node, principalType=Node.ELEMENT_NODE):
if node.nodeType != principalType:
return 0
try:
return node.namespaceURI == context.processorNss[self._prefix]
except KeyError:
raise RuntimeException(RuntimeException.UNDEFINED_PREFIX,
self._prefix)
def __repr__(self):
return self._prefix + ':*'
class QualifiedNameTest(NodeTestBase):
nodeType = Node.ELEMENT_NODE
def __init__(self, prefix, localName):
self.priority = 0
self._prefix = prefix
self._localName = localName
def getQuickKey(self, namespaces):
try:
namespace = namespaces[self._prefix]
except KeyError:
raise RuntimeException(RuntimeException.UNDEFINED_PREFIX,
self._prefix)
return (self.nodeType, (namespace, self._localName))
def match(self, context, node, principalType=Node.ELEMENT_NODE):
if node.nodeType == principalType:
if node.localName == self._localName:
try:
return node.namespaceURI == context.processorNss[self._prefix]
except KeyError:
raise RuntimeException(RuntimeException.UNDEFINED_PREFIX,
self._prefix)
return 0
def __repr__(self):
return self._prefix + ':' + self._localName
g_classMap = {
'node' : NodeTest,
'comment' : CommentNodeTest,
'text' : TextNodeTest,
'processing-instruction' : ProcessingInstructionNodeTest,
}
|