Class REXML::XPathParser
In: lib/xmpp4r/rexmladdons.rb
Parent: Object
XMLTokens XPathParser Text IOSource Attribute Element lib/xmpp4r/rexmladdons.rb REXML dot/m_2_0.png

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 128
128:     def initialize( )
129:       @parser = REXML::Parsers::XPathParser.new
130:       @namespaces = {}
131:       @variables = {}
132:     end

Public Instance methods

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 166
166:     def []=( variable_name, value )
167:       @variables[ variable_name ] = value
168:     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 173
173:     def first( path_stack, node )
174:       #puts "#{depth}) Entering match( #{path.inspect}, #{tree.inspect} )"
175:       return nil if path.size == 0
176: 
177:       case path[0]
178:       when :document
179:         # do nothing 
180:         return first( path[1..-1], node )
181:       when :child
182:         for c in node.children
183:           #puts "#{depth}) CHILD checking #{name(c)}"
184:           r = first( path[1..-1], c )
185:           #puts "#{depth}) RETURNING #{r.inspect}" if r
186:           return r if r
187:         end
188:       when :qname
189:         name = path[2]
190:         #puts "#{depth}) QNAME #{name(tree)} == #{name} (path => #{path.size})"
191:         if node.name == name
192:           #puts "#{depth}) RETURNING #{tree.inspect}" if path.size == 3
193:           return node if path.size == 3
194:           return first( path[3..-1], node )
195:         else
196:           return nil
197:         end
198:       when :descendant_or_self
199:         r = first( path[1..-1], node )
200:         return r if r
201:         for c in node.children
202:           r = first( path, c )
203:           return r if r
204:         end
205:       when :node
206:         return first( path[1..-1], node )
207:       when :any
208:         return first( path[1..-1], node )
209:       end
210:       return nil
211:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 153
153:     def get_first path, nodeset
154:      #puts "#"*40
155:      path_stack = @parser.parse( path )
156:      #puts "PARSE: #{path} => #{path_stack.inspect}"
157:      #puts "PARSE: nodeset = #{nodeset.inspect}"
158:      first( path_stack, nodeset )
159:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 214
214:     def match( path_stack, nodeset ) 
215:       #puts "MATCH: path_stack = #{path_stack.inspect}"
216:       #puts "MATCH: nodeset = #{nodeset.inspect}"
217:       r = expr( path_stack, nodeset )
218:       #puts "MAIN EXPR => #{r.inspect}"
219:       r
220:       
221:       #while ( path_stack.size > 0 and nodeset.size > 0 ) 
222:       #  #puts "MATCH: #{path_stack.inspect} '#{nodeset.collect{|n|n.class}.inspect}'"
223:       #  nodeset = expr( path_stack, nodeset )
224:       #  #puts "NODESET: #{nodeset.inspect}"
225:       #  #puts "PATH_STACK: #{path_stack.inspect}"
226:       #end
227:       #nodeset
228:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 134
134:     def namespaces=( namespaces )
135:       namespaces ||= {}
136:       Functions::namespace_context = namespaces
137:       @namespaces = namespaces
138:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 145
145:     def parse path, nodeset
146:      #puts "#"*40
147:      path_stack = @parser.parse( path )
148:      #puts "PARSE: #{path} => #{path_stack.inspect}"
149:      #puts "PARSE: nodeset = #{nodeset.inspect}"
150:      match( path_stack, nodeset )
151:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 161
161:     def predicate path, nodeset
162:       path_stack = @parser.parse( path )
163:       expr( path_stack, nodeset )
164:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 140
140:     def variables=( vars={} )
141:       Functions::variables = vars
142:       @variables = vars
143:     end

