Class REXML::XPathParser
In: lib/xmpp4r/rexmladdons.rb
Parent: Object
XMLTokens XPathParser IOSource Element lib/xmpp4r/rexmladdons.rb REXML Module: REXML

The XPath parser has bugs. Here is a patch.

You don’t want to use this class. Really. Use XPath, which is a wrapper for this class. Believe me. You don’t want to poke around in here. There is strange, dark magic at work in this code. Beware. Go back! Go back while you still can!

Methods

Included Modules

XMLTokens

Constants

ALL = [ :attribute, :element, :text, :processing_instruction, :comment ] unless defined?(ALL)   Expr takes a stack of path elements and a set of nodes (either a Parent or an Array and returns an Array of matching nodes
ELEMENTS = [ :element ] unless defined?(ELEMENTS)

Public Class methods

LITERAL = /^’([^’]*)’|^"([^"]*)"/u

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 113
113:     def initialize( )
114:       @parser = REXML::Parsers::XPathParser.new
115:       @namespaces = {}
116:       @variables = {}
117:     end

Public Instance methods

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 150
150:     def []=( variable_name, value )
151:       @variables[ variable_name ] = value
152:     end

Performs a depth-first (document order) XPath search, and returns the first match. This is the fastest, lightest way to return a single result.

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 157
157:     def first( path_stack, node )
158:       #puts "#{depth}) Entering match( #{path.inspect}, #{tree.inspect} )"
159:       return nil if path.size == 0
160: 
161:       case path[0]
162:       when :document
163:         # do nothing 
164:         return first( path[1..-1], node )
165:       when :child
166:         for c in node.children
167:           #puts "#{depth}) CHILD checking #{name(c)}"
168:           r = first( path[1..-1], c )
169:           #puts "#{depth}) RETURNING #{r.inspect}" if r
170:           return r if r
171:         end
172:       when :qname
173:         name = path[2]
174:         #puts "#{depth}) QNAME #{name(tree)} == #{name} (path => #{path.size})"
175:         if node.name == name
176:           #puts "#{depth}) RETURNING #{tree.inspect}" if path.size == 3
177:           return node if path.size == 3
178:           return first( path[3..-1], node )
179:         else
180:           return nil
181:         end
182:       when :descendant_or_self
183:         r = first( path[1..-1], node )
184:         return r if r
185:         for c in node.children
186:           r = first( path, c )
187:           return r if r
188:         end
189:       when :node
190:         return first( path[1..-1], node )
191:       when :any
192:         return first( path[1..-1], node )
193:       end
194:       return nil
195:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 137
137:     def get_first path, nodeset
138:      #puts "#"*40
139:      path_stack = @parser.parse( path )
140:      #puts "PARSE: #{path} => #{path_stack.inspect}"
141:      #puts "PARSE: nodeset = #{nodeset.inspect}"
142:      first( path_stack, nodeset )
143:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 198
198:     def match( path_stack, nodeset ) 
199:       #puts "MATCH: path_stack = #{path_stack.inspect}"
200:       #puts "MATCH: nodeset = #{nodeset.inspect}"
201:       r = expr( path_stack, nodeset )
202:       #puts "MAIN EXPR => #{r.inspect}"
203:       r
204:       
205:       #while ( path_stack.size > 0 and nodeset.size > 0 ) 
206:       #  #puts "MATCH: #{path_stack.inspect} '#{nodeset.collect{|n|n.class}.inspect}'"
207:       #  nodeset = expr( path_stack, nodeset )
208:       #  #puts "NODESET: #{nodeset.inspect}"
209:       #  #puts "PATH_STACK: #{path_stack.inspect}"
210:       #end
211:       #nodeset
212:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 119
119:     def namespaces=( namespaces={} )
120:       Functions::namespace_context = namespaces
121:       @namespaces = namespaces
122:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 129
129:     def parse path, nodeset
130:      #puts "#"*40
131:      path_stack = @parser.parse( path )
132:      #puts "PARSE: #{path} => #{path_stack.inspect}"
133:      #puts "PARSE: nodeset = #{nodeset.inspect}"
134:      match( path_stack, nodeset )
135:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 145
145:     def predicate path, nodeset
146:       path_stack = @parser.parse( path )
147:       expr( path_stack, nodeset )
148:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 124
124:     def variables=( vars={} )
125:       Functions::variables = vars
126:       @variables = vars
127:     end

