--- On Wed, 7/7/10, Brian Theado <[email protected]> wrote:
> I've seen Ville's 'find_b' and
> 'find_h' which allows
> xpath-like/jquery-like chainable searches of Leo body
> and head
> strings. That functionality does not deal much with
> tree
> structure--it always searches child nodes. That is
> useful, but
> sometimes more flexible traversal of tree structure is
> needed. I have
> written some code that provides this flexible traversal.
There's also plugins/leocursor.py and the example referenced from that file.
From memory:
lc = LeoCursor(g.findTopLevelNode(c,'Website'))
len(lc.Events) == number of children of Events node under Website.
lc.Events[0].start_time == start_time attribute embedded in body text (or
unknownAttributes) of first Events child
lc.Contact.Email == Email node under Contact node under Website node.
lc("*/@updated") == 'updated' attribute from all children of Website.
Like me, LeoCursor ignores clones :-}
Cheers -Terry
> Leo already has many tree iterators and in fact my code is
> mostly just
> a thin wrapper around the already existing leo
> iterators. However,
> the chainability is a nice touch that makes tree traversal
> tasks more
> pleasant.
>
> The code is kind of long for an email, but I've included
> it
> nonetheless. Example uses at the bottom, but here is
> my favorite
> example:
>
> g.es ('\nParents of all the clones of the current
> position')
> map (g.es, LeoTreeql(p).clones().parent().it())
>
> The rest of the code:
>
> import itertools
>
> class LeoTreeql:
> def __init__(self,p):
> # The initial iterable is just
> the given p as a list.
> # With each step in the chain,
> this iterable will be
> # refined as a composition of
> the new iterable upon the
> # results of the old iterable
> self.i = [p]
> self.p = p
>
> def refine (self,f):
> # Here is the main engine which
> applies the new iterable to the
> # results of the previously
> stored iterable. It took quite a bit of
> # experimentation to arrive at
> this and I don't fully understand
> # it to know whether it could
> be made clearer. From my limited
> # testing, it seems to work.
> self.i =
> itertools.chain.from_iterable (itertools.imap (f, self.i))
> return
>
> def descendants (self):
> # Pass in the "subtree" method
> of the position class as the
> # refining iterable
>
> self.refine(type(self.p).subtree)
> return self
>
> def children (self):
>
> self.refine(type(self.p).children)
> return self
>
> def ancestors (self):
>
> self.refine(type(self.p).parents)
> return self
>
> def parent (self):
> # Returns just a single parent,
> but each step must be an
> # iterable, so make one.
> def parent_iter(p0):
> yield
> p0.parent()
> raise
> StopIteration
> self.refine(parent_iter)
> return self
>
> def following_siblings (self):
>
> self.refine(type(self.p).following_siblings)
> return self
>
> def self_and_siblings (self):
>
> self.refine(type(self.p).self_and_siblings)
> return self
>
> def root (self):
> # This is a strange one.
> Leo's root is not the parent of all toplevel
> # nodes, it is simply the first
> toplevel node. So walking the whole
> # tree is not
> q.root().descendants() and instead is
> #
> q.root().following_siblings().descendants()
> # Strange only because that is
> a different definition of root than
> # I've seen before
> def root_iter(p0):
> yield
> p0.findRootPosition()
> self.refine(root_iter)
> return self
>
> def clones(self):
> # My own all_positions that
> doesn't depend on 'c', since this
> class only knows about 'p'
> def all_positions (p0):
> p =
> p0.findRootPosition()
> while p:
>
> yield p
>
> p.moveToThreadNext()
> raise
> StopIteration
>
> def clones_iter(p0):
> def
> isCloneOf(p1,p2=p0):
>
> return p1.v == p2.v
>
> # Finding clones
> of a node involves searching the whole tree
> return
> itertools.ifilter (isCloneOf, all_positions(p0))
>
> self.refine(clones_iter)
> return self
>
> # This is the "escape hatch" from the
> chaining. This method will
> # typically be the last call in the chain
> def it(self):
> return self.i
>
> # Looks to be working for at least a few cases
> g.es('=======')
>
> g.es('\nChildren, nieces, and nephews')
> map (g.es,
> LeoTreeql(p).self_and_siblings().children().it())
>
> g.es('\nAncestors of descendants of brothers and sisters')
> map (g.es,
> LeoTreeql(p).self_and_siblings().descendants().ancestors().it())
>
> g.es('\nChildren of parents (should be the same as self and
> parents)')
> map (g.es, LeoTreeql(p).parent().children().it())
>
> g.es('\nChildren of root (i.e. toplevel nodes except the
> first one)')
> map (g.es, LeoTreeql(p).root().following_siblings().it())
>
> g.es ('\nParents of all the clones of the current
> position')
> map (g.es, LeoTreeql(p).clones().parent().it())
>
> g.es ('\nCousins of current position and siblings and
> self')
> map (g.es,
> LeoTreeql(p).parent().self_and_siblings().children().it())
>
> g.es ('\nmultiple levels of empty iterators should be
> handled fine
> (just an empty result):')
> map (g.es,
> LeoTreeql(p).children().children().children().it())
>
> Brian
>
> --
> You received this message because you are subscribed to the
> Google Groups "leo-editor" group.
> To post to this group, send email to [email protected].
> To unsubscribe from this group, send email to
> [email protected].
> For more options, visit this group at
> http://groups.google.com/group/leo-editor?hl=en.
>
>
--
You received this message because you are subscribed to the Google Groups
"leo-editor" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/leo-editor?hl=en.