Private Instance methods

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 811
811:     def compare a, op, b
812:       #puts "COMPARE #{a.inspect}(#{a.class.name}) #{op} #{b.inspect}(#{b.class.name})"
813:       case op
814:       when :eq
815:         a == b
816:       when :neq
817:         a != b
818:       when :lt
819:         a < b
820:       when :lteq
821:         a <= b
822:       when :gt
823:         a > b
824:       when :gteq
825:         a >= b
826:       when :and
827:         a and b
828:       when :or
829:         a or b
830:       else
831:         false
832:       end
833:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 576
576:     def d_o_s( p, ns, r )
577:       #puts "IN DOS with #{ns.inspect}; ALREADY HAVE #{r.inspect}"
578:       nt = nil
579:       ns.each_index do |i|
580:         n = ns[i]
581:         #puts "P => #{p.inspect}"
582:         x = expr( p.dclone, [ n ] )
583:         nt = n.node_type
584:         d_o_s( p, n.children, x ) if nt == :element or nt == :document and n.children.size > 0
585:         r.concat(x) if x.size > 0
586:       end
587:     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 568
568:     def descendant_or_self( path_stack, nodeset )
569:       rs = []
570:       d_o_s( path_stack, nodeset, rs )
571:       #puts "RS = #{rs.collect{|n|n.to_s}.inspect}"
572:       document_order(rs.flatten.compact)
573:       #rs.flatten.compact
574:     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 598
598:     def document_order( array_of_nodes )
599:       new_arry = []
600:       array_of_nodes.each { |node|
601:         node_idx = [] 
602:         np = node.node_type == :attribute ? node.element : node
603:         while np.parent and np.parent.node_type == :element
604:           node_idx << np.parent.index( np )
605:           np = np.parent
606:         end
607:         new_arry << [ node_idx.reverse, node ]
608:       }
609:       #puts "new_arry = #{new_arry.inspect}"
610:       new_arry.sort{ |s1, s2| s1[0] <=> s2[0] }.collect{ |s| s[1] }
611:     end

[Source]

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

[Source]

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

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 669
669:     def following( node )
670:       #puts "IN PRECEDING"
671:       acc = []
672:       p = next_sibling_node( node )
673:       #puts "P = #{p.inspect}"
674:       while p
675:         acc << p
676:         p = following_node_of( p )
677:         #puts "P = #{p.inspect}"
678:       end
679:       acc
680:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 682
682:     def following_node_of( node )
683:       #puts "NODE: #{node.inspect}"
684:       #puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}"
685:       #puts "PARENT NODE: #{node.parent}"
686:       if node.kind_of? Element and node.children.size > 0
687:         return node.children[0]
688:       end
689:       return next_sibling_node(node)
690:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 692
692:     def next_sibling_node(node)
693:       psn = node.next_sibling_node 
694:       while psn.nil?
695:         if node.parent.nil? or node.parent.class == Document 
696:           return nil
697:         end
698:         node = node.parent
699:         psn = node.next_sibling_node
700:         #puts "psn = #{psn.inspect}"
701:       end
702:       return psn
703:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 705
705:     def norm b
706:       case b
707:       when true, false
708:         return b
709:       when 'true', 'false'
710:         return Functions::boolean( b )
711:       when /^\d+(\.\d+)?$/
712:         return Functions::number( b )
713:       else
714:         return Functions::string( b )
715:       end
716:     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 627
627:     def preceding( node )
628:       #puts "IN PRECEDING"
629:       ancestors = []
630:       p = node.parent
631:       while p
632:         ancestors << p
633:         p = p.parent
634:       end
635: 
636:       acc = []
637:       p = preceding_node_of( node )
638:       #puts "P = #{p.inspect}"
639:       while p
640:         if ancestors.include? p
641:           ancestors.delete(p)
642:         else
643:           acc << p
644:         end
645:         p = preceding_node_of( p )
646:         #puts "P = #{p.inspect}"
647:       end
648:       acc
649:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 651
651:     def preceding_node_of( node )
652:      #puts "NODE: #{node.inspect}"
653:      #puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}"
654:      #puts "PARENT NODE: #{node.parent}"
655:       psn = node.previous_sibling_node 
656:       if psn.nil?
657:         if node.parent.nil? or node.parent.class == Document 
658:           return nil
659:         end
660:         return node.parent
661:         #psn = preceding_node_of( node.parent )
662:       end
663:       while psn and psn.kind_of? Element and psn.children.size > 0
664:         psn = psn.children[-1]
665:       end
666:       psn
667:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 614
614:     def recurse( nodeset, &block )
615:       for node in nodeset
616:         yield node
617:         recurse( node, &block ) if node.node_type == :element
618:       end
619:     end

[Validate]