Class REXML::Parsers::XPathParser
In: lib/rexml/parsers/xpathparser.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
AXIS = /^(ancestor|ancestor-or-self|attribute|child|descendant|descendant-or-self|following|following-sibling|namespace|parent|preceding|preceding-sibling|self)::/   RelativeLocationPath
  |                                                    Step
    | (AXIS_NAME '::' | '@' | '')                     AxisSpecifier
      NodeTest
        Predicate
    | '.' | '..'                                      AbbreviatedStep
  |  RelativeLocationPath '/' Step
  | RelativeLocationPath '//' Step
NCNAMETEST = /^(#{NCNAME_STR}):\*/u  
 Returns a 1-1 map of the nodeset
 The contents of the resulting array are either:
   true/false, if a positive match
   String, if a name match
NodeTest
  | ('*' | NCNAME ':' '*' | QNAME)                NameTest
  | NODE_TYPE '(' ')'                              NodeType
  | PI '(' LITERAL ')'                            PI
    | '[' expr ']'                                Predicate
QNAME = Namespace::NAMESPLIT
NODE_TYPE = /^(comment|text|node)\(\s*\)/m
PI = /^processing-instruction\(/
VARIABLE_REFERENCE = /^\$(#{NAME_STR})/u   | VARIABLE_REFERENCE | ’(’ expr ’)’ | LITERAL | NUMBER | FunctionCall
NUMBER = /^(\d*\.?\d+)/
NT = /^comment|text|processing-instruction|node$/

Public Instance methods

[Source]

    # File lib/rexml/parsers/xpathparser.rb, line 33
33:       def abbreviate( path )
34:         path = path.kind_of?(String) ? parse( path ) : path
35:         string = ""
36:         document = false
37:         while path.size > 0
38:           op = path.shift
39:           case op
40:           when :node
41:           when :attribute
42:                                                 string << "/" if string.size > 0
43:                                                 string << "@"
44:           when :child
45:                                                 string << "/" if string.size > 0
46:           when :descendant_or_self
47:             string << "/"
48:           when :self
49:             string << "."
50:           when :parent
51:             string << ".."
52:           when :any
53:             string << "*"
54:                                         when :text
55:                                                 string << "text()"
56:           when :following, :following_sibling, 
57:                 :ancestor, :ancestor_or_self, :descendant, 
58:                 :namespace, :preceding, :preceding_sibling
59:             string << "/" unless string.size == 0
60:             string << op.to_s.tr("_", "-")
61:             string << "::"
62:           when :qname
63:             prefix = path.shift
64:             name = path.shift
65:             string << prefix+":" if prefix.size > 0
66:             string << name
67:           when :predicate
68:             string << '['
69:             string << predicate_to_string( path.shift ) {|x| abbreviate( x ) }
70:             string << ']'
71:           when :document
72:             document = true
73:                                         when :function
74:                                                 string << path.shift
75:                                                 string << "( "
76:                                                 string << predicate_to_string( path.shift[0] ) {|x| abbreviate( x )}
77:                                                 string << " )"
78:                                         when :literal
79:                                                 string << %Q{ "#{path.shift}" }
80:           else
81:             string << "/" unless string.size == 0
82:             string << "UNKNOWN("
83:             string << op.inspect
84:             string << ")"
85:           end
86:         end
87:                                 string = "/"+string if document
88:         return string
89:       end

[Source]

     # File lib/rexml/parsers/xpathparser.rb, line 91
 91:       def expand( path )
 92:         path = path.kind_of?(String) ? parse( path ) : path
 93:         string = ""
 94:         document = false
 95:         while path.size > 0
 96:           op = path.shift
 97:           case op
 98:           when :node
 99:             string << "node()"
100:           when :attribute, :child, :following, :following_sibling, 
101:                 :ancestor, :ancestor_or_self, :descendant, :descendant_or_self,
102:                 :namespace, :preceding, :preceding_sibling, :self, :parent
103:             string << "/" unless string.size == 0
104:             string << op.to_s.tr("_", "-")
105:             string << "::"
106:           when :any
107:             string << "*"
108:           when :qname
109:             prefix = path.shift
110:             name = path.shift
111:             string << prefix+":" if prefix.size > 0
112:             string << name
113:           when :predicate
114:             string << '['
115:             string << predicate_to_string( path.shift ) { |x| expand(x) }
116:             string << ']'
117:           when :document
118:             document = true
119:           else
120:             string << "/" unless string.size == 0
121:             string << "UNKNOWN("
122:             string << op.inspect
123:             string << ")"
124:           end
125:         end
126:         string = "/"+string if document
127:         return string
128:       end

[Source]

    # File lib/rexml/parsers/xpathparser.rb, line 14
14:       def namespaces=( namespaces )
15:         Functions::namespace_context = namespaces
16:         @namespaces = namespaces
17:       end

[Source]

    # File lib/rexml/parsers/xpathparser.rb, line 19
19:       def parse path
20:         path.gsub!(/([\(\[])\s+/, '\1') # Strip ignorable spaces
21:         path.gsub!( /\s+([\]\)])/, '\1' )
22:         parsed = []
23:         path = OrExpr(path, parsed)
24:         parsed
25:       end

[Source]

    # File lib/rexml/parsers/xpathparser.rb, line 27
27:       def predicate path
28:         parsed = []
29:         Predicate( "[#{path}]", parsed )
30:         parsed
31:       end

[Source]

     # File lib/rexml/parsers/xpathparser.rb, line 130
130:       def predicate_to_string( path, &block )
131:         string = ""
132:         case path[0]
133:         when :and, :or, :mult, :plus, :minus, :neq, :eq, :lt, :gt, :lteq, :gteq, :div, :mod, :union
134:           op = path.shift
135:           case op
136:           when :eq
137:             op = "="
138:           when :lt
139:             op = "<"
140:           when :gt
141:             op = ">"
142:           when :lteq
143:             op = "<="
144:           when :gteq
145:             op = ">="
146:           when :neq
147:             op = "!="
148:           when :union
149:             op = "|"
150:           end
151:           left = predicate_to_string( path.shift, &block )
152:           right = predicate_to_string( path.shift, &block )
153:           string << " "
154:           string << left
155:           string << " "
156:           string << op.to_s
157:           string << " "
158:           string << right
159:           string << " "
160:         when :function
161:           path.shift
162:           name = path.shift
163:           string << name
164:           string << "( "
165:           string << predicate_to_string( path.shift, &block )
166:           string << " )"
167:         when :literal
168:           path.shift
169:           string << " "
170:           string << path.shift.inspect
171:           string << " "
172:         else
173:           string << " "
174:           string << yield( path )
175:           string << " "
176:         end
177:         return string.squeeze(" ")
178:       end

Private Instance methods

| AdditiveExpr (’+’ | S ’-’) MultiplicativeExpr | MultiplicativeExpr

[Source]

     # File lib/rexml/parsers/xpathparser.rb, line 448
448:       def AdditiveExpr path, parsed
449:         #puts "ADDITIVE >>> #{path}"
450:         n = []
451:         rest = MultiplicativeExpr( path, n )
452:         #puts "ADDITIVE <<< #{rest}"
453:         if rest != path
454:           while rest =~ /^\s*(\+| -)\s*/
455:             if $1[0] == ?+
456:               n = [ :plus, n, [] ]
457:             else
458:               n = [ :minus, n, [] ]
459:             end
460:             rest = MultiplicativeExpr( $', n[-1] )
461:           end
462:         end
463:         if parsed.size == 0 and n.size != 0
464:           parsed.replace(n)
465:         elsif n.size > 0
466:           parsed << n
467:         end
468:         rest
469:       end

| AndExpr S ‘and’ S EqualityExpr | EqualityExpr

[Source]

     # File lib/rexml/parsers/xpathparser.rb, line 373
373:       def AndExpr path, parsed
374:         #puts "AND >>> #{path}"
375:         n = []
376:         rest = EqualityExpr( path, n )
377:         #puts "AND <<< #{rest}"
378:         if rest != path
379:           while rest =~ /^\s*( and )/
380:             n = [ :and, n, [] ]
381:             #puts "AND >>> #{rest}"
382:             rest = EqualityExpr( $', n[-1] )
383:             #puts "AND <<< #{rest}"
384:           end
385:         end
386:         if parsed.size == 0 and n.size != 0
387:           parsed.replace(n)
388:         elsif n.size > 0
389:           parsed << n
390:         end
391:         rest
392:       end

| EqualityExpr (’=’ | ’!=’) RelationalExpr | RelationalExpr

[Source]

     # File lib/rexml/parsers/xpathparser.rb, line 396
396:       def EqualityExpr path, parsed
397:         #puts "EQUALITY >>> #{path}"
398:         n = []
399:         rest = RelationalExpr( path, n )
400:         #puts "EQUALITY <<< #{rest}"
401:         if rest != path
402:           while rest =~ /^\s*(!?=)\s*/
403:             if $1[0] == ?!
404:               n = [ :neq, n, [] ]
405:             else
406:               n = [ :eq, n, [] ]
407:             end
408:             rest = RelationalExpr( $', n[-1] )
409:           end
410:         end
411:         if parsed.size == 0 and n.size != 0
412:           parsed.replace(n)
413:         elsif n.size > 0
414:           parsed << n
415:         end
416:         rest
417:       end

| FilterExpr Predicate | PrimaryExpr

[Source]

     # File lib/rexml/parsers/xpathparser.rb, line 561
561:       def FilterExpr path, parsed
562:         #puts "FILTER >>> #{path}"
563:         n = []
564:         path = PrimaryExpr( path, n )
565:         #puts "FILTER <<< #{path}"
566:         path = Predicate(path, n) if path and path[0] == ?[
567:         #puts "FILTER <<< #{path}"
568:         parsed.concat(n)
569:         path
570:       end

| FUNCTION_NAME ’(’ ( expr ( ’,’ expr )* )? ’)’

[Source]

     # File lib/rexml/parsers/xpathparser.rb, line 622
622:       def FunctionCall rest, parsed
623:         path, arguments = parse_args(rest)
624:         argset = []
625:         for argument in arguments
626:           args = []
627:           OrExpr( argument, args )
628:           argset << args
629:         end
630:         parsed << argset
631:         path
632:       end

LocationPath

  | RelativeLocationPath
  | '/' RelativeLocationPath?
  | '//' RelativeLocationPath

[Source]

     # File lib/rexml/parsers/xpathparser.rb, line 185
185:       def LocationPath path, parsed
186:         #puts "LocationPath '#{path}'"
187:         path = path.strip
188:         if path[0] == ?/
189:           parsed << :document
190:           if path[1] == ?/
191:             parsed << :descendant_or_self
192:             parsed << :node
193:             path = path[2..-1]
194:           else
195:             path = path[1..-1]
196:           end
197:         end
198:         #puts parsed.inspect
199:         return RelativeLocationPath( path, parsed ) if path.size > 0
200:       end

| MultiplicativeExpr (’*’ | S (‘div’ | ‘mod’) S) UnaryExpr | UnaryExpr

[Source]

     # File lib/rexml/parsers/xpathparser.rb, line 473
473:       def MultiplicativeExpr path, parsed
474:         #puts "MULT >>> #{path}"
475:         n = []
476:         rest = UnaryExpr( path, n )
477:         #puts "MULT <<< #{rest}"
478:         if rest != path
479:           while rest =~ /^\s*(\*| div | mod )\s*/
480:             if $1[0] == ?*
481:               n = [ :mult, n, [] ]
482:             elsif $1.include?( "div" )
483:               n = [ :div, n, [] ]
484:             else
485:               n = [ :mod, n, [] ]
486:             end
487:             rest = UnaryExpr( $', n[-1] )
488:           end
489:         end
490:         if parsed.size == 0 and n.size != 0
491:           parsed.replace(n)
492:         elsif n.size > 0
493:           parsed << n
494:         end
495:         rest
496:       end

[Source]

     # File lib/rexml/parsers/xpathparser.rb, line 283
283:       def NodeTest path, parsed
284:         #puts "NodeTest with #{path}"
285:         res = nil
286:         case path
287:         when /^\*/
288:           path = $'
289:           parsed << :any
290:         when NODE_TYPE
291:           type = $1
292:           path = $'
293:           parsed << type.tr('-', '_').intern
294:         when PI
295:           path = $'
296:           literal = nil
297:           if path !~ /^\s*\)/
298:             path =~ LITERAL
299:             literal = $1
300:             path = $'
301:             raise ParseException.new("Missing ')' after processing instruction") if path[0] != ?)
302:             path = path[1..-1]
303:           end
304:           parsed << :processing_instruction
305:           parsed << (literal || '')
306:         when NCNAMETEST
307:           #puts "NCNAMETEST"
308:           prefix = $1
309:           path = $'
310:           parsed << :namespace
311:           parsed << prefix
312:         when QNAME
313:           #puts "QNAME"
314:           prefix = $1
315:           name = $2
316:           path = $'
317:           prefix = "" unless prefix
318:           parsed << :qname
319:           parsed << prefix
320:           parsed << name
321:         end
322:         return path
323:       end

| OrExpr S ‘or’ S AndExpr | AndExpr

[Source]

     # File lib/rexml/parsers/xpathparser.rb, line 352
352:       def OrExpr path, parsed
353:         #puts "OR >>> #{path}"
354:         n = []
355:         rest = AndExpr( path, n )
356:         #puts "OR <<< #{rest}"
357:         if rest != path
358:           while rest =~ /^\s*( or )/
359:             n = [ :or, n, [] ]
360:             rest = AndExpr( $', n[-1] )
361:           end
362:         end
363:         if parsed.size == 0 and n.size != 0
364:           parsed.replace(n)
365:         elsif n.size > 0
366:           parsed << n
367:         end
368:         rest
369:       end

| LocationPath | FilterExpr (’/’ | ’//’) RelativeLocationPath

[Source]

     # File lib/rexml/parsers/xpathparser.rb, line 541
541:       def PathExpr path, parsed
542:         path =~ /^\s*/
543:         path = $'
544:         #puts "PATH >>> #{path}"
545:         n = []
546:         rest = FilterExpr( path, n )
547:         #puts "PATH <<< '#{rest}'"
548:         if rest != path
549:           if rest and rest[0] == ?/
550:             return RelativeLocationPath(rest, n)
551:           end
552:         end
553:         #puts "BEFORE WITH '#{rest}'"
554:         rest = LocationPath(rest, n) if rest =~ /\A[\/\.\@\[\w_*]/
555:         parsed.concat(n)
556:         return rest
557:       end

Filters the supplied nodeset on the predicate(s)

[Source]

     # File lib/rexml/parsers/xpathparser.rb, line 326
326:       def Predicate path, parsed
327:         #puts "PREDICATE with #{path}"
328:         return nil unless path[0] == ?[
329:         predicates = []
330:         while path[0] == ?[
331:           path, expr = get_group(path)
332:           predicates << expr[1..-2] if expr
333:         end
334:         #puts "PREDICATES = #{predicates.inspect}"
335:         predicates.each{ |expr| 
336:           #puts "ORING #{expr}"
337:           preds = []
338:           parsed << :predicate
339:           parsed << preds
340:           OrExpr(expr, preds) 
341:         }
342:         #puts "PREDICATES = #{predicates.inspect}"
343:         path
344:       end

[Source]

     # File lib/rexml/parsers/xpathparser.rb, line 580
580:       def PrimaryExpr path, parsed
581:         arry = []
582:         case path
583:         when VARIABLE_REFERENCE
584:           varname = $1
585:           path = $'
586:           parsed << :variable
587:           parsed << varname
588:           #arry << @variables[ varname ]
589:         when /^(\w[-\w]*)(?:\()/
590:           #puts "PrimaryExpr :: Function >>> #$1 -- '#$''"
591:           fname = $1
592:           tmp = $'
593:           #puts "#{fname} =~ #{NT.inspect}"
594:           return path if fname =~ NT
595:           path = tmp
596:           parsed << :function
597:           parsed << fname
598:           path = FunctionCall(path, parsed)
599:         when NUMBER
600:           #puts "LITERAL or NUMBER: #$1"
601:           varname = $1.nil? ? $2 : $1
602:           path = $'
603:           parsed << :literal 
604:           parsed << (varname.include?('.') ? varname.to_f : varname.to_i)
605:         when LITERAL
606:           #puts "LITERAL or NUMBER: #$1"
607:           varname = $1.nil? ? $2 : $1
608:           path = $'
609:           parsed << :literal 
610:           parsed << varname
611:         when /^\(/                                               #/
612:           path, contents = get_group(path)
613:           contents = contents[1..-2]
614:           n = []
615:           OrExpr( contents, n )
616:           parsed.concat(n)
617:         end
618:         path
619:       end

| RelationalExpr (’<’ | ’>’ | ’<=’ | ’>=’) AdditiveExpr | AdditiveExpr

[Source]

     # File lib/rexml/parsers/xpathparser.rb, line 421
421:       def RelationalExpr path, parsed
422:         #puts "RELATION >>> #{path}"
423:         n = []
424:         rest = AdditiveExpr( path, n )
425:         #puts "RELATION <<< #{rest}"
426:         if rest != path
427:           while rest =~ /^\s*([<>]=?)\s*/
428:             if $1[0] == ?<
429:               sym = "lt"
430:             else
431:               sym = "gt"
432:             end
433:             sym << "eq" if $1[-1] == ?=
434:             n = [ sym.intern, n, [] ]
435:             rest = AdditiveExpr( $', n[-1] )
436:           end
437:         end
438:         if parsed.size == 0 and n.size != 0
439:           parsed.replace(n)
440:         elsif n.size > 0
441:           parsed << n
442:         end
443:         rest
444:       end

[Source]

     # File lib/rexml/parsers/xpathparser.rb, line 211
211:       def RelativeLocationPath path, parsed
212:         #puts "RelativeLocationPath #{path}"
213:         while path.size > 0
214:           # (axis or @ or <child::>) nodetest predicate  >
215:           # OR                                          >  / Step
216:           # (. or ..)                                    >
217:           if path[0] == ?.
218:             if path[1] == ?.
219:               parsed << :parent
220:               parsed << :node
221:               path = path[2..-1]
222:             else
223:               parsed << :self
224:               parsed << :node
225:               path = path[1..-1]
226:             end
227:           else
228:             if path[0] == ?@
229:               #puts "ATTRIBUTE"
230:               parsed << :attribute
231:               path = path[1..-1]
232:               # Goto Nodetest
233:             elsif path =~ AXIS
234:               parsed << $1.tr('-','_').intern
235:               path = $'
236:               # Goto Nodetest
237:             else
238:               parsed << :child
239:             end
240: 
241:             #puts "NODETESTING '#{path}'"
242:             n = []
243:             path = NodeTest( path, n)
244:             #puts "NODETEST RETURNED '#{path}'"
245: 
246:             if path[0] == ?[
247:               path = Predicate( path, n )
248:             end
249: 
250:             parsed.concat(n)
251:           end
252:           
253:           if path.size > 0
254:             if path[0] == ?/
255:               if path[1] == ?/
256:                 parsed << :descendant_or_self
257:                 parsed << :node
258:                 path = path[2..-1]
259:               else
260:                 path = path[1..-1]
261:               end
262:             else
263:               return path
264:             end
265:           end
266:         end
267:         return path
268:       end

| ’-’ UnaryExpr | UnionExpr

[Source]

     # File lib/rexml/parsers/xpathparser.rb, line 500
500:       def UnaryExpr path, parsed
501:         path =~ /^(\-*)/
502:         path = $'
503:         if $1 and (($1.size % 2) != 0)
504:           mult = -1
505:         else
506:           mult = 1
507:         end
508:         parsed << :neg if mult < 0
509: 
510:         #puts "UNARY >>> #{path}"
511:         n = []
512:         path = UnionExpr( path, n )
513:         #puts "UNARY <<< #{path}"
514:         parsed.concat( n )
515:         path
516:       end

| UnionExpr ’|’ PathExpr | PathExpr

[Source]

     # File lib/rexml/parsers/xpathparser.rb, line 520
520:       def UnionExpr path, parsed
521:         #puts "UNION >>> #{path}"
522:         n = []
523:         rest = PathExpr( path, n )
524:         #puts "UNION <<< #{rest}"
525:         if rest != path
526:           while rest =~ /^\s*(\|)\s*/
527:             n = [ :union, n, [] ]
528:             rest = PathExpr( $', n[-1] )
529:           end
530:         end
531:         if parsed.size == 0 and n.size != 0
532:           parsed.replace( n )
533:         elsif n.size > 0
534:           parsed << n
535:         end
536:         rest
537:       end

get_group( ’[foo]bar’ ) -> [‘bar’, ’[foo]’]

[Source]

     # File lib/rexml/parsers/xpathparser.rb, line 635
635:       def get_group string
636:         ind = 0
637:         depth = 0
638:         st = string[0,1]
639:         en = (st == "(" ? ")" : "]")
640:         begin
641:           case string[ind,1]
642:           when st
643:             depth += 1
644:           when en
645:             depth -= 1
646:           end
647:           ind += 1
648:         end while depth > 0 and ind < string.length
649:         return nil unless depth==0
650:         [string[ind..-1], string[0..ind-1]]
651:       end

[Source]

     # File lib/rexml/parsers/xpathparser.rb, line 653
653:       def parse_args( string )
654:         arguments = []
655:         ind = 0
656:                                 inquot = false
657:                                 inapos = false
658:         depth = 1
659:         begin
660:           case string[ind]
661:           when ?"
662:                 inquot = !inquot unless inapos
663:           when ?'
664:                 inapos = !inapos unless inquot
665:           else
666:                 unless inquot or inapos
667:                         case string[ind]
668:                                                         when ?(
669:                                                                 depth += 1
670:                 if depth == 1
671:                         string = string[1..-1]
672:                         ind -= 1
673:                 end
674:                                                         when ?)
675:                                                                 depth -= 1
676:                                                                 if depth == 0
677:                                                                         s = string[0,ind].strip
678:                                                                         arguments << s unless s == ""
679:                                                                         string = string[ind+1..-1]
680:                                                                 end
681:                                                         when ?,
682:                                                                 if depth == 1
683:                                                                         s = string[0,ind].strip
684:                                                                         arguments << s unless s == ""
685:                                                                         string = string[ind+1..-1]
686:                                                                         ind = -1 
687:                                                                 end
688:                                                         end
689:             end
690:           end
691:           ind += 1
692:         end while depth > 0 and ind < string.length
693:         return nil unless depth==0
694:         [string,arguments]
695:       end

[Validate]