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 759
759:     def compare a, op, b
760:       #puts "COMPARE #{a.inspect}(#{a.class.name}) #{op} #{b.inspect}(#{b.class.name})"
761:       case op
762:       when :eq
763:         a == b
764:       when :neq
765:         a != b
766:       when :lt
767:         a < b
768:       when :lteq
769:         a <= b
770:       when :gt
771:         a > b
772:       when :gteq
773:         a >= b
774:       when :and
775:         a and b
776:       when :or
777:         a or b
778:       else
779:         false
780:       end
781:     end

[Source]

     # File lib/rexml/xpath_parser.rb, line 524
524:     def d_o_s( p, ns, r )
525:       #puts "IN DOS with #{ns.inspect}; ALREADY HAVE #{r.inspect}"
526:       nt = nil
527:       ns.each_index do |i|
528:         n = ns[i]
529:         #puts "P => #{p.inspect}"
530:         x = expr( p.dclone, [ n ] )
531:         nt = n.node_type
532:         d_o_s( p, n.children, x ) if nt == :element or nt == :document and n.children.size > 0
533:         r.concat(x) if x.size > 0
534:       end
535:     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 513
513:     def descendant_or_self( path_stack, nodeset )
514:       rs = []
515:       #puts "#"*80
516:       #puts "PATH_STACK = #{path_stack.inspect}"
517:       #puts "NODESET = #{nodeset.collect{|n|n.inspect}.inspect}"
518:       d_o_s( path_stack, nodeset, rs )
519:       #puts "RS = #{rs.collect{|n|n.inspect}.inspect}"
520:       document_order(rs.flatten.compact)
521:       #rs.flatten.compact
522:     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 546
546:     def document_order( array_of_nodes )
547:       new_arry = []
548:       array_of_nodes.each { |node|
549:         node_idx = [] 
550:         np = node.node_type == :attribute ? node.element : node
551:         while np.parent and np.parent.node_type == :element
552:           node_idx << np.parent.index( np )
553:           np = np.parent
554:         end
555:         new_arry << [ node_idx.reverse, node ]
556:       }
557:       #puts "new_arry = #{new_arry.inspect}"
558:       new_arry.sort{ |s1, s2| s1[0] <=> s2[0] }.collect{ |s| s[1] }
559:     end

[Source]

     # File lib/rexml/xpath_parser.rb, line 666