Private Instance methods

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 795
795:     def compare a, op, b
796:       #puts "COMPARE #{a.inspect}(#{a.class.name}) #{op} #{b.inspect}(#{b.class.name})"
797:       case op
798:       when :eq
799:         a == b
800:       when :neq
801:         a != b
802:       when :lt
803:         a < b
804:       when :lteq
805:         a <= b
806:       when :gt
807:         a > b
808:       when :gteq
809:         a >= b
810:       when :and
811:         a and b
812:       when :or
813:         a or b
814:       else
815:         false
816:       end
817:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 560
560:     def d_o_s( p, ns, r )
561:       #puts "IN DOS with #{ns.inspect}; ALREADY HAVE #{r.inspect}"
562:       nt = nil
563:       ns.each_index do |i|
564:         n = ns[i]
565:         #puts "P => #{p.inspect}"
566:         x = expr( p.dclone, [ n ] )
567:         nt = n.node_type
568:         d_o_s( p, n.children, x ) if nt == :element or nt == :document and n.children.size > 0
569:         r.concat(x) if x.size > 0
570:       end
571:     end

FIXME The next two methods are BAD MOJO! This is my achilles heel. If anybody thinks of a better way of doing this, be my guest. This really sucks, but it took me three days to get it to work at all. ########################################################

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 552
552:     def descendant_or_self( path_stack, nodeset )
553:       rs = []
554:       d_o_s( path_stack, nodeset, rs )
555:       #puts "RS = #{rs.collect{|n|n.to_s}.inspect}"
556:       document_order(rs.flatten.compact)
557:       #rs.flatten.compact
558:     end

Reorders an array of nodes so that they are in document order It tries to do this efficiently.

