From 124d531b41c0c4c08f71a1d8e5f2a18460707873 Mon Sep 17 00:00:00 2001
From: Brian Warner <warner@lothar.com>
Date: Sun, 21 Jan 2007 13:31:16 -0700
Subject: [PATCH] filetree: mark leaf nodes by adding is_leaf_subtree(), stop
 traversing when we hit them, to make vdrive._get_file_uri() work

---
 src/allmydata/filetree/directory.py     | 11 +++++++++++
 src/allmydata/filetree/file.py          |  6 ++++++
 src/allmydata/filetree/interfaces.py    | 21 +++++++++++++++++++++
 src/allmydata/filetree/redirect.py      |  6 ++++++
 src/allmydata/filetree/vdrive.py        |  2 +-
 src/allmydata/test/test_filetree_new.py |  4 ++--
 6 files changed, 47 insertions(+), 3 deletions(-)

diff --git a/src/allmydata/filetree/directory.py b/src/allmydata/filetree/directory.py
index 91613e6e..93a3f291 100644
--- a/src/allmydata/filetree/directory.py
+++ b/src/allmydata/filetree/directory.py
@@ -104,6 +104,8 @@ class SubTreeNode:
                 child = node_maker.make_node_from_serialized(child_data)
             self.children[name] = child
 
+    def is_leaf_subtree(self):
+        return False
 
 
 class _DirectorySubTree(object):
@@ -190,6 +192,9 @@ class LocalFileSubTreeNode(BaseDataNode):
     def set_base_data(self, data):
         self.filename = data
 
+    def is_leaf_subtree(self):
+        return False
+
 class LocalFileSubTree(_DirectorySubTree):
     node_class = LocalFileSubTreeNode
 
@@ -238,6 +243,9 @@ class CHKDirectorySubTreeNode(BaseDataNode):
     def get_uri(self):
         return self.uri
 
+    def is_leaf_subtree(self):
+        return False
+
 
 class CHKDirectorySubTree(_DirectorySubTree):
     # maybe mutable, maybe not
@@ -306,6 +314,9 @@ class SSKDirectorySubTreeNode(object):
     def set_write_capability(self, write_cap):
         self.write_cap = write_cap
 
+    def is_leaf_subtree(self):
+        return False
+
 
 class SSKDirectorySubTree(_DirectorySubTree):
     node_class = SSKDirectorySubTreeNode
diff --git a/src/allmydata/filetree/file.py b/src/allmydata/filetree/file.py
index e08f2f9a..8dced32b 100644
--- a/src/allmydata/filetree/file.py
+++ b/src/allmydata/filetree/file.py
@@ -20,6 +20,9 @@ class CHKFileNode(BaseDataNode):
     def get_uri(self):
         return self.uri
 
+    def is_leaf_subtree(self):
+        return True
+
 class SSKFileNode(object):
     implements(INode, IFileNode)
     prefix = "SSKFile"
@@ -37,3 +40,6 @@ class SSKFileNode(object):
     def get_write_capability(self):
         return self.write_cap
 
+    def is_leaf_subtree(self):
+        return True
+
diff --git a/src/allmydata/filetree/interfaces.py b/src/allmydata/filetree/interfaces.py
index 665d1b63..e39cd5c8 100644
--- a/src/allmydata/filetree/interfaces.py
+++ b/src/allmydata/filetree/interfaces.py
@@ -23,6 +23,27 @@ class INode(Interface):
         provides that same make_node_from_serialized function to create any
         internal child nodes that might be necessary."""
 
+    def is_leaf_subtree():
+        """Return True if this node does not refer to a traversable
+        subtree. When searching for the node that describes a path, the
+        search will stop at the first leaf node found. IFileNodes should
+        return True here.
+        """
+# TODO: there is a slightly confusing mixture of IDirectoryNodes and all
+# other kinds of nodes. It is convenient to mix them because that way list()
+# can point at nodes of all sorts, but IDirectoryNodes are very different
+# than the rest (because they to not represent distinct subtrees). There
+# might be a better way to factor this.
+
+# TODO: 'node' is a problematic term here. It refers to nodes in the graph of
+# connected subtrees. It also refers to nodes in the graph of directories and
+# links within a single subtree. And the interface named INode is
+# unfortunately a homonym with "inode", the data structure we previously used
+# to represent information about an uploaded file which was too large to keep
+# locally (the list of blockids), which meant the inode itself was uploaded.
+# We no longer use inodes, but using a word that sounds just like it may
+# cause confusion.
+
 class IFileNode(Interface):
     """This is a file which can be retrieved."""
     # TODO: not sure which of these to provide.. should URIs contain "CHK" or
diff --git a/src/allmydata/filetree/redirect.py b/src/allmydata/filetree/redirect.py
index bfd54016..3e2f1086 100644
--- a/src/allmydata/filetree/redirect.py
+++ b/src/allmydata/filetree/redirect.py
@@ -19,6 +19,9 @@ class LocalFileRedirectionNode(BaseDataNode):
     def set_base_data(self, data):
         self.handle = data
 
+    def is_leaf_subtree(self):
+        return False
+
 class _BaseRedirection(object):
     implements(ISubTree)
 
@@ -231,6 +234,9 @@ class HTTPRedirectionNode(BaseDataNode):
     def set_base_data(self, data):
         self.url = data
 
+    def is_leaf_subtree(self):
+        return False
+
 class HTTPRedirection(_BaseRedirection):
     node_class = HTTPRedirectionNode
 
diff --git a/src/allmydata/filetree/vdrive.py b/src/allmydata/filetree/vdrive.py
index 8a44f343..473257ca 100644
--- a/src/allmydata/filetree/vdrive.py
+++ b/src/allmydata/filetree/vdrive.py
@@ -106,7 +106,7 @@ class VirtualDrive(object):
     def _get_closest_node_1(self, subtree, path):
         (found_path, node, remaining_path) = subtree.get_node_for_path(path)
         parent_is_mutable = subtree.is_mutable()
-        if IDirectoryNode.providedBy(node):
+        if IDirectoryNode.providedBy(node) or node.is_leaf_subtree():
             # traversal done
             return (node, remaining_path)
         # otherwise, we must open and recurse into a new subtree
diff --git a/src/allmydata/test/test_filetree_new.py b/src/allmydata/test/test_filetree_new.py
index 2f1eccb3..3d23f843 100644
--- a/src/allmydata/test/test_filetree_new.py
+++ b/src/allmydata/test/test_filetree_new.py
@@ -469,8 +469,8 @@ class Stuff(unittest.TestCase):
                                             {"c": child2, "d": child3})
         d.addCallback(_listed4)
 
-        #d.addCallback(lambda res: v._get_file_uri(["b","c"]))
-        #d.addCallback(self.failUnlessEqual, "uri2")
+        d.addCallback(lambda res: v._get_file_uri(["b","c"]))
+        d.addCallback(self.failUnlessEqual, "uri2")
 
         d.addCallback(lambda res: v.list(["bogus"]))
         def _listed_bogus(res):
-- 
2.45.2