Class REXML::XPathParser
In: lib/rexml/xpath_parser.rb
Parent: Object

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

LITERAL = /^'([^']*)'|^"([^"]*)"/u
ALL = [ :attribute, :element, :text, :processing_instruction, :comment ]   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 ]

Public Class methods

[Source]

    # File lib/rexml/xpath_parser.rb, line 39
39:     def initialize( )
40:       @parser = REXML::Parsers::XPathParser.new
41:       @namespaces = nil
42:       @variables = {}
43:     end

Public Instance methods

[Source]

    # File lib/rexml/xpath_parser.rb, line 76
76:     def []=( variable_name, value )
77:       @variables[ variable_name ] = value
78:     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.

FIXME: This method is incomplete!

[Source]

     # File lib/rexml/xpath_parser.rb, line 85
 85:     def first( path_stack, node )
 86:       #puts "#{depth}) Entering match( #{path.inspect}, #{tree.inspect} )"
 87:       return nil if path.size == 0
 88: 
 89:       case path[0]
 90:       when :document
 91:         # do nothing 
 92:         return first( path[1..-1], node )
 93:       when :child
 94:         for c in node.children
 95:           #puts "#{depth}) CHILD checking #{name(c)}"
 96:           r = first( path[1..-1], c )
 97:           #puts "#{depth}) RETURNING #{r.inspect}" if r
 98:           return r if r
 99:         end
100:       when :qname
101:         name = path[2]
102:         #puts "#{depth}) QNAME #{name(tree)} == #{name} (path => #{path.size})"
103:         if node.name == name
104:           #puts "#{depth}) RETURNING #{tree.inspect}" if path.size == 3
105:           return node if path.size == 3
106:           return first( path[3..-1], node )
107:         else
108:           return nil
109:         end
110:       when :descendant_or_self
111:         r = first( path[1..-1], node )
112:         return r if r
113:         for c in node.children
114:           r = first( path, c )
115:           return r if r
116:         end
117:       when :node
118:         return first( path[1..-1], node )
119:       when :any
120:         return first( path[1..-1], node )
121:       end
122:       return nil
123:     end

[Source]

    # File lib/rexml/xpath_parser.rb, line 63
63:     def get_first path, nodeset
64:      #puts "#"*40
65:      path_stack = @parser.parse( path )
66:      #puts "PARSE: #{path} => #{path_stack.inspect}"
67:      #puts "PARSE: nodeset = #{nodeset.inspect}"
68:      first( path_stack, nodeset )
69:     end

[Source]

     # File lib/rexml/xpath_parser.rb, line 126
126:     def match( path_stack, nodeset ) 
127:       #puts "MATCH: path_stack = #{path_stack.inspect}"
128:       #puts "MATCH: nodeset = #{nodeset.inspect}"
129:       r = expr( path_stack, nodeset )
130:       #puts "MAIN EXPR => #{r.inspect}"
131:       r
132:     end

[Source]

    # File lib/rexml/xpath_parser.rb, line 45
45:     def namespaces=( namespaces={} )
46:       Functions::namespace_context = namespaces
47:       @namespaces = namespaces
48:     end

[Source]

    # File lib/rexml/xpath_parser.rb, line 55
55:     def parse path, nodeset
56:      #puts "#"*40
57:      path_stack = @parser.parse( path )
58:      #puts "PARSE: #{path} => #{path_stack.inspect}"
59:      #puts "PARSE: nodeset = #{nodeset.inspect}"
60:      match( path_stack, nodeset )
61:     end

[Source]

    # File lib/rexml/xpath_parser.rb, line 71
71:     def predicate path, nodeset
72:       path_stack = @parser.parse( path )
73:       expr( path_stack, nodeset )
74:     end

[Source]

    # File lib/rexml/xpath_parser.rb, line 50
50:     def variables=( vars={} )
51:       Functions::variables = vars
52:       @variables = vars
53:     end

Private Instance methods

[Source]

     # File lib/rexml/xpath_parser.rb, line 768
768:     def compare a, op, b
769:       #puts "COMPARE #{a.inspect}(#{a.class.name}) #{op} #{b.inspect}(#{b.class.name})"
770:       case op
771:       when :eq
772:         a == b
773:       when :neq
774:         a != b
775:       when :lt
776:         a < b
777:       when :lteq
778:         a <= b
779:       when :gt
780:         a > b
781:       when :gteq
782:         a >= b
783:       when :and
784:         a and b
785:       when :or
786:         a or b
787:       else
788:         false
789:       end
790:     end

[Source]

     # File lib/rexml/xpath_parser.rb, line 533
533:     def d_o_s( p, ns, r )
534:       #puts "IN DOS with #{ns.inspect}; ALREADY HAVE #{r.inspect}"
535:       nt = nil
536:       ns.each_index do |i|
537:         n = ns[i]
538:         #puts "P => #{p.inspect}"
539:         x = expr( p.dclone, [ n ] )
540:         nt = n.node_type
541:         d_o_s( p, n.children, x ) if nt == :element or nt == :document and n.children.size > 0
542:         r.concat(x) if x.size > 0
543:       end
544:     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 is a wonder it works at all. ########################################################

