commit-gnue
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnue] r8445 - trunk/gnue-common/src/utils


From: reinhard
Subject: [gnue] r8445 - trunk/gnue-common/src/utils
Date: Thu, 20 Apr 2006 16:27:31 -0500 (CDT)

Author: reinhard
Date: 2006-04-20 16:27:31 -0500 (Thu, 20 Apr 2006)
New Revision: 8445

Added:
   trunk/gnue-common/src/utils/tree.py
Log:
Added a very basic tree structure handling library.


Added: trunk/gnue-common/src/utils/tree.py
===================================================================
--- trunk/gnue-common/src/utils/tree.py 2006-04-20 13:39:50 UTC (rev 8444)
+++ trunk/gnue-common/src/utils/tree.py 2006-04-20 21:27:31 UTC (rev 8445)
@@ -0,0 +1,242 @@
+# GNU Enterprise Common Library - Tree structure classes
+#
+# Copyright 2001-2006 Free Software Foundation
+#
+# This file is part of GNU Enterprise
+#
+# GNU Enterprise is free software; you can redistribute it
+# and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation; either
+# version 2, or (at your option) any later version.
+#
+# GNU Enterprise is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# $Id: http.py 8129 2006-01-18 21:25:44Z jcater $
+
+"""
+Classes representing a node in a tree structure.
+"""
+
+from gnue.common.apps import errors
+
+__all__ = ['CircularReferenceError', 'Node']
+
+
+# =============================================================================
+# Exceptions
+# =============================================================================
+
+class CircularReferenceError(errors.SystemError):
+    """
+    Setting parent would create a circular reference.
+
+    Setting the requested parent for this node would create a circular
+    reference in the tree, like A is the parent of B, B is the parent of C, and
+    C is the parent of A.
+    """
+    def __init__(self):
+        message = u_("Setting parent would create a circular reference")
+        errors.SystemError.__init__(self, message)
+
+
+# =============================================================================
+# Node class
+# =============================================================================
+
+class Node(object):
+    """
+    A node in a n-ary tree.
+
+    Instances of this class represent nodes that make up a tree structure. Each
+    node can have one parent (a node with a parent of None is a root node) and
+    an arbitary number of children.
+
+    The parent of a node is defined on creation of the node, but can be changed
+    later. Nodes keep track of their children automatically, so changing the
+    parent of a node from A to B will automatically remove the node from A's
+    list of children and add it to B's list of children.
+
+    Children of a node are sorted in the order in which they were attached to
+    the parent.
+    """
+
+    # -------------------------------------------------------------------------
+    # Constructor
+    # -------------------------------------------------------------------------
+
+    def __init__(self, parent):
+        """
+        Initialize a new node.
+
+        @param parent: Parent node.
+        @type parent: Node
+        """
+        self.__parent = None
+        self.__children = []
+
+        self.parent = parent
+
+
+    # -------------------------------------------------------------------------
+    # Property "parent"
+    # -------------------------------------------------------------------------
+
+    def __get_parent(self):
+
+        return self.__parent
+
+    # -------------------------------------------------------------------------
+
+    def __set_parent(self, value):
+
+        checktype(value, [Node, None])
+
+        # Make sure that we don't create a circular reference
+        node = value
+        while node is not None:
+            if node is self:
+                raise CircularReferenceError
+            node = node.__parent
+
+        if self.__parent is not None:
+            self.__parent.__children.remove(self)
+        self.__parent = value
+        if self.__parent is not None:
+            self.__parent.__children.append(self)
+
+    # -------------------------------------------------------------------------
+
+    parent = property(__get_parent, __set_parent, None,
+            """
+            Parent node, or None if this is the root of the tree.
+
+            @type: Node
+            """)
+
+
+    # -------------------------------------------------------------------------
+    # Property "children"
+    # -------------------------------------------------------------------------
+
+    # This is a property because we want to make it readonly
+
+    def __get_children(self):
+
+        return self.__children
+
+    # -------------------------------------------------------------------------
+
+    children = property(__get_children, None, None,
+            """
+            List of child nodes. (readonly)
+
+            @type: [Node]
+            """)
+
+
+    # -------------------------------------------------------------------------
+    # Property "root"
+    # -------------------------------------------------------------------------
+
+    def __get_root(self):
+
+        result = self
+        while result.__parent is not None:
+            result = result.__parent
+        return result
+
+    # -------------------------------------------------------------------------
+
+    root = property(__get_root, None, None,
+            """
+            Root node of the tree this node is a member of. (readonly)
+
+            @type: Node
+            """)
+
+
+    # -------------------------------------------------------------------------
+    # Apply a function to every element in the tree
+    # -------------------------------------------------------------------------
+
+    def walk(self, function, *args, **kwargs):
+        """
+        Apply a function to all nodes of the (sub)tree rooted at this node.
+
+        The function is first applied to this node, then to the first child
+        node, then to all children of the first child node recursively, then to
+        the next child node, etc.
+
+        The function is called with the repective node as the first argument,
+        followed by the positional arguments and keyword arguments provided in
+        the call to "walk".
+
+        @param function: Function to call on every node.
+        @type function: callable
+        @param args: Positional arguments
+        @type args: tuple
+        @param kwargs: Keyword arguments
+        @type kwargs: dict
+        """
+
+        function(self, *args, **kwargs)
+        for child in self.__children:
+            child.walk(function, *args, **kwargs)
+
+
+# =============================================================================
+# Self test code
+# =============================================================================
+
+if __name__ == '__main__':
+
+    # Descendant of Node used in test code
+    class TestNode(Node):
+        def __init__(self, parent, text):
+            Node.__init__(self, parent)
+            self.__text = text
+        def __str__(self):
+            return self.__text
+
+    # Build up our tree
+    root = TestNode(None, 'People')
+    monty_python = TestNode(root, 'Monty Python')
+    john_cleese = TestNode(monty_python, 'John Cleese')
+    TestNode(monty_python, 'Michael Palin')
+    TestNode(monty_python, 'Graham Chapman')
+    TestNode(monty_python, 'Terry Jones')
+    TestNode(monty_python, 'Terry Gilliam')
+    star_trek = TestNode(root, 'Star Trek')
+    TestNode(star_trek, 'James T. Kirk')
+    TestNode(star_trek, 'Mr. Spock')
+    TestNode(star_trek, 'Leonard McCoy')
+
+    # Test "children" property
+    print "The Monty Python group:"
+    for child in monty_python.children:
+        print "   ", child
+
+    # Test "parent" and "root" property
+    print john_cleese, "is a member of", john_cleese.parent
+    print "    and all are", john_cleese.root
+
+    # Test "walk" function
+    def printout(self, prefix):
+        print prefix, self
+    print "All nodes:"
+    root.walk(printout, "   ")
+
+    # Test "CircularReferenceError" exception
+    try:
+        monty_python.parent = john_cleese
+    except CircularReferenceError, error:
+        print "Correctly got an exception:", error
+    print monty_python, "has now a parent of", monty_python.parent





reply via email to

[Prev in Thread] Current Thread [Next in Thread]