SF.net SVN: fclient: [391] trunk/sandbox/fcp2/fcp_lib/node.py
Status: Pre-Alpha
Brought to you by:
jurner
|
From: <jU...@us...> - 2008-04-09 21:09:44
|
Revision: 391
http://fclient.svn.sourceforge.net/fclient/?rev=391&view=rev
Author: jUrner
Date: 2008-04-09 14:09:48 -0700 (Wed, 09 Apr 2008)
Log Message:
-----------
add node tree to deal with hirarchical message params
Added Paths:
-----------
trunk/sandbox/fcp2/fcp_lib/node.py
Added: trunk/sandbox/fcp2/fcp_lib/node.py
===================================================================
--- trunk/sandbox/fcp2/fcp_lib/node.py (rev 0)
+++ trunk/sandbox/fcp2/fcp_lib/node.py 2008-04-09 21:09:48 UTC (rev 391)
@@ -0,0 +1,174 @@
+"""Nodes and trees"""
+
+
+#***********************************************************************
+#
+#***********************************************************************
+class Node(object):
+ """Simple node tree implementation
+
+ @cvar ComponentSep: (str) separator to be used for keys
+
+ @ivar children: (dict) name --> node of child nodes
+ @ivar key: (str) key of the node
+ @ivar name: (str) name of the node (take care that this is None for root nodes)
+ @ivar parent: (Node) parent node of the node (take care that this is None for root nodes)
+ @ivar value: (any) value contained by the node
+
+ >>> node = Node()
+
+ # for the root of the name and parent is always None
+ >>> node.name
+
+ # to populate the tree with nodes, assign a value to the desired key.
+ # All intermediate nodes will be created automatically
+ >>> node['foo/bar/baz'] = 1234
+ >>> node['foo/bar/baz'].value
+ 1234
+
+ >>> node['foo'].value
+
+ """
+ __slots__ = ('children', 'name', 'parent', 'value')
+
+ ComponentSep = '/'
+
+ def __init__(self):
+ """Creates a new root node"""
+
+ self.children = {}
+ self.name = None
+ self.parent = None
+ self.value = None
+
+ def _get_key(self):
+ out = []
+ node = self
+ while node.name is not None:
+ out.append(node.name)
+ node = node.parent
+ out.reverse()
+ return self.ComponentSep.join(out)
+
+ key = property(_get_key, None, None)
+
+
+ def __getitem__(self, key):
+ """Returns the node located at key"""
+ L = key.split(self.ComponentSep)
+ node = self
+ while L:
+ nodeName = L.pop(0)
+ tmp_node = node.children.get(nodeName, None)
+ if tmp_node is None:
+ raise KeyError('No such node: %s' % key)
+ node = tmp_node
+ return node
+
+
+ def __setitem__(self, key, value):
+ """Sets the value contained by a node at key key"""
+ L = key.split(self.ComponentSep)
+ node = self
+ while L:
+ nodeName = L.pop(0)
+ tmp_node = node.children.get(nodeName, None)
+ if tmp_node is None:
+ tmp_node = Node()
+ tmp_node.name = nodeName
+ tmp_node.parent = node
+ node.children[nodeName] = tmp_node #<-- complete node init before querying default value!
+ tmp_node.value = self.getDefaultValue(tmp_node)
+ else:
+ node.children[nodeName] = tmp_node
+ node = tmp_node
+ node.value = value
+
+ def get(self, key, default=None):
+ """Returns the node associated to key or default if the node does not exist"""
+ try:
+ return self[key]
+ except KeyError:
+ return default
+
+ def getDefaultValue(self, node):
+ """Returns the default value of a node when a new node is about to be created
+ @paran node: the new node to query the default value for
+ @note: the default implementation returns None. Overwrite to customize
+ """
+ return None
+
+ def select(self, path):
+ """Selects node according to a path pattern
+ @patram path: (str) path pattern
+ @return: (list) of matching nodes
+ @note: the pattern supported is pretty simplistic. It may contain wildcards
+ to select any node 'key.*.other' or a double wildcard to recursively select all
+ childnodes 'key.**'. That's it.
+ """
+ components = path.split(self.ComponentSep)
+ tmp_components = components[:]
+ stack = [[self, ], ]
+ while tmp_components:
+ tmp_stack = []
+ component = tmp_components.pop(0)
+ for node in stack[-1]:
+ if component == '*':
+ for node in node.children.values():
+ tmp_stack.append(node)
+ elif component == '**':
+ if tmp_components:
+ raise ValueError('Component "**" must be last component')
+ enum = node.walk()
+ enum.next()
+ for node in enum:
+ tmp_stack.append(node)
+ else:
+ node = node.children.get(component, None)
+ if node is not None:
+ tmp_stack.append(node)
+ if not tmp_stack:
+ break
+ stack.append(tmp_stack)
+
+ if len(stack) == len(components) +1:
+ return stack[-1]
+ return []
+
+ def walk(self, report=False):
+ """Recursively iterates over the node and all its children
+
+ @param report: if True, a tuple(levelup, node) is returned. Where levelup is set to 1
+ if a node containing child nodes is about to be traversed, -1 if traversal of such a node
+ is completed and 0 if the node does not contain child nodes. If report is False, the next
+ node in turn is returned.
+ """
+ if report:
+ def walker(node):
+ if node.children:
+ yield 1, node
+ else:
+ yield 0, node
+ for node in node.children.values():
+ for x in walker(node):
+ yield x
+ if node.children:
+ yield -1, node
+ else:
+ def walker(node):
+ yield node
+ for node in node.children.values():
+ for x in walker(node):
+ yield x
+ return walker(self)
+
+#***********************************************************************
+#
+#***********************************************************************
+if __name__ == '__main__':
+ import doctest
+ print 'doctests failed: %s/%s' % doctest.testmod()
+
+
+
+
\ No newline at end of file
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|