[ZPT] CVS: Zope/lib/python/ZTUtils - Iterator.py:1.7.4.2 SimpleTree.py:1.3.4.1 Tree.py:1.6.4.1 Zope.py:1.10.4.1

Chris McDonough chrism@zope.com
Tue, 8 Oct 2002 14:41:46 -0400


Update of /cvs-repository/Zope/lib/python/ZTUtils
In directory cvs.zope.org:/tmp/cvs-serv20268/lib/python/ZTUtils

Modified Files:
      Tag: chrism-install-branch
	Iterator.py SimpleTree.py Tree.py Zope.py 
Log Message:
Merging HEAD into chrism-install-branch.


=== Zope/lib/python/ZTUtils/Iterator.py 1.7.4.1 => 1.7.4.2 ===


=== Zope/lib/python/ZTUtils/SimpleTree.py 1.3 => 1.3.4.1 ===
--- Zope/lib/python/ZTUtils/SimpleTree.py:1.3	Wed Aug 14 18:10:12 2002
+++ Zope/lib/python/ZTUtils/SimpleTree.py	Tue Oct  8 14:41:15 2002
@@ -52,6 +52,9 @@
         node.id = b2a(self.getId(object))
         return node
 
-    def markRoot(self, node):
-        node.tree_pre = self.tree_pre
-        node.baseURL = node.object.REQUEST['BASEPATH1']
+    def tree(self, root, expanded=None, subtree=0):
+        node = TreeMaker.tree(self, root, expanded, subtree)
+        if not subtree:
+            node.tree_pre = self.tree_pre
+            node.baseURL = root.REQUEST['BASEPATH1']
+        return node


=== Zope/lib/python/ZTUtils/Tree.py 1.6 => 1.6.4.1 ===
--- Zope/lib/python/ZTUtils/Tree.py:1.6	Wed Aug 14 18:10:12 2002
+++ Zope/lib/python/ZTUtils/Tree.py	Tue Oct  8 14:41:15 2002
@@ -17,6 +17,7 @@
 
 from Acquisition import Explicit
 from ComputedAttribute import ComputedAttribute
+from types import ListType, TupleType
 
 class TreeNode(Explicit):
     __allow_access_to_unprotected_subobjects__ = 1
@@ -63,8 +64,11 @@
     _assume_children = 0
     _values_filter = None
     _values_function = None
+    _state_function = None
     _expand_root = 1
 
+    _cached_children = None
+
     def setChildAccess(self, attrname=_marker, filter=_marker,
                        function=_marker):
         '''Set the criteria for fetching child nodes.
@@ -82,6 +86,60 @@
         else:
             self._values_function = function
 
+    def setIdAttr(self, id):
+        """Set the attribute or method name called to get a unique Id.
+
+        The id attribute or method is used to get a unique id for every node in
+        the tree, so that the state of the tree can be encoded as a string using
+        Tree.encodeExpansion(). The returned id should be unique and stable
+        across Zope requests.
+
+        If the attribute or method isn't found on an object, either the objects
+        persistence Id or the result of id() on the object is used instead.
+
+        """
+        self._id = id
+
+    def setExpandRoot(self, expand):
+        """Set wether or not to expand the root node by default.
+        
+        When no expanded flag or mapping is passed to .tree(), assume the root
+        node is expanded, and leave all subnodes closed.
+
+        The default is to expand the root node.
+        
+        """
+        self._expand_root = expand and True or False
+
+    def setAssumeChildren(self, assume):
+        """Set wether or not to assume nodes have children.
+        
+        When a node is not expanded, when assume children is set, don't
+        determine if it is a leaf node, but assume it can be opened. Use this
+        when determining the children for a node is expensive.
+        
+        The default is to not assume there are children.
+        
+        """
+        self._assume_children = assume and True or False
+
+    def setStateFunction(self, function):
+        """Set the expansion state function.
+
+        This function will be called to determine if a node should be open or
+        collapsed, or should be treated as a leaf node. The function is passed
+        the current object, and the intended state for that object. It should
+        return the actual state the object should be in. State is encoded as an
+        integer, meaning:
+
+            -1: Node closed. Children will not be processed.
+             0: Leaf node, cannot be opened or closed, no children are
+                processed.
+             1: Node opened. Children will be processed as part of the tree.
+        
+        """
+        self._state_function = function
+
     def tree(self, root, expanded=None, subtree=0):
         '''Create a tree from root, with specified nodes expanded.
 
@@ -96,18 +154,19 @@
             # Assume a mapping
             expanded = expanded.has_key(node.id)
             child_exp = child_exp.get(node.id)