FIXME: I need to get rid of this, but the issue is that most of the XPath interpreter functions as a filter, which means that we lose context going in and out of function calls. If I knew what the index of the nodes was, I wouldn’t have to do this. Maybe add a document IDX for each node? Problems with mutable documents. Or, rewrite everything.

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 582
582:     def document_order( array_of_nodes )
583:       new_arry = []
584:       array_of_nodes.each { |node|
585:         node_idx = [] 
586:         np = node.node_type == :attribute ? node.element : node
587:         while np.parent and np.parent.node_type == :element
588:           node_idx << np.parent.index( np )
589:           np = np.parent
590:         end
591:         new_arry << [ node_idx.reverse, node ]
592:       }
593:       #puts "new_arry = #{new_arry.inspect}"
594:       new_arry.sort{ |s1, s2| s1[0] <=> s2[0] }.collect{ |s| s[1] }
595:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 702
702:     def equality_relational_compare( set1, op, set2 )
703:       #puts "EQ_REL_COMP(#{set1.inspect} #{op.inspect} #{set2.inspect})"
704:       if set1.kind_of? Array and set2.kind_of? Array
705:                           #puts "#{set1.size} & #{set2.size}"
706:         if set1.size == 1 and set2.size == 1
707:           set1 = set1[0]
708:           set2 = set2[0]
709:         elsif set1.size == 0 or set2.size == 0
710:           nd = set1.size==0 ? set2 : set1
711:           rv = nd.collect { |il| compare( il, op, nil ) }
712:           #puts "RV = #{rv.inspect}"
713:           return rv
714:         else
715:           res = []
716:           enum = SyncEnumerator.new( set1, set2 ).each { |i1, i2|
717:             #puts "i1 = #{i1.inspect} (#{i1.class.name})"
718:             #puts "i2 = #{i2.inspect} (#{i2.class.name})"
719:             i1 = norm( i1 )
720:             i2 = norm( i2 )
721:             res << compare( i1, op, i2 )
722:           }
723:           return res
724:         end
725:       end
726:                   #puts "EQ_REL_COMP: #{set1.inspect} (#{set1.class.name}), #{op}, #{set2.inspect} (#{set2.class.name})"
727:       #puts "COMPARING VALUES"
728:       # If one is nodeset and other is number, compare number to each item
729:       # in nodeset s.t. number op number(string(item))
730:       # If one is nodeset and other is string, compare string to each item
731:       # in nodeset s.t. string op string(item)
732:       # If one is nodeset and other is boolean, compare boolean to each item
733:       # in nodeset s.t. boolean op boolean(item)
734:       if set1.kind_of? Array or set2.kind_of? Array
735:                           #puts "ISA ARRAY"
736:         if set1.kind_of? Array
737:           a = set1
738:           b = set2
739:         else
740:           a = set2
741:           b = set1
742:         end
743: 
744:         case b
745:         when true, false
746:           return a.collect {|v| compare( Functions::boolean(v), op, b ) }
747:         when Numeric
748:           return a.collect {|v| compare( Functions::number(v), op, b )}
749:         when /^\d+(\.\d+)?$/
750:           b = Functions::number( b )
751:           #puts "B = #{b.inspect}"
752:           return a.collect {|v| compare( Functions::number(v), op, b )}
753:         else
754:                                   #puts "Functions::string( #{b}(#{b.class.name}) ) = #{Functions::string(b)}"
755:           b = Functions::string( b )
756:           return a.collect { |v| compare( Functions::string(v), op, b ) }
757:         end
758:       else
759:         # If neither is nodeset,
760:         #   If op is = or !=
761:         #     If either boolean, convert to boolean
762:         #     If either number, convert to number
763:         #     Else, convert to string
764:         #   Else
765:         #     Convert both to numbers and compare
766:         s1 = set1.to_s
767:         s2 = set2.to_s
768:         #puts "EQ_REL_COMP: #{set1}=>#{s1}, #{set2}=>#{s2}"
769:         if s1 == 'true' or s1 == 'false' or s2 == 'true' or s2 == 'false'
770:           #puts "Functions::boolean(#{set1})=>#{Functions::boolean(set1)}"
771:           #puts "Functions::boolean(#{set2})=>#{Functions::boolean(set2)}"
772:           set1 = Functions::boolean( set1 )
773:           set2 = Functions::boolean( set2 )
774:         else
775:           if op == :eq or op == :neq
776:             if s1 =~ /^\d+(\.\d+)?$/ or s2 =~ /^\d+(\.\d+)?$/
777:               set1 = Functions::number( s1 )
778:               set2 = Functions::number( s2 )
779:             else
780:               set1 = Functions::string( set1 )
781:               set2 = Functions::string( set2 )
782:             end
783:           else
784:             set1 = Functions::number( set1 )
785:             set2 = Functions::number( set2 )
786:           end
787:         end
788:         #puts "EQ_REL_COMP: #{set1} #{op} #{set2}"
789:         #puts ">>> #{compare( set1, op, set2 )}"
790:         return compare( set1, op, set2 )
791:       end
792:       return false
793:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 221
221:     def expr( path_stack, nodeset, context=nil )
222:       #puts "#"*15
223:       #puts "In expr with #{path_stack.inspect}"
224:       #puts "Returning" if path_stack.length == 0 || nodeset.length == 0
225:       node_types = ELEMENTS
226:       return nodeset if path_stack.length == 0 || nodeset.length == 0
227:       while path_stack.length > 0
228:         #puts "Path stack = #{path_stack.inspect}"
229:         #puts "Nodeset is #{nodeset.inspect}"
230:         case (op = path_stack.shift)
231:         when :document
232:           nodeset = [ nodeset[0].root_node ]
233:           #puts ":document, nodeset = #{nodeset.inspect}"
234: 
235:         when :qname
236:           #puts "IN QNAME"
237:           prefix = path_stack.shift
238:           name = path_stack.shift
239:           default_ns = @namespaces[prefix]
240:           default_ns = default_ns ? default_ns : ''
241:           nodeset.delete_if do |node|
242:             ns = default_ns
243:             # FIXME: This DOUBLES the time XPath searches take
244:             ns = node.namespace( prefix ) if node.node_type == :element and ns == ''
245:             #puts "NS = #{ns.inspect}"
246:             #puts "node.node_type == :element => #{node.node_type == :element}"
247:             if node.node_type == :element
248:               #puts "node.name == #{name} => #{node.name == name}"
249:               if node.name == name
250:                 #puts "node.namespace == #{ns.inspect} => #{node.namespace == ns}"
251:               end
252:             end
253:             !(node.node_type == :element and 
254:               node.name == name and 
255:               node.namespace == ns )
256:           end
257:           node_types = ELEMENTS
258: 
259:         when :any
260:           #puts "ANY 1: nodeset = #{nodeset.inspect}"
261:           #puts "ANY 1: node_types = #{node_types.inspect}"
262:           nodeset.delete_if { |node| !node_types.include?(node.node_type) }
263:           #puts "ANY 2: nodeset = #{nodeset.inspect}"
264: 
265:         when :self
266:           # This space left intentionally blank
267: 
268:         when :processing_instruction
269:           target = path_stack.shift
270:           nodeset.delete_if do |node|
271:             (node.node_type != :processing_instruction) or 
272:             ( target!='' and ( node.target != target ) )
273:           end
274: 
275:         when :text
276:           nodeset.delete_if { |node| node.node_type != :text }
277: 
278:         when :comment
279:           nodeset.delete_if { |node| node.node_type != :comment }
280: 
281:         when :node
282:           # This space left intentionally blank
283:           node_types = ALL
284: 
285:         when :child
286:           new_nodeset = []
287:           nt = nil
288:           for node in nodeset
289:             nt = node.node_type
290:             new_nodeset += node.children if nt == :element or nt == :document
291:           end
292:           nodeset = new_nodeset
293:           node_types = ELEMENTS
294: 
295:         when :literal
296:           literal = path_stack.shift
297:           if literal =~ /^\d+(\.\d+)?$/
298:             return ($1 ? literal.to_f : literal.to_i) 
299:           end
300:           return literal
301:         
302:         when :attribute
303:           new_nodeset = []
304:           case path_stack.shift
305:           when :qname
306:             prefix = path_stack.shift
307:             name = path_stack.shift
308:             for element in nodeset
309:               if element.node_type == :element
310:                 #puts element.name
311:                 attr = element.attribute( name, @namespaces[prefix] )
312:                 new_nodeset << attr if attr
313:               end
314:             end
315:           when :any
316:             #puts "ANY"
317:             for element in nodeset
318:               if element.node_type == :element
319:                 new_nodeset += element.attributes.to_a
320:               end
321:             end
322:           end
323:           nodeset = new_nodeset
324: 
325:         when :parent
326:           #puts "PARENT 1: nodeset = #{nodeset}"
327:           nodeset = nodeset.collect{|n| n.parent}.compact
328:           #nodeset = expr(path_stack.dclone, nodeset.collect{|n| n.parent}.compact)
329:           #puts "PARENT 2: nodeset = #{nodeset.inspect}"
330:           node_types = ELEMENTS
331: 
332:         when :ancestor
333:           new_nodeset = []
334:           for node in nodeset
335:             while node.parent
336:               node = node.parent
337:               new_nodeset << node unless new_nodeset.include? node
338:             end
339:           end
340:           nodeset = new_nodeset
341:           node_types = ELEMENTS
342: 
343:         when :ancestor_or_self
344:           new_nodeset = []
345:           for node in nodeset
346:             if node.node_type == :element
347:               new_nodeset << node
348:               while ( node.parent )
349:                 node = node.parent
350:                 new_nodeset << node unless new_nodeset.include? node
351:               end
352:             end
353:           end
354:           nodeset = new_nodeset
355:           node_types = ELEMENTS
356: 
357:         when :predicate
358:           new_nodeset = []
359:           subcontext = { :size => nodeset.size }
360:           pred = path_stack.shift
361:           nodeset.each_with_index { |node, index|
362:             subcontext[ :node ] = node
363:             #puts "PREDICATE SETTING CONTEXT INDEX TO #{index+1}"
364:             subcontext[ :index ] = index+1
365:             pc = pred.dclone
366:             #puts "#{node.hash}) Recursing with #{pred.inspect} and [#{node.inspect}]"
367:             result = expr( pc, [node], subcontext )
368:             result = result[0] if result.kind_of? Array and result.length == 1
369:             #puts "#{node.hash}) Result = #{result.inspect} (#{result.class.name})"
370:             if result.kind_of? Numeric
371:               #puts "Adding node #{node.inspect}" if result == (index+1)
372:               new_nodeset << node if result == (index+1)
373:             elsif result.instance_of? Array
374:               #puts "Adding node #{node.inspect}" if result.size > 0
375:               new_nodeset << node if result.size > 0
376:             else
377:               #puts "Adding node #{node.inspect}" if result
378:               new_nodeset << node if result
379:             end
380:           }
381:           #puts "New nodeset = #{new_nodeset.inspect}"
382:           #puts "Path_stack  = #{path_stack.inspect}"
383:           nodeset = new_nodeset
384: ??
385: 
386:         when :descendant_or_self
387:           rv = descendant_or_self( path_stack, nodeset )
388:           path_stack.clear
389:           nodeset = rv
390:           node_types = ELEMENTS
391: 
392:         when :descendant
393:           results = []
394:           nt = nil
395:           for node in nodeset
396:             nt = node.node_type
397:             results += expr( path_stack.dclone.unshift( :descendant_or_self ),
398:               node.children ) if nt == :element or nt == :document
399:           end
400:           nodeset = results
401:           node_types = ELEMENTS
402: 
403:         when :following_sibling
404:           #puts "FOLLOWING_SIBLING 1: nodeset = #{nodeset}"
405:           results = []
406:           for node in nodeset
407:             all_siblings = node.parent.children
408:             current_index = all_siblings.index( node )
409:             following_siblings = all_siblings[ current_index+1 .. -1 ]
410:             results += expr( path_stack.dclone, following_siblings )
411:           end
412:           #puts "FOLLOWING_SIBLING 2: nodeset = #{nodeset}"
413:           nodeset = results
414: 
415:         when :preceding_sibling
416:           results = []
417:           for node in nodeset
418:             all_siblings = node.parent.children
419:             current_index = all_siblings.index( node )
420:             preceding_siblings = all_siblings[ 0 .. current_index-1 ].reverse
421:             #results += expr( path_stack.dclone, preceding_siblings )
422:           end
423:           nodeset = preceding_siblings
424:           node_types = ELEMENTS
425: 
426:         when :preceding
427:           new_nodeset = []
428:           for node in nodeset
429:             new_nodeset += preceding( node )
430:           end
431:           #puts "NEW NODESET => #{new_nodeset.inspect}"
432:           nodeset = new_nodeset
433:           node_types = ELEMENTS
434: 
435:         when :following
436:           new_nodeset = []
437:           for node in nodeset
438:             new_nodeset += following( node )
439:           end
440:           nodeset = new_nodeset
441:           node_types = ELEMENTS
442: 
443:         when :namespace
444:           new_set = []
445:           for node in nodeset
446:             new_nodeset << node.namespace if node.node_type == :element or node.node_type == :attribute
447:           end
448:           nodeset = new_nodeset
449: 
450:         when :variable
451:           var_name = path_stack.shift
452:           return @variables[ var_name ]
453: 
454:         # :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq
455:         when :eq, :neq, :lt, :lteq, :gt, :gteq, :and, :or
456:           left = expr( path_stack.shift, nodeset, context )
457:           #puts "LEFT => #{left.inspect} (#{left.class.name})"
458:           right = expr( path_stack.shift, nodeset, context )
459:           #puts "RIGHT => #{right.inspect} (#{right.class.name})"
460:           res = equality_relational_compare( left, op, right )
461:           #puts "RES => #{res.inspect}"
462:           return res
463: 
464:         when :div
465:           left = Functions::number(expr(path_stack.shift, nodeset, context)).to_f
466:           right = Functions::number(expr(path_stack.shift, nodeset, context)).to_f
467:           return (left / right)
468: 
469:         when :mod
470:           left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
471:           right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
472:           return (left % right)
473: 
474:         when :mult
475:           left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
476:           right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
477:           return (left * right)
478: 
479:         when :plus
480:           left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
481:           right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
482:           return (left + right)
483: 
484:         when :minus
485:           left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
486:           right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
487:           return (left - right)
488: 
489:         when :union
490:           left = expr( path_stack.shift, nodeset, context )
491:           right = expr( path_stack.shift, nodeset, context )
492:           return (left | right)
493: 
494:         when :neg
495:           res = expr( path_stack, nodeset, context )
496:           return -(res.to_f)
497: 
498:         when :not
499:         when :function
500:           func_name = path_stack.shift.tr('-','_')
501:           arguments = path_stack.shift
502:           #puts "FUNCTION 0: #{func_name}(#{arguments.collect{|a|a.inspect}.join(', ')})" 
503:           subcontext = context ? nil : { :size => nodeset.size }
504: 
505:           res = []
506:           cont = context
507:           nodeset.each_with_index { |n, i| 
508:             if subcontext
509:               subcontext[:node]  = n
510:               subcontext[:index] = i
511:               cont = subcontext
512:             end
513:             arg_clone = arguments.dclone
514:             args = arg_clone.collect { |arg| 
515:               #puts "FUNCTION 1: Calling expr( #{arg.inspect}, [#{n.inspect}] )"
516:               expr( arg, [n], cont ) 
517:             }
518:             #puts "FUNCTION 2: #{func_name}(#{args.collect{|a|a.inspect}.join(', ')})" 
519:             Functions.context = cont
520:             res << Functions.send( func_name, *args )
521:             #puts "FUNCTION 3: #{res[-1].inspect}"
522:           }
523:           return res
524: 
525:         end
526:       end # while
527:       #puts "EXPR returning #{nodeset.inspect}"
528:       return nodeset
529:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 653
653:     def following( node )
654:       #puts "IN PRECEDING"
655:       acc = []
656:       p = next_sibling_node( node )
657:       #puts "P = #{p.inspect}"
658:       while p
659:         acc << p
660:         p = following_node_of( p )
661:         #puts "P = #{p.inspect}"
662:       end
663:       acc
664:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 666
666:     def following_node_of( node )
667:       #puts "NODE: #{node.inspect}"
668:       #puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}"
669:       #puts "PARENT NODE: #{node.parent}"
670:       if node.kind_of? Element and node.children.size > 0
671:         return node.children[0]
672:       end
673:       return next_sibling_node(node)
674:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 676
676:     def next_sibling_node(node)
677:       psn = node.next_sibling_node 
678:       while psn.nil?
679:         if node.parent.nil? or node.parent.class == Document 
680:           return nil
681:         end
682:         node = node.parent
683:         psn = node.next_sibling_node
684:         #puts "psn = #{psn.inspect}"
685:       end
686:       return psn
687:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 689
689:     def norm b
690:       case b
691:       when true, false
692:         return b
693:       when 'true', 'false'
694:         return Functions::boolean( b )
695:       when /^\d+(\.\d+)?$/
696:         return Functions::number( b )
697:       else
698:         return Functions::string( b )
699:       end
700:     end