[Source]

     # File lib/rexml/xpath_parser.rb, line 522
522:     def descendant_or_self( path_stack, nodeset )
523:       rs = []
524:       #puts "#"*80
525:       #puts "PATH_STACK = #{path_stack.inspect}"
526:       #puts "NODESET = #{nodeset.collect{|n|n.inspect}.inspect}"
527:       d_o_s( path_stack, nodeset, rs )
528:       #puts "RS = #{rs.collect{|n|n.inspect}.inspect}"
529:       document_order(rs.flatten.compact)
530:       #rs.flatten.compact
531:     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/rexml/xpath_parser.rb, line 555
555:     def document_order( array_of_nodes )
556:       new_arry = []
557:       array_of_nodes.each { |node|
558:         node_idx = [] 
559:         np = node.node_type == :attribute ? node.element : node
560:         while np.parent and np.parent.node_type == :element
561:           node_idx << np.parent.index( np )
562:           np = np.parent
563:         end
564:         new_arry << [ node_idx.reverse, node ]
565:       }
566:       #puts "new_arry = #{new_arry.inspect}"
567:       new_arry.sort{ |s1, s2| s1[0] <=> s2[0] }.collect{ |s| s[1] }
568:     end

[Source]

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

[Source]

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

[Source]

     # File lib/rexml/xpath_parser.rb, line 626
626:     def following( node )
627:       #puts "IN PRECEDING"
628:       acc = []
629:       p = next_sibling_node( node )
630:       #puts "P = #{p.inspect}"
631:       while p
632:         acc << p
633:         p = following_node_of( p )
634:         #puts "P = #{p.inspect}"
635:       end
636:       acc
637:     end

[Source]

     # File lib/rexml/xpath_parser.rb, line 639
639:     def following_node_of( node )
640:       #puts "NODE: #{node.inspect}"
641:       #puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}"
642:       #puts "PARENT NODE: #{node.parent}"
643:       if node.kind_of? Element and node.children.size > 0
644:         return node.children[0]
645:       end
646:       return next_sibling_node(node)
647:     end

Returns a String namespace for a node, given a prefix The rules are:

 1. Use the supplied namespace mapping first.
 2. If no mapping was supplied, use the context node to look up the namespace

[Source]

     # File lib/rexml/xpath_parser.rb, line 142
142:     def get_namespace( node, prefix )
143:       if @namespaces
144:         return @namespaces[prefix] || ''
145:       else
146:         return node.namespace( prefix ) if node.node_type == :element
147:         return ''
148:       end
149:     end

[Source]

     # File lib/rexml/xpath_parser.rb, line 649
649:     def next_sibling_node(node)
650:       psn = node.next_sibling_node 
651:       while psn.nil?
652:         if node.parent.nil? or node.parent.class == Document 
653:           return nil
654:         end
655:         node = node.parent
656:         psn = node.next_sibling_node
657:         #puts "psn = #{psn.inspect}"
658:       end
659:       return psn
660:     end

[Source]

     # File lib/rexml/xpath_parser.rb, line 662
662:     def norm b
663:       case b
664:       when true, false
665:         return b
666:       when 'true', 'false'
667:         return Functions::boolean( b )
668:       when /^\d+(\.\d+)?$/
669:         return Functions::number( b )
670:       else
671:         return Functions::string( b )
672:       end
673:     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/rexml/xpath_parser.rb, line 584
584:     def preceding( node )
585:       #puts "IN PRECEDING"
586:       ancestors = []
587:       p = node.parent
588:       while p
589:         ancestors << p
590:         p = p.parent
591:       end
592: 
593:       acc = []
594:       p = preceding_node_of( node )
595:       #puts "P = #{p.inspect}"
596:       while p
597:         if ancestors.include? p
598:           ancestors.delete(p)
599:         else
600:           acc << p
601:         end
602:         p = preceding_node_of( p )
603:         #puts "P = #{p.inspect}"
604:       end
605:       acc
606:     end

[Source]

     # File lib/rexml/xpath_parser.rb, line 608
608:     def preceding_node_of( node )
609:      #puts "NODE: #{node.inspect}"
610:      #puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}"
611:      #puts "PARENT NODE: #{node.parent}"
612:       psn = node.previous_sibling_node 
613:       if psn.nil?
614:         if node.parent.nil? or node.parent.class == Document 
615:           return nil
616:         end
617:         return node.parent
618:         #psn = preceding_node_of( node.parent )
619:       end
620:       while psn and psn.kind_of? Element and psn.children.size > 0
621:         psn = psn.children[-1]
622:       end
623:       psn
624:     end

[Source]

     # File lib/rexml/xpath_parser.rb, line 571
571:     def recurse( nodeset, &block )
572:       for node in nodeset
573:         yield node
574:         recurse( node, &block ) if node.node_type == :element
575:       end
576:     end

[Validate]