-        if expanded or (not subtree and self._expand_root):
-            children = self.getChildren(root)
-            if children:
-                node.state = 1 # expanded
-                for child in children:
-                    node._add_child(self.tree(child, child_exp, 1))
-        elif self.hasChildren(root):
-            node.state = -1 # collapsed
+
+        expanded = expanded or (not subtree and self._expand_root)
+        # Set state to 0 (leaf), 1 (opened), or -1 (closed)
+        state = self.hasChildren(root) and (expanded or -1)
+        if self._state_function is not None:
+            state = self._state_function(node.object, state)
+        node.state = state
+        if state > 0:
+            for child in self.getChildren(root):
+                node._add_child(self.tree(child, child_exp, 1))
+
         if not subtree:
             node.depth = 0
-            if hasattr(self, 'markRoot'):
-                self.markRoot(node)
         return node
 
     def node(self, object):
@@ -128,26 +187,42 @@
     def hasChildren(self, object):
         if self._assume_children:
             return 1
-        return self.getChildren(object)
+        # Cache generated children for a subsequent call to getChildren
+        self._cached_children = (object, self.getChildren(object))
+        return not not self._cached_children[1]
 
     def getChildren(self, object):
+        # Check and clear cache first
+        if self._cached_children is not None:
+            ob, children = self._cached_children
+            self._cached_children = None
+            if ob is object:
+                return children
+    
         if self._values_function is not None:
             return self._values_function(object)
-        if self._values_filter and hasattr(object, 'aq_acquire'):
-            return object.aq_acquire(self._values, aqcallback,
-                                     self._values_filter)()
-        return getattr(object, self._values)()
+
+        children = getattr(object, self._values)
+        if not (isinstance(children, ListType) or
+                isinstance(children, TupleType)):
+            # Assume callable; result not useful anyway otherwise.
+            children = children()
+
+        return self.filterChildren(children)
+
+    def filterChildren(self, children):
+        if self._values_filter:
+            return self._values_filter(children)
+        return children
 
 def simple_type(ob,
                 is_simple={type(''):1, type(0):1, type(0.0):1,
                            type(0L):1, type(None):1 }.has_key):
     return is_simple(type(ob))
 
-def aqcallback(self, inst, parent, name, value, filter):
-    return filter(self, inst, parent, name, value)
-
 from binascii import b2a_base64, a2b_base64
 from string import translate, maketrans
+import zlib
 
 a2u_map = maketrans('+/=', '-._')
 u2a_map = maketrans('-._', '+/=')
@@ -175,7 +250,7 @@
         frags.append(a2b_base64(s[i:i + 76]))
     return ''.join(frags)
 
-def encodeExpansion(nodes):
+def encodeExpansion(nodes, compress=1):
     '''Encode the expanded node ids of a tree into a string.
 
     Accepts a list of nodes, such as that produced by root.flat().
@@ -191,17 +266,26 @@
         dd = last_depth - node.depth + 1
         last_depth = node.depth
         if dd > 0:
-            steps.append('.' * dd)
+            steps.append('_' * dd)
         steps.append(node.id)
         node.expansion_number = n
         n = n + 1
-    return ':'.join(steps)
+    result = ':'.join(steps)
+    if compress:
+        result = ':'  + b2a(zlib.compress(result, 9))
+    return result
 
 def decodeExpansion(s, nth=None):
     '''Decode an expanded node map from a string.
 
     If nth is an integer, also return the (map, key) pair for the nth entry.
     '''
+    if len(s) > 8192: # Set limit to 8K, to avoid DoS attacks.
+        raise ValueError('Encoded node map too large')
+
+    if s[0] == ':': # Compressed state
+        s = zlib.decompress(a2b(s[1:]))
+    
     map = m = {}
     mstack = []
     pop = 0
@@ -209,7 +293,7 @@
     if nth is not None:
         nth_pair = (None, None)
     for step in s.split(':'):
-        if step[:1] == '.':
+        if step[0] == '_':
             pop = len(step) - 1
             continue
         if pop < 0:


=== Zope/lib/python/ZTUtils/Zope.py 1.10 => 1.10.4.1 ===
--- Zope/lib/python/ZTUtils/Zope.py:1.10	Wed Aug 14 18:10:12 2002
+++ Zope/lib/python/ZTUtils/Zope.py	Tue Oct  8 14:41:15 2002
@@ -99,6 +99,9 @@
         return self
     def getChildren(self, object):
         return LazyFilter(self._getChildren(object), skip=self.skip)
+    def filterChildren(self, children):
+        if self._values_filter:
+            return self._values_filter(LazyFilter(children, skip=self.skip))
 
 class TreeMaker(TreeSkipMixin, TreeMaker):
     _getChildren = TreeMaker.getChildren