Builds a nodeset of all of the preceding nodes of the supplied node, in reverse document order

preceding:includes every element in the document that precedes this node,

except for ancestors

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 611
611:     def preceding( node )
612:       #puts "IN PRECEDING"
613:       ancestors = []
614:       p = node.parent
615:       while p
616:         ancestors << p
617:         p = p.parent
618:       end
619: 
620:       acc = []
621:       p = preceding_node_of( node )
622:       #puts "P = #{p.inspect}"
623:       while p
624:         if ancestors.include? p
625:           ancestors.delete(p)
626:         else
627:           acc << p
628:         end
629:         p = preceding_node_of( p )
630:         #puts "P = #{p.inspect}"
631:       end
632:       acc
633:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 635
635:     def preceding_node_of( node )
636:      #puts "NODE: #{node.inspect}"
637:      #puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}"
638:      #puts "PARENT NODE: #{node.parent}"
639:       psn = node.previous_sibling_node 
640:       if psn.nil?
641:         if node.parent.nil? or node.parent.class == Document 
642:           return nil
643:         end
644:         return node.parent
645:         #psn = preceding_node_of( node.parent )
646:       end
647:       while psn and psn.kind_of? Element and psn.children.size > 0
648:         psn = psn.children[-1]
649:       end
650:       psn
651:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 598
598:     def recurse( nodeset, &block )
599:       for node in nodeset
600:         yield node
601:         recurse( node, &block ) if node.node_type == :element
602:       end
603:     end

[Validate]