--- 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.

Reply via email to