666:     def equality_relational_compare( set1, op, set2 )
667:       #puts "EQ_REL_COMP(#{set1.inspect} #{op.inspect} #{set2.inspect})"
668:       if set1.kind_of? Array and set2.kind_of? Array
669:                           #puts "#{set1.size} & #{set2.size}"
670:         if set1.size == 1 and set2.size == 1
671:           set1 = set1[0]
672:           set2 = set2[0]
673:         elsif set1.size == 0 or set2.size == 0
674:           nd = set1.size==0 ? set2 : set1
675:           rv = nd.collect { |il| compare( il, op, nil ) }
676:           #puts "RV = #{rv.inspect}"
677:           return rv
678:         else
679:           res = []
680:           enum = SyncEnumerator.new( set1, set2 ).each { |i1, i2|
681:             #puts "i1 = #{i1.inspect} (#{i1.class.name})"
682:             #puts "i2 = #{i2.inspect} (#{i2.class.name})"
683:             i1 = norm( i1 )
684:             i2 = norm( i2 )
685:             res << compare( i1, op, i2 )
686:           }
687:           return res
688:         end
689:       end
690:                   #puts "EQ_REL_COMP: #{set1.inspect} (#{set1.class.name}), #{op}, #{set2.inspect} (#{set2.class.name})"
691:       #puts "COMPARING VALUES"
692:       # If one is nodeset and other is number, compare number to each item
693:       # in nodeset s.t. number op number(string(item))
694:       # If one is nodeset and other is string, compare string to each item
695:       # in nodeset s.t. string op string(item)
696:       # If one is nodeset and other is boolean, compare boolean to each item
697:       # in nodeset s.t. boolean op boolean(item)
698:       if set1.kind_of? Array or set2.kind_of? Array
699:                           #puts "ISA ARRAY"
700:         if set1.kind_of? Array
701:           a = set1
702:           b = set2
703:         else
704:           a = set2
705:           b = set1
706:         end
707: 
708:         case b
709:         when true, false
710:           return a.collect {|v| compare( Functions::boolean(v), op, b ) }
711:         when Numeric
712:           return a.collect {|v| compare( Functions::number(v), op, b )}
713:         when /^\d+(\.\d+)?$/
714:           b = Functions::number( b )
715:           #puts "B = #{b.inspect}"
716:           return a.collect {|v| compare( Functions::number(v), op, b )}
717:         else
718:                                   #puts "Functions::string( #{b}(#{b.class.name}) ) = #{Functions::string(b)}"
719:           b = Functions::string( b )
720:           return a.collect { |v| compare( Functions::string(v), op, b ) }
721:         end
722:       else
723:         # If neither is nodeset,
724:         #   If op is = or !=
725:         #     If either boolean, convert to boolean
726:         #     If either number, convert to number
727:         #     Else, convert to string
728:         #   Else
729:         #     Convert both to numbers and compare
730:         s1 = set1.to_s
731:         s2 = set2.to_s
732:         #puts "EQ_REL_COMP: #{set1}=>#{s1}, #{set2}=>#{s2}"
733:         if s1 == 'true' or s1 == 'false' or s2 == 'true' or s2 == 'false'
734:           #puts "Functions::boolean(#{set1})=>#{Functions::boolean(set1)}"
735:           #puts "Functions::boolean(#{set2})=>#{Functions::boolean(set2)}"
736:           set1 = Functions::boolean( set1 )
737:           set2 = Functions::boolean( set2 )
738:         else
739:           if op == :eq or op == :neq
740:             if s1 =~ /^\d+(\.\d+)?$/ or s2 =~ /^\d+(\.\d+)?$/
741:               set1 = Functions::number( s1 )
742:               set2 = Functions::number( s2 )
743:             else
744:               set1 = Functions::string( set1 )
745:               set2 = Functions::string( set2 )
746:             end
747:           else
748:             set1 = Functions::number( set1 )
749:             set2 = Functions::number( set2 )
750:           end
751:         end
752:         #puts "EQ_REL_COMP: #{set1} #{op} #{set2}"
753:         #puts ">>> #{compare( set1, op, set2 )}"
754:         return compare( set1, op, set2 )
755:       end
756:       return false
757:     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 "Path stack = #{path_stack.inspect}"
164:         #puts "Nodeset is #{nodeset.inspect}"
165:         if nodeset.length == 0
166:           path_stack.clear
167:           return []
168:         end
169:         case (op = path_stack.shift)
170:         when :document
171:           nodeset = [ nodeset[0].root_node ]
172:           #puts ":document, nodeset = #{nodeset.inspect}"
173: 
174:         when :qname
175:           #puts "IN QNAME"
176:           prefix = path_stack.shift
177:           name = path_stack.shift
178:           nodeset.delete_if do |node|
179:             # FIXME: This DOUBLES the time XPath searches take
180:             ns = get_namespace( node, prefix )
181:             #puts "NS = #{ns.inspect}"
182:             #puts "node.node_type == :element => #{node.node_type == :element}"
183:             if node.node_type == :element
184:               #puts "node.name == #{name} => #{node.name == name}"
185:               if node.name == name
186:                 #puts "node.namespace == #{ns.inspect} => #{node.namespace == ns}"
187:               end
188:             end
189:             !(node.node_type == :element and 
190:               node.name == name and 
191:               node.namespace == ns )
192:           end
193:           node_types = ELEMENTS
194: 
195:         when :any
196:           #puts "ANY 1: nodeset = #{nodeset.inspect}"
197:           #puts "ANY 1: node_types = #{node_types.inspect}"
198:           nodeset.delete_if { |node| !node_types.include?(node.node_type) }
199:           #puts "ANY 2: nodeset = #{nodeset.inspect}"
200: 
201:         when :self
202:           # This space left intentionally blank
203: 
204:         when :processing_instruction
205:           target = path_stack.shift
206:           nodeset.delete_if do |node|
207:             (node.node_type != :processing_instruction) or 
208:             ( target!='' and ( node.target != target ) )
209:           end
210: 
211:         when :text
212:           nodeset.delete_if { |node| node.node_type != :text }
213: 
214:         when :comment
215:           nodeset.delete_if { |node| node.node_type != :comment }
216: 
217:         when :node
218:           # This space left intentionally blank
219:           node_types = ALL
220: 
221:         when :child
222:           new_nodeset = []
223:           nt = nil
224:           for node in nodeset
225:             nt = node.node_type
226:             new_nodeset += node.children if nt == :element or nt == :document
227:           end
228:           nodeset = new_nodeset
229:           node_types = ELEMENTS
230: 
231:         when :literal
232:           return path_stack.shift
233:         
234:         when :attribute
235:           new_nodeset = []
236:           case path_stack.shift
237:           when :qname
238:             prefix = path_stack.shift
239:             name = path_stack.shift
240:             for element in nodeset
241:               if element.node_type == :element
242:                 #puts "Element name = #{element.name}"
243:                 #puts "get_namespace( #{element.inspect}, #{prefix} ) = #{get_namespace(element, prefix)}"
244:                 attrib = element.attribute( name, get_namespace(element, prefix) )
245:                 #puts "attrib = #{attrib.inspect}"
246:                 new_nodeset << attrib if attrib
247:               end
248:             end
249:           when :any
250:             #puts "ANY"
251:             for element in nodeset
252:               if element.node_type == :element
253:                 new_nodeset += element.attributes.to_a
254:               end
255:             end
256:           end
257:           nodeset = new_nodeset
258: 
259:         when :parent
260:           #puts "PARENT 1: nodeset = #{nodeset}"
261:           nodeset = nodeset.collect{|n| n.parent}.compact
262:           #nodeset = expr(path_stack.dclone, nodeset.collect{|n| n.parent}.compact)
263:           #puts "PARENT 2: nodeset = #{nodeset.inspect}"
264:           node_types = ELEMENTS
265: 
266:         when :ancestor
267:           new_nodeset = []
268:           for node in nodeset
269:             while node.parent
270:               node = node.parent
271:               new_nodeset << node unless new_nodeset.include? node
272:             end
273:           end
274:           nodeset = new_nodeset
275:           node_types = ELEMENTS
276: 
277:         when :ancestor_or_self
278:           new_nodeset = []
279:           for node in nodeset
280:             if node.node_type == :element
281:               new_nodeset << node
282:               while ( node.parent )
283:                 node = node.parent
284:                 new_nodeset << node unless new_nodeset.include? node
285:               end
286:             end
287:           end
288:           nodeset = new_nodeset
289:           node_types = ELEMENTS
290: 
291:         when :predicate
292:           new_nodeset = []
293:           subcontext = { :size => nodeset.size }
294:           pred = path_stack.shift
295:           nodeset.each_with_index { |node, index|
296:             subcontext[ :node ] = node
297:             #puts "PREDICATE SETTING CONTEXT INDEX TO #{index+1}"
298:             subcontext[ :index ] = index+1
299:             pc = pred.dclone
300:             #puts "#{node.hash}) Recursing with #{pred.inspect} and [#{node.inspect}]"
301:             result = expr( pc, [node], subcontext )
302:             result = result[0] if result.kind_of? Array and result.length == 1
303:             #puts "#{node.hash}) Result = #{result.inspect} (#{result.class.name})"
304:             if result.kind_of? Numeric
305:               #puts "Adding node #{node.inspect}" if result == (index+1)
306:               new_nodeset << node if result == (index+1)
307:             elsif result.instance_of? Array
308:               if result.size > 0 and result.inject(false) {|k,s| s or k}
309:                 #puts "Adding node #{node.inspect}" if result.size > 0
310:                 new_nodeset << node if result.size > 0
311:               end
312:             else
313:               #puts "Adding node #{node.inspect}" if result
314:               new_nodeset << node if result
315:             end
316:           }
317:           #puts "New nodeset = #{new_nodeset.inspect}"
318:           #puts "Path_stack  = #{path_stack.inspect}"
319:           nodeset = new_nodeset
320: ??
321: 
322:         when :descendant_or_self
323:           rv = descendant_or_self( path_stack, nodeset )
324:           path_stack.clear
325:           nodeset = rv
326:           node_types = ELEMENTS
327: 
328:         when :descendant
329:           results = []
330:           nt = nil
331:           for node in nodeset
332:             nt = node.node_type
333:             results += expr( path_stack.dclone.unshift( :descendant_or_self ),
334:               node.children ) if nt == :element or nt == :document
335:           end
336:           nodeset = results
337:           node_types = ELEMENTS
338: 
339:         when :following_sibling
340:           #puts "FOLLOWING_SIBLING 1: nodeset = #{nodeset}"
341:           results = []
342:           for node in nodeset
343:             all_siblings = node.parent.children
344:             current_index = all_siblings.index( node )
345:             following_siblings = all_siblings[ current_index+1 .. -1 ]
346:             results += expr( path_stack.dclone, following_siblings )
347:           end
348:           #puts "FOLLOWING_SIBLING 2: nodeset = #{nodeset}"
349:           nodeset = results
350: 
351:         when :preceding_sibling
352:           results = []
353:           for node in nodeset
354:             all_siblings = node.parent.children
355:             current_index = all_siblings.index( node )
356:             preceding_siblings = all_siblings[ 0 .. current_index-1 ].reverse
357:             #results += expr( path_stack.dclone, preceding_siblings )
358:           end
359:           nodeset = preceding_siblings || []
360:           node_types = ELEMENTS
361: 
362:         when :preceding
363:           new_nodeset = []
364:           for node in nodeset
365:             new_nodeset += preceding( node )
366:           end
367:           #puts "NEW NODESET => #{new_nodeset.inspect}"
368:           nodeset = new_nodeset
369:           node_types = ELEMENTS
370: 
371:         when :following
372:           new_nodeset = []
373:           for node in nodeset
374:             new_nodeset += following( node )
375:           end
376:           nodeset = new_nodeset
377:           node_types = ELEMENTS
378: 
379:         when :namespace
380:           new_nodeset = []
381:           prefix = path_stack.shift
382:           for node in nodeset
383:             if (node.node_type == :element or node.node_type == :attribute)
384:               if (node.node_type == :element)
385:                 namespaces = node.namespaces
386:               else
387:                 namespaces = node.element.namesapces
388:               end
389:               if (node.namespace == namespaces[prefix])
390:                 new_nodeset << node
391:               end
392:             end
393:           end
394:           nodeset = new_nodeset
395: 
396:         when :variable
397:           var_name = path_stack.shift
398:           return @variables[ var_name ]
399: 
400:         # :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq
401:                                 # TODO: Special case for :or and :and -- not evaluate the right
402:                                 # operand if the left alone determines result (i.e. is true for
403:                                 # :or and false for :and).
404:         when :eq, :neq, :lt, :lteq, :gt, :gteq, :and, :or
405:           left = expr( path_stack.shift, nodeset.dup, context )
406:           #puts "LEFT => #{left.inspect} (#{left.class.name})"
407:           right = expr( path_stack.shift, nodeset.dup, context )
408:           #puts "RIGHT => #{right.inspect} (#{right.class.name})"
409:           res = equality_relational_compare( left, op, right )
410:           #puts "RES => #{res.inspect}"
411:           return res
412: 
413:         when :and
414:           left = expr( path_stack.shift, nodeset.dup, context )
415:           #puts "LEFT => #{left.inspect} (#{left.class.name})"
416:           if left == false || left.nil? || !left.inject(false) {|a,b| a | b}
417:             return []
418:           end
419:           right = expr( path_stack.shift, nodeset.dup, context )
420:           #puts "RIGHT => #{right.inspect} (#{right.class.name})"
421:           res = equality_relational_compare( left, op, right )
422:           #puts "RES => #{res.inspect}"
423:           return res
424: 
425:         when :div
426:           left = Functions::number(expr(path_stack.shift, nodeset, context)).to_f
427:           right = Functions::number(expr(path_stack.shift, nodeset, context)).to_f
428:           return (left / right)
429: 
430:         when :mod
431:           left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
432:           right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
433:           return (left % right)
434: 
435:         when :mult
436:           left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
437:           right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
438:           return (left * right)
439: 
440:         when :plus
441:           left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
442:           right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
443:           return (left + right)
444: 
445:         when :minus
446:           left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
447:           right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
448:           return (left - right)
449: 
450:         when :union
451:           left = expr( path_stack.shift, nodeset, context )
452:           right = expr( path_stack.shift, nodeset, context )
453:           return (left | right)
454: 
455:         when :neg
456:           res = expr( path_stack, nodeset, context )
457:           return -(res.to_f)
458: 
459:         when :not
460:         when :function
461:           func_name = path_stack.shift.tr('-','_')
462:           arguments = path_stack.shift
463:           #puts "FUNCTION 0: #{func_name}(#{arguments.collect{|a|a.inspect}.join(', ')})" 
464:           subcontext = context ? nil : { :size => nodeset.size }
465: 
466:           res = []
467:           cont = context
468:           nodeset.each_with_index { |n, i| 
469:             if subcontext
470:               subcontext[:node]  = n
471:               subcontext[:index] = i
472:               cont = subcontext
473:             end
474:             arg_clone = arguments.dclone
475:             args = arg_clone.collect { |arg| 
476:               #puts "FUNCTION 1: Calling expr( #{arg.inspect}, [#{n.inspect}] )"
477:               expr( arg, [n], cont ) 
478:             }
479:             #puts "FUNCTION 2: #{func_name}(#{args.collect{|a|a.inspect}.join(', ')})" 
480:             Functions.context = cont
481:             res << Functions.send( func_name, *args )
482:             #puts "FUNCTION 3: #{res[-1].inspect}"
483:           }
484:           return res
485: 
486:         end
487:       end # while
488:       #puts "EXPR returning #{nodeset.inspect}"
489:       return nodeset
490:     end

