diff -r d83b10c210e3 -r fa216534ae45 lib/page.py --- a/lib/page.py Fri Feb 06 23:55:23 2009 +0200 +++ b/lib/page.py Sat Feb 07 01:33:30 2009 +0200 @@ -25,31 +25,132 @@ pass -class PageList (object) : +class PageInfo (object) : + """ + Contains metainformation about a page + """ + + def __init__ (self, parent, name, title, children=None) : + """ + Initialize, children defaults to empty list + """ + + # store + self.parent = parent + self.name = name + self.title = title + self.children = children if children else [] + + # no url get + self._url = None + + def set_parent (self, parent) : + """ + Set a parent where non was set before + """ + + assert self.parent is None + + self.parent = parent + + def add_child (self, child) : + """ + Add a PageInfo child + """ + + self.children.append(child) + + def get_child (self, name) : + """ + Look up a child by name, returning None if not found + """ + + return dict((c.name, c) for c in self.children).get(name) + + @property + def url (self) : + """ + Build this page's URL + """ + + # cached? + if self._url : + return self._url + + # collect URL segments in reverse order + segments = [] + + # add empty segment if dir + if self.children : + segments.append('') + + # iterate over ancestry + item = self + + # add all parent names, but not root's + while item and item.parent : + segments.append(item.name) + + item = item.parent + + # reverse segments + segments.reverse() + + # join + url = '/'.join(segments) + + # cache + self._url = url + + # done + return url + +class PageTree (object) : """ The list of pages """ def __init__ (self) : """ - Loads the page list from the list file + Empty PageList, must call load_page_list to initialize, once """ - # initialize list of pages - self.pages = [] - - # load from file - self._load(PAGE_LIST_FILE) - - def _load (self, path) : + def _load (self, path=PAGE_LIST_FILE) : """ Processes the lines in the given file """ + + # collect the page list + pages = [] + + # stack of (indent, PageInfo) items + stack = [] + + # the previous item processed, None for first one + prev = None for line in open(path, 'rb') : - # ignore whitespace + indent = 0 + + # count indent + for char in line : + # tabs break things + assert char != '\t' + + # increment up to first non-space char + if char == ' ' : + indent += 1 + + elif char == ':' : + indent = 0 + break + + else : + break + + # strip whitespace line = line.strip() - + # ignore empty lines if not line : continue @@ -57,52 +158,135 @@ # parse line url, title = line.split(':') - # add - self._add_item(url.strip(), title.strip()) - - def _add_item (self, url, title) : - """ - Add item to pages list - """ - - # remove index from URL - if url.endswith('/index') or url == 'index' : - url = url[:-len('/index')] - - self.pages.append((url, title)) - - def get_title (self, page) : - """ - Gets the title for the given page, or None if not found - """ + # remove whitespace + url = url.strip() + title = title.strip() + + # create PageInfo item without parent + item = PageInfo(None, url, title) - return dict(self.pages).get(page) - - def get_siblings (self, page) : - """ - Gets the (url, title) tuple list of siblings (including the given page itself) for the given page - """ - - siblings = [] + # are we the first item? + if not prev : + assert url == '', "Page list must begin with root item" + + # root node does not have a parent + parent = None + + # set root + self.root = item - # parent url - parent = os.path.split(page.url)[0] + # tee hee hee + self.root.add_child(self.root) - # how many segments in the page name - segment_count = len(page.url.split('/')) + # initialize stack + stack.append((0, self.root)) + + else : + # peek stack + stack_indent, stack_parent = stack[-1] + + # new indent level? + if indent > stack_indent : + # set parent to previous item, and push new indent level + parent to stack + parent = prev + + # push new indent level + its parent + stack.append((indent, parent)) + + # same indent level as previous + elif indent == stack_indent : + # parent is the one of the current stack level, stack doesn't change + parent = stack_parent + + # unravel stack + elif indent < stack_indent : + while True : + # remove current stack level + stack.pop(-1) + + # peek next level + stack_indent, stack_parent = stack[-1] + + # found the level to return to? + if stack_indent == indent : + # restore prev + parent = stack_parent + + break + + elif stack_indent < indent : + assert False, "Bad un-indent" + + # add to parent? + if parent : + item.set_parent(parent) + parent.add_child(item) + + # update prev + prev = item - # go through all pages - for url, title in self.pages : - # it's a sibling if the parent is the same, and the number of segments it the same - if url.startswith(parent) and len(url.split('/')) == segment_count : - siblings.append((url, title)) + # get root + assert hasattr(self, 'root'), "No root item found!" + + def get_page (self, url) : + """ + Lookup the given page URL, and return the matching PageInfo object, or None, if not found + """ + + # start from root + node = self.root + + # traverse the object tree + for segment in url.split('/') : + if segment : + node = node.get_child(segment) + + if not node : + return None # return - return siblings + return node + + def get_siblings (self, url) : + """ + Get the list of siblings for the given url, including the given page itself + """ + + # look up the page itself + page = self.get_page(url) + + # specialcase root/unknown node + if page and page.parent : + return page.parent.children -# global singleton instance -page_list = PageList() + else : + return self.root.children + + def dump (self) : + """ + Returns a string representation of the tree + """ + + def _print_node (indent, node) : + return '\n'.join('%s%s' % (' '*indent, line) for line in [ + "%-15s : %s" % (node.name, node.title) + ] + [ + _print_node(indent + 4, child) for child in node.children + ]) + return _print_node(0, self.root) + +# global singleton PageList instance +page_tree = PageTree() + +def load_page_tree () : + """ + Load the global singleton PageInfo instance + """ + + page_tree._load() + +# XXX: should inherit from PageInfo class Page (object) : """ This object represents the information about our attempt to render some specific page @@ -153,10 +337,13 @@ """ # lookup in page_list - title = page_list.get_title(self.url) + page_info = page_tree.get_page(self.url) # fallback to titlecase - if not title : + if page_info : + title = page_info.title + + else : title = self.basename.title() return title @@ -195,6 +382,7 @@ return template.render_file(self.path, request = self.request, + page_tree = page_tree ) # list of page handlers, by type