Fixed query algorithm. Added '@@@' operator. Added parseExt. Added documentation.
[confparser-old] / confparser_ext.py
index a49846e..e6a0443 100644 (file)
@@ -161,62 +161,100 @@ def confToNodeset( node ) :
 #
 # FIXME: UGLY
 #
-def select( ns , path ) :
-
-       def select_( ns , path ) :
-
-               result = NodeSet( ns )
-               while path :
-                       ns = result
-                       recPath = path
-                       part , path = path[ 0 ] , path[ 1 : ]
-                       recurse = False
-                       if part == '' :
-                               recurse = True
-                               part , path = path[ 0 ] , path[ 1 : ]
-                       #
-                       # Build predicates
-                       #
-                       if ':' in part :
-                               part = part.split( ':' )
-                               def matcher( sub ) :
-                                       result = ( part[ 0 ] in [ sub.name , '*' ] and len( sub.values ) == len( part ) - 1 )
-                                       if result :
-                                               for v1 , v2 in zip( sub.values , part[ 1 : ] ) :
-                                                       if re.match( v2
-                                                               .replace( '.' , '\\.' )
-                                                               .replace( '*' , '.*' )
-                                                               .replace( '?' , '. ') , v1 , re.I ) is None :
-                                                               result = False
-                                                               break
-                                       return result
-                       else :
-                               def matcher( sub ) :
-                                       return part in [ sub.name , '*' ]
-                       #
-                       # Select subnodes according to the predicate
-                       #
-                       result = NodeSet()
-                       for item in ns :
-                               for sub in item.subs :
-                                       if matcher( sub ) :
-                                               result += sub
-                                       if recurse :
-                                               result += select_( sub , recPath )
+def select( nodeset , path ) :
+
+       def matchValue_( match , value ) :
+
+               return re.match( match
+                                                .replace( '.' , '\\.' )
+                                                .replace( '*' , '.*' )
+                                                .replace( '?' , '. ') , str( value ) , re.I ) is not None
+
+       def select_( nodeset , path ) :
+
+               '''Search *children* of nodeset matching 'path'.'''
+
+               assert path
+               assert isinstance( nodeset , NodeSet )
+
+               # Extract first path element
+               element , subPath = path[ 0 ] , path[ 1 : ]
+
+               #
+               # Remove leading empty element(s) and set recurse flag
+               # in such case.
+               #
+               recurse = False
+               while element == '' : # the '//' path element
+                       assert subPath , '// must to be followed by a path element.'
+                       element , subPath = subPath[ 0 ] , subPath[ 1 : ]
+                       recurse = True
+
+               #
+               # Build predicates
+               #
+               if ':' in element :
+                       element = element.split( ':' )
+                       def matcher( sub ) :
+                               result = ( element[ 0 ] in [ sub.name , '*' ] and len( sub.values ) == len( element ) - 1 )
+                               if result :
+                                       for v1 , v2 in zip( sub.values , element[ 1 : ] ) :
+                                               if not matchValue_( v2 , v1 ) :
+                                                       result = False
+                                                       break
+                               return result
+               else :
+                       def matcher( sub ) :
+                               return element in [ sub.name , '*' ]
+
+               #
+               # process either continue filtering process, or keep current
+               # result according if path fully processed.
+               #
+               if subPath :
+                       def process( result ) :
+                               return select_( NodeSet( result ) , subPath )
+               else :
+                       def process( result ) :
+                               return result
+
+               #
+               # Select subnodes according to the predicate
+               #
+               result = NodeSet()
+               for item in nodeset :
+                       for sub in item.subs :
+                               if matcher( sub ) :
+                                       result += process( sub )
+                               if recurse :
+                                       result += select_( NodeSet( sub ) , path )
+
                return result
 
-       path = path.split( '/' )
-       if path and path[ 0 ] == '' :
-               # assume // at beginning.. BAD BAD BAD
-               del path[ 0 ]
-       if not path :
-               result = ns
-       else :
-               if path[ -1 ] == '@@' :
+       if not path.startswith( '/' ) :
+               # Always assume path from the root
+               # (It's the only possible case anyway.)
+               path = '/' + path
+
+       nodeset = NodeSet( nodeset )
+
+       path = path.split( '/' )[ 1 : ]
+
+       def finally_( result ) :
+               return result
+
+       if path :
+               if path[ -1 ] == '@@@' :
+                       def finally_( result ) :
+                               return [ ( node.name , ) + node.values for node in result ]
+                       path.pop()
+               elif path[ -1 ] == '@@' :
+                       # FIXME: Factorize with @@@
                        def finally_( result ) :
                                return [ node.values for node in result ]
                        path.pop()
                elif path[ -1 ] == '@' :
+                       # FIXME: Factorize with @@ or @@@
                        def finally_( result ) :
                                return sum( [ node.values for node in result ] , () )
                        path.pop()
@@ -225,11 +263,11 @@ def select( ns , path ) :
                                if len( result ) :
                                        return ' '.join( [ str( s ) for node in result for s in node.values ] )
                        path.pop()
-               else :
-                       def finally_( result ) :
-                               return result
-               result = select_( ns , path )
-               result = finally_( result )
+       if not path or ( len( path ) == 1 and path[ 0 ] == '' ) :
+               result = nodeset
+       else :
+               result = select_( nodeset , path )
+       result = finally_( result )
 
        return result
 
@@ -246,10 +284,12 @@ def test() :
                print n[ 'default/unhide/?' ] # the string for the whole nodesets
                print n[ 'default/unhide/@' ] # the flatten list of text
                print n[ 'default/unhide/@@' ] # the list of list of text
+               print n[ 'default/unhide/@@@' ] # the list of list of text, include node name
                print n[ 'server/@' ]
                print n[ 'server:m*/rootdev/@' ]
                print n[ '*/@@' ]
                print n[ '//*/@@' ]
+               print n[ '//*/@@@' ]
        else :
                print 'Unable to parse %r' % filename