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 105
105:     def initialize( )
106:       @parser = REXML::Parsers::XPathParser.new
107:       @namespaces = {}
108:       @variables = {}
109:     end

Public Instance methods

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 142
142:     def []=( variable_name, value )
143:       @variables[ variable_name ] = value
144:     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 149
149:     def first( path_stack, node )
150:       #puts "#{depth}) Entering match( #{path.inspect}, #{tree.inspect} )"
151:       return nil if path.size == 0
152: 
153:       case path[0]
154:       when :document
155:         # do nothing 
156:         return first( path[1..-1], node )
157:       when :child
158:         for c in node.children
159:           #puts "#{depth}) CHILD checking #{name(c)}"
160:           r = first( path[1..-1], c )
161:           #puts "#{depth}) RETURNING #{r.inspect}" if r
162:           return r if r
163:         end
164:       when :qname
165:         name = path[2]
166:         #puts "#{depth}) QNAME #{name(tree)} == #{name} (path => #{path.size})"
167:         if node.name == name
168:           #puts "#{depth}) RETURNING #{tree.inspect}" if path.size == 3
169:           return node if path.size == 3
170:           return first( path[3..-1], node )
171:         else
172:           return nil
173:         end
174:       when :descendant_or_self
175:         r = first( path[1..-1], node )
176:         return r if r
177:         for c in node.children
178:           r = first( path, c )
179:           return r if r
180:         end
181:       when :node
182:         return first( path[1..-1], node )
183:       when :any
184:         return first( path[1..-1], node )
185:       end
186:       return nil
187:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 129
129:     def get_first 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:      first( path_stack, nodeset )
135:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 190
190:     def match( path_stack, nodeset ) 
191:       #puts "MATCH: path_stack = #{path_stack.inspect}"
192:       #puts "MATCH: nodeset = #{nodeset.inspect}"
193:       r = expr( path_stack, nodeset )
194:       #puts "MAIN EXPR => #{r.inspect}"
195:       r
196:       
197:       #while ( path_stack.size > 0 and nodeset.size > 0 ) 
198:       #  #puts "MATCH: #{path_stack.inspect} '#{nodeset.collect{|n|n.class}.inspect}'"
199:       #  nodeset = expr( path_stack, nodeset )
200:       #  #puts "NODESET: #{nodeset.inspect}"
201:       #  #puts "PATH_STACK: #{path_stack.inspect}"
202:       #end
203:       #nodeset
204:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 111
111:     def namespaces=( namespaces={} )
112:       Functions::namespace_context = namespaces
113:       @namespaces = namespaces
114:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 121
121:     def parse path, nodeset
122:      #puts "#"*40
123:      path_stack = @parser.parse( path )
124:      #puts "PARSE: #{path} => #{path_stack.inspect}"
125:      #puts "PARSE: nodeset = #{nodeset.inspect}"
126:      match( path_stack, nodeset )
127:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 137
137:     def predicate path, nodeset
138:       path_stack = @parser.parse( path )
139:       expr( path_stack, nodeset )
140:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 116
116:     def variables=( vars={} )
117:       Functions::variables = vars
118:       @variables = vars
119:     end