[Source]

     # File lib/rexml/xpath_parser.rb, line 617
617:     def following( node )
618:       #puts "IN PRECEDING"
619:       acc = []
620:       p = next_sibling_node( node )
621:       #puts "P = #{p.inspect}"
622:       while p
623:         acc << p
624:         p = following_node_of( p )
625:         #puts "P = #{p.inspect}"
626:       end
627:       acc
628:     end

[Source]

     # File lib/rexml/xpath_parser.rb, line 630
630:     def following_node_of( node )
631:       #puts "NODE: #{node.inspect}"
632:       #puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}"
633:       #puts "PARENT NODE: #{node.parent}"
634:       if node.kind_of? Element and node.children.size > 0
635:         return node.children[0]
636:       end
637:       return next_sibling_node(node)
638:     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 640
640:     def next_sibling_node(node)
641:       psn = node.next_sibling_node 
642:       while psn.nil?
643:         if node.parent.nil? or node.parent.class == Document 
644:           return nil
645:         end
646:         node = node.parent
647:         psn = node.next_sibling_node
648:         #puts "psn = #{psn.inspect}"
649:       end
650:       return psn
651:     end

[Source]

     # File lib/rexml/xpath_parser.rb, line 653
653:     def norm b
654:       case b
655:       when true, false
656:         return b
657:       when 'true', 'false'
658:         return Functions::boolean( b )
659:       when /^\d+(\.\d+)?$/
660:         return Functions::number( b )
661:       else
662:         return Functions::string( b )
663:       end
664:     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 575
575:     def preceding( node )
576:       #puts "IN PRECEDING"
577:       ancestors = []
578:       p = node.parent
579:       while p
580:         ancestors << p
581:         p = p.parent
582:       end
583: 
584:       acc = []
585:       p = preceding_node_of( node )
586:       #puts "P = #{p.inspect}"
587:       while p
588:         if ancestors.include? p
589:           ancestors.delete(p)
590:         else
591:           acc << p
592:         end
593:         p = preceding_node_of( p )
594:         #puts "P = #{p.inspect}"
595:       end
596:       acc
597:     end

[Source]

     # File lib/rexml/xpath_parser.rb, line 599
599:     def preceding_node_of( node )
600:      #puts "NODE: #{node.inspect}"
601:      #puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}"
602:      #puts "PARENT NODE: #{node.parent}"
603:       psn = node.previous_sibling_node 
604:       if psn.nil?
605:         if node.parent.nil? or node.parent.class == Document 
606:           return nil
607:         end
608:         return node.parent
609:         #psn = preceding_node_of( node.parent )
610:       end
611:       while psn and psn.kind_of? Element and psn.children.size > 0
612:         psn = psn.children[-1]
613:       end
614:       psn
615:     end

[Source]

     # File lib/rexml/xpath_parser.rb, line 562
562:     def recurse( nodeset, &block )
563:       for node in nodeset
564:         yield node
565:         recurse( node, &block ) if node.node_type == :element
566:       end
567:     end

[Validate]