Private Instance methods

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 787
787:     def compare a, op, b
788:       #puts "COMPARE #{a.inspect}(#{a.class.name}) #{op} #{b.inspect}(#{b.class.name})"
789:       case op
790:       when :eq
791:         a == b
792:       when :neq
793:         a != b
794:       when :lt
795:         a < b
796:       when :lteq
797:         a <= b
798:       when :gt
799:         a > b
800:       when :gteq
801:         a >= b
802:       when :and
803:         a and b
804:       when :or
805:         a or b
806:       else
807:         false
808:       end
809:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 552
552:     def d_o_s( p, ns, r )
553:       #puts "IN DOS with #{ns.inspect}; ALREADY HAVE #{r.inspect}"
554:       nt = nil
555:       ns.each_index do |i|
556:         n = ns[i]
557:         #puts "P => #{p.inspect}"
558:         x = expr( p.dclone, [ n ] )
559:         nt = n.node_type
560:         d_o_s( p, n.children, x ) if nt == :element or nt == :document and n.children.size > 0
561:         r.concat(x) if x.size > 0
562:       end
563:     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 544
544:     def descendant_or_self( path_stack, nodeset )
545:       rs = []
546:       d_o_s( path_stack, nodeset, rs )
547:       #puts "RS = #{rs.collect{|n|n.to_s}.inspect}"
548:       document_order(rs.flatten.compact)
549:       #rs.flatten.compact
550:     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 574
574:     def document_order( array_of_nodes )
575:       new_arry = []
576:       array_of_nodes.each { |node|
577:         node_idx = [] 
578:         np = node.node_type == :attribute ? node.element : node
579:         while np.parent and np.parent.node_type == :element
580:           node_idx << np.parent.index( np )
581:           np = np.parent
582:         end
583:         new_arry << [ node_idx.reverse, node ]
584:       }
585:       #puts "new_arry = #{new_arry.inspect}"
586:       new_arry.sort{ |s1, s2| s1[0] <=> s2[0] }.collect{ |s| s[1] }
587:     end

[Source]

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

[Source]

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

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 645
645:     def following( node )
646:       #puts "IN PRECEDING"
647:       acc = []
648:       p = next_sibling_node( node )
649:       #puts "P = #{p.inspect}"
650:       while p
651:         acc << p
652:         p = following_node_of( p )
653:         #puts "P = #{p.inspect}"
654:       end
655:       acc
656:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 658
658:     def following_node_of( node )
659:       #puts "NODE: #{node.inspect}"
660:       #puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}"
661:       #puts "PARENT NODE: #{node.parent}"
662:       if node.kind_of? Element and node.children.size > 0
663:         return node.children[0]
664:       end
665:       return next_sibling_node(node)
666:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 668
668:     def next_sibling_node(node)
669:       psn = node.next_sibling_node 
670:       while psn.nil?
671:         if node.parent.nil? or node.parent.class == Document 
672:           return nil
673:         end
674:         node = node.parent
675:         psn = node.next_sibling_node
676:         #puts "psn = #{psn.inspect}"
677:       end
678:       return psn
679:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 681
681:     def norm b
682:       case b
683:       when true, false
684:         return b
685:       when 'true', 'false'
686:         return Functions::boolean( b )
687:       when /^\d+(\.\d+)?$/
688:         return Functions::number( b )
689:       else
690:         return Functions::string( b )
691:       end
692:     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 603
603:     def preceding( node )
604:       #puts "IN PRECEDING"
605:       ancestors = []
606:       p = node.parent
607:       while p
608:         ancestors << p
609:         p = p.parent
610:       end
611: 
612:       acc = []
613:       p = preceding_node_of( node )
614:       #puts "P = #{p.inspect}"
615:       while p
616:         if ancestors.include? p
617:           ancestors.delete(p)
618:         else
619:           acc << p
620:         end
621:         p = preceding_node_of( p )
622:         #puts "P = #{p.inspect}"
623:       end
624:       acc
625:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 627
627:     def preceding_node_of( node )
628:      #puts "NODE: #{node.inspect}"
629:      #puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}"
630:      #puts "PARENT NODE: #{node.parent}"
631:       psn = node.previous_sibling_node 
632:       if psn.nil?
633:         if node.parent.nil? or node.parent.class == Document 
634:           return nil
635:         end
636:         return node.parent
637:         #psn = preceding_node_of( node.parent )
638:       end
639:       while psn and psn.kind_of? Element and psn.children.size > 0
640:         psn = psn.children[-1]
641:       end
642:       psn
643:     end

[Source]

     # File lib/xmpp4r/rexmladdons.rb, line 590
590:     def recurse( nodeset, &block )
591:       for node in nodeset
592:         yield node
593:         recurse( node, &block ) if node.node_type == :element
594:       end
595:     end

[Validate]