rubysl-rexml 1.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -2
  3. data/lib/rexml/attlistdecl.rb +56 -56
  4. data/lib/rexml/attribute.rb +155 -149
  5. data/lib/rexml/cdata.rb +48 -48
  6. data/lib/rexml/child.rb +82 -82
  7. data/lib/rexml/comment.rb +59 -59
  8. data/lib/rexml/doctype.rb +22 -24
  9. data/lib/rexml/document.rb +185 -129
  10. data/lib/rexml/dtd/attlistdecl.rb +7 -7
  11. data/lib/rexml/dtd/dtd.rb +41 -41
  12. data/lib/rexml/dtd/elementdecl.rb +13 -13
  13. data/lib/rexml/dtd/entitydecl.rb +49 -49
  14. data/lib/rexml/dtd/notationdecl.rb +32 -32
  15. data/lib/rexml/element.rb +122 -107
  16. data/lib/rexml/encoding.rb +37 -58
  17. data/lib/rexml/entity.rb +144 -144
  18. data/lib/rexml/formatters/default.rb +6 -4
  19. data/lib/rexml/formatters/pretty.rb +11 -8
  20. data/lib/rexml/formatters/transitive.rb +4 -3
  21. data/lib/rexml/functions.rb +33 -21
  22. data/lib/rexml/instruction.rb +49 -49
  23. data/lib/rexml/light/node.rb +190 -191
  24. data/lib/rexml/namespace.rb +39 -39
  25. data/lib/rexml/node.rb +38 -38
  26. data/lib/rexml/output.rb +17 -12
  27. data/lib/rexml/parent.rb +26 -25
  28. data/lib/rexml/parseexception.rb +4 -4
  29. data/lib/rexml/parsers/baseparser.rb +90 -61
  30. data/lib/rexml/parsers/lightparser.rb +41 -43
  31. data/lib/rexml/parsers/pullparser.rb +1 -1
  32. data/lib/rexml/parsers/sax2parser.rb +233 -198
  33. data/lib/rexml/parsers/streamparser.rb +6 -2
  34. data/lib/rexml/parsers/treeparser.rb +9 -6
  35. data/lib/rexml/parsers/ultralightparser.rb +40 -40
  36. data/lib/rexml/parsers/xpathparser.rb +51 -52
  37. data/lib/rexml/quickpath.rb +247 -248
  38. data/lib/rexml/rexml.rb +9 -10
  39. data/lib/rexml/sax2listener.rb +92 -92
  40. data/lib/rexml/security.rb +27 -0
  41. data/lib/rexml/source.rb +95 -50
  42. data/lib/rexml/streamlistener.rb +90 -90
  43. data/lib/rexml/syncenumerator.rb +3 -4
  44. data/lib/rexml/text.rb +157 -76
  45. data/lib/rexml/validation/relaxng.rb +18 -18
  46. data/lib/rexml/validation/validation.rb +5 -5
  47. data/lib/rexml/xmldecl.rb +59 -63
  48. data/lib/rexml/xmltokens.rb +14 -14
  49. data/lib/rexml/xpath.rb +67 -53
  50. data/lib/rexml/xpath_parser.rb +49 -38
  51. data/lib/rubysl/rexml.rb +1 -0
  52. data/lib/rubysl/rexml/version.rb +1 -1
  53. data/rubysl-rexml.gemspec +3 -1
  54. metadata +19 -28
  55. data/lib/rexml/encodings/CP-1252.rb +0 -103
  56. data/lib/rexml/encodings/EUC-JP.rb +0 -35
  57. data/lib/rexml/encodings/ICONV.rb +0 -22
  58. data/lib/rexml/encodings/ISO-8859-1.rb +0 -7
  59. data/lib/rexml/encodings/ISO-8859-15.rb +0 -72
  60. data/lib/rexml/encodings/SHIFT-JIS.rb +0 -37
  61. data/lib/rexml/encodings/SHIFT_JIS.rb +0 -1
  62. data/lib/rexml/encodings/UNILE.rb +0 -34
  63. data/lib/rexml/encodings/US-ASCII.rb +0 -30
  64. data/lib/rexml/encodings/UTF-16.rb +0 -35
  65. data/lib/rexml/encodings/UTF-8.rb +0 -18
@@ -5,11 +5,11 @@ module REXML
5
5
  @listener = listener
6
6
  @parser = BaseParser.new( source )
7
7
  end
8
-
8
+
9
9
  def add_listener( listener )
10
10
  @parser.add_listener( listener )
11
11
  end
12
-
12
+
13
13
  def parse
14
14
  # entity string
15
15
  while true
@@ -38,6 +38,10 @@ module REXML
38
38
  @listener.send( event[0].to_s, *event[1..-1] )
39
39
  when :entitydecl, :notationdecl
40
40
  @listener.send( event[0].to_s, event[1..-1] )
41
+ when :externalentity
42
+ entity_reference = event[1]
43
+ content = entity_reference.gsub(/\A%|;\z/, "")
44
+ @listener.entity(content)
41
45
  end
42
46
  end
43
47
  end
@@ -24,13 +24,16 @@ module REXML
24
24
  case event[0]
25
25
  when :end_document
26
26
  unless tag_stack.empty?
27
- #raise ParseException.new("No close tag for #{tag_stack.inspect}")
28
- raise ParseException.new("No close tag for #{@build_context.xpath}")
27
+ raise ParseException.new("No close tag for #{@build_context.xpath}",
28
+ @parser.source, @parser)
29
29
  end
30
30
  return
31
31
  when :start_element
32
32
  tag_stack.push(event[1])
33
- el = @build_context = @build_context.add_element( event[1], event[2] )
33
+ el = @build_context = @build_context.add_element( event[1] )
34
+ event[2].each do |key, value|
35
+ el.attributes[key]=Attribute.new(key,value,self)
36
+ end
34
37
  when :end_element
35
38
  tag_stack.pop
36
39
  @build_context = @build_context.parent
@@ -39,8 +42,8 @@ module REXML
39
42
  if @build_context[-1].instance_of? Text
40
43
  @build_context[-1] << event[1]
41
44
  else
42
- @build_context.add(
43
- Text.new(event[1], @build_context.whitespace, nil, true)
45
+ @build_context.add(
46
+ Text.new(event[1], @build_context.whitespace, nil, true)
44
47
  ) unless (
45
48
  @build_context.ignore_whitespace_nodes and
46
49
  event[1].strip.size==0
@@ -86,7 +89,7 @@ module REXML
86
89
  end
87
90
  rescue REXML::Validation::ValidationException
88
91
  raise
89
- rescue REXML::UndefinedNamespaceException
92
+ rescue REXML::ParseException
90
93
  raise
91
94
  rescue
92
95
  raise ParseException.new( $!.message, @parser.source, @parser, $! )
@@ -2,12 +2,12 @@ require 'rexml/parsers/streamparser'
2
2
  require 'rexml/parsers/baseparser'
3
3
 
4
4
  module REXML
5
- module Parsers
6
- class UltraLightParser
7
- def initialize stream
8
- @stream = stream
9
- @parser = REXML::Parsers::BaseParser.new( stream )
10
- end
5
+ module Parsers
6
+ class UltraLightParser
7
+ def initialize stream
8
+ @stream = stream
9
+ @parser = REXML::Parsers::BaseParser.new( stream )
10
+ end
11
11
 
12
12
  def add_listener( listener )
13
13
  @parser.add_listener( listener )
@@ -18,39 +18,39 @@ module REXML
18
18
  @parser.stream = @stream
19
19
  end
20
20
 
21
- def parse
22
- root = context = []
23
- while true
24
- event = @parser.pull
25
- case event[0]
26
- when :end_document
27
- break
28
- when :end_doctype
29
- context = context[1]
30
- when :start_element, :doctype
31
- context << event
32
- event[1,0] = [context]
33
- context = event
34
- when :end_element
35
- context = context[1]
36
- else
37
- context << event
38
- end
39
- end
40
- root
41
- end
42
- end
21
+ def parse
22
+ root = context = []
23
+ while true
24
+ event = @parser.pull
25
+ case event[0]
26
+ when :end_document
27
+ break
28
+ when :end_doctype
29
+ context = context[1]
30
+ when :start_element, :doctype
31
+ context << event
32
+ event[1,0] = [context]
33
+ context = event
34
+ when :end_element
35
+ context = context[1]
36
+ else
37
+ context << event
38
+ end
39
+ end
40
+ root
41
+ end
42
+ end
43
43
 
44
- # An element is an array. The array contains:
45
- # 0 The parent element
46
- # 1 The tag name
47
- # 2 A hash of attributes
48
- # 3..-1 The child elements
49
- # An element is an array of size > 3
50
- # Text is a String
51
- # PIs are [ :processing_instruction, target, data ]
52
- # Comments are [ :comment, data ]
53
- # DocTypes are DocType structs
54
- # The root is an array with XMLDecls, Text, DocType, Array, Text
55
- end
44
+ # An element is an array. The array contains:
45
+ # 0 The parent element
46
+ # 1 The tag name
47
+ # 2 A hash of attributes
48
+ # 3..-1 The child elements
49
+ # An element is an array of size > 3
50
+ # Text is a String
51
+ # PIs are [ :processing_instruction, target, data ]
52
+ # Comments are [ :comment, data ]
53
+ # DocTypes are DocType structs
54
+ # The root is an array with XMLDecls, Text, DocType, Array, Text
55
+ end
56
56
  end
@@ -17,8 +17,9 @@ module REXML
17
17
  end
18
18
 
19
19
  def parse path
20
+ path = path.dup
20
21
  path.gsub!(/([\(\[])\s+/, '\1') # Strip ignorable spaces
21
- path.gsub!( /\s+([\]\)])/, '\1' )
22
+ path.gsub!( /\s+([\]\)])/, '\1')
22
23
  parsed = []
23
24
  path = OrExpr(path, parsed)
24
25
  parsed
@@ -39,10 +40,10 @@ module REXML
39
40
  case op
40
41
  when :node
41
42
  when :attribute
42
- string << "/" if string.size > 0
43
- string << "@"
43
+ string << "/" if string.size > 0
44
+ string << "@"
44
45
  when :child
45
- string << "/" if string.size > 0
46
+ string << "/" if string.size > 0
46
47
  when :descendant_or_self
47
48
  string << "/"
48
49
  when :self
@@ -51,10 +52,10 @@ module REXML
51
52
  string << ".."
52
53
  when :any
53
54
  string << "*"
54
- when :text
55
- string << "text()"
56
- when :following, :following_sibling,
57
- :ancestor, :ancestor_or_self, :descendant,
55
+ when :text
56
+ string << "text()"
57
+ when :following, :following_sibling,
58
+ :ancestor, :ancestor_or_self, :descendant,
58
59
  :namespace, :preceding, :preceding_sibling
59
60
  string << "/" unless string.size == 0
60
61
  string << op.to_s.tr("_", "-")
@@ -70,13 +71,13 @@ module REXML
70
71
  string << ']'
71
72
  when :document
72
73
  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}" }
74
+ when :function
75
+ string << path.shift
76
+ string << "( "
77
+ string << predicate_to_string( path.shift[0] ) {|x| abbreviate( x )}
78
+ string << " )"
79
+ when :literal
80
+ string << %Q{ "#{path.shift}" }
80
81
  else
81
82
  string << "/" unless string.size == 0
82
83
  string << "UNKNOWN("
@@ -84,7 +85,7 @@ module REXML
84
85
  string << ")"
85
86
  end
86
87
  end
87
- string = "/"+string if document
88
+ string = "/"+string if document
88
89
  return string
89
90
  end
90
91
 
@@ -97,7 +98,7 @@ module REXML
97
98
  case op
98
99
  when :node
99
100
  string << "node()"
100
- when :attribute, :child, :following, :following_sibling,
101
+ when :attribute, :child, :following, :following_sibling,
101
102
  :ancestor, :ancestor_or_self, :descendant, :descendant_or_self,
102
103
  :namespace, :preceding, :preceding_sibling, :self, :parent
103
104
  string << "/" unless string.size == 0
@@ -249,7 +250,7 @@ module REXML
249
250
 
250
251
  parsed.concat(n)
251
252
  end
252
-
253
+
253
254
  if path.size > 0
254
255
  if path[0] == ?/
255
256
  if path[1] == ?/
@@ -282,7 +283,6 @@ module REXML
282
283
  PI = /^processing-instruction\(/
283
284
  def NodeTest path, parsed
284
285
  #puts "NodeTest with #{path}"
285
- res = nil
286
286
  case path
287
287
  when /^\*/
288
288
  path = $'
@@ -332,12 +332,12 @@ module REXML
332
332
  predicates << expr[1..-2] if expr
333
333
  end
334
334
  #puts "PREDICATES = #{predicates.inspect}"
335
- predicates.each{ |expr|
336
- #puts "ORING #{expr}"
335
+ predicates.each{ |pred|
336
+ #puts "ORING #{pred}"
337
337
  preds = []
338
338
  parsed << :predicate
339
339
  parsed << preds
340
- OrExpr(expr, preds)
340
+ OrExpr(pred, preds)
341
341
  }
342
342
  #puts "PREDICATES = #{predicates.inspect}"
343
343
  path
@@ -551,7 +551,7 @@ module REXML
551
551
  end
552
552
  end
553
553
  #puts "BEFORE WITH '#{rest}'"
554
- rest = LocationPath(rest, n) if rest =~ /\A[\/\.\@\[\w_*]/
554
+ rest = LocationPath(rest, n) if rest =~ /\A[\/\.\@\[\w*]/
555
555
  parsed.concat(n)
556
556
  return rest
557
557
  end
@@ -578,7 +578,6 @@ module REXML
578
578
  NUMBER = /^(\d*\.?\d+)/
579
579
  NT = /^comment|text|processing-instruction|node$/
580
580
  def PrimaryExpr path, parsed
581
- arry = []
582
581
  case path
583
582
  when VARIABLE_REFERENCE
584
583
  varname = $1
@@ -600,13 +599,13 @@ module REXML
600
599
  #puts "LITERAL or NUMBER: #$1"
601
600
  varname = $1.nil? ? $2 : $1
602
601
  path = $'
603
- parsed << :literal
602
+ parsed << :literal
604
603
  parsed << (varname.include?('.') ? varname.to_f : varname.to_i)
605
604
  when LITERAL
606
605
  #puts "LITERAL or NUMBER: #$1"
607
606
  varname = $1.nil? ? $2 : $1
608
607
  path = $'
609
- parsed << :literal
608
+ parsed << :literal
610
609
  parsed << varname
611
610
  when /^\(/ #/
612
611
  path, contents = get_group(path)
@@ -649,43 +648,43 @@ module REXML
649
648
  return nil unless depth==0
650
649
  [string[ind..-1], string[0..ind-1]]
651
650
  end
652
-
651
+
653
652
  def parse_args( string )
654
653
  arguments = []
655
654
  ind = 0
656
- inquot = false
657
- inapos = false
655
+ inquot = false
656
+ inapos = false
658
657
  depth = 1
659
658
  begin
660
659
  case string[ind]
661
660
  when ?"
662
- inquot = !inquot unless inapos
661
+ inquot = !inquot unless inapos
663
662
  when ?'
664
- inapos = !inapos unless inquot
663
+ inapos = !inapos unless inquot
665
664
  else
666
- unless inquot or inapos
667
- case string[ind]
668
- when ?(
669
- depth += 1
665
+ unless inquot or inapos
666
+ case string[ind]
667
+ when ?(
668
+ depth += 1
670
669
  if depth == 1
671
- string = string[1..-1]
672
- ind -= 1
670
+ string = string[1..-1]
671
+ ind -= 1
672
+ end
673
+ when ?)
674
+ depth -= 1
675
+ if depth == 0
676
+ s = string[0,ind].strip
677
+ arguments << s unless s == ""
678
+ string = string[ind+1..-1]
673
679
  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
680
+ when ?,
681
+ if depth == 1
682
+ s = string[0,ind].strip
683
+ arguments << s unless s == ""
684
+ string = string[ind+1..-1]
685
+ ind = -1
686
+ end
687
+ end
689
688
  end
690
689
  end
691
690
  ind += 1
@@ -2,265 +2,264 @@ require 'rexml/functions'
2
2
  require 'rexml/xmltokens'
3
3
 
4
4
  module REXML
5
- class QuickPath
6
- include Functions
7
- include XMLTokens
5
+ class QuickPath
6
+ include Functions
7
+ include XMLTokens
8
8
 
9
- EMPTY_HASH = {}
9
+ # A base Hash object to be used when initializing a
10
+ # default empty namespaces set.
11
+ EMPTY_HASH = {}
10
12
 
11
- def QuickPath::first element, path, namespaces=EMPTY_HASH
12
- match(element, path, namespaces)[0]
13
- end
13
+ def QuickPath::first element, path, namespaces=EMPTY_HASH
14
+ match(element, path, namespaces)[0]
15
+ end
14
16
 
15
- def QuickPath::each element, path, namespaces=EMPTY_HASH, &block
16
- path = "*" unless path
17
- match(element, path, namespaces).each( &block )
18
- end
17
+ def QuickPath::each element, path, namespaces=EMPTY_HASH, &block
18
+ path = "*" unless path
19
+ match(element, path, namespaces).each( &block )
20
+ end
19
21
 
20
- def QuickPath::match element, path, namespaces=EMPTY_HASH
21
- raise "nil is not a valid xpath" unless path
22
- results = nil
23
- Functions::namespace_context = namespaces
24
- case path
25
- when /^\/([^\/]|$)/u
26
- # match on root
27
- path = path[1..-1]
28
- return [element.root.parent] if path == ''
29
- results = filter([element.root], path)
30
- when /^[-\w]*::/u
31
- results = filter([element], path)
32
- when /^\*/u
33
- results = filter(element.to_a, path)
34
- when /^[\[!\w:]/u
35
- # match on child
36
- matches = []
37
- children = element.to_a
38
- results = filter(children, path)
39
- else
40
- results = filter([element], path)
41
- end
42
- return results
43
- end
22
+ def QuickPath::match element, path, namespaces=EMPTY_HASH
23
+ raise "nil is not a valid xpath" unless path
24
+ results = nil
25
+ Functions::namespace_context = namespaces
26
+ case path
27
+ when /^\/([^\/]|$)/u
28
+ # match on root
29
+ path = path[1..-1]
30
+ return [element.root.parent] if path == ''
31
+ results = filter([element.root], path)
32
+ when /^[-\w]*::/u
33
+ results = filter([element], path)
34
+ when /^\*/u
35
+ results = filter(element.to_a, path)
36
+ when /^[\[!\w:]/u
37
+ # match on child
38
+ children = element.to_a
39
+ results = filter(children, path)
40
+ else
41
+ results = filter([element], path)
42
+ end
43
+ return results
44
+ end
44
45
 
45
- # Given an array of nodes it filters the array based on the path. The
46
- # result is that when this method returns, the array will contain elements
47
- # which match the path
48
- def QuickPath::filter elements, path
49
- return elements if path.nil? or path == '' or elements.size == 0
50
- case path
51
- when /^\/\//u # Descendant
52
- return axe( elements, "descendant-or-self", $' )
53
- when /^\/?\b(\w[-\w]*)\b::/u # Axe
54
- axe_name = $1
55
- rest = $'
56
- return axe( elements, $1, $' )
57
- when /^\/(?=\b([:!\w][-\.\w]*:)?[-!\*\.\w]*\b([^:(]|$)|\*)/u # Child
58
- rest = $'
59
- results = []
60
- elements.each do |element|
61
- results |= filter( element.to_a, rest )
62
- end
63
- return results
64
- when /^\/?(\w[-\w]*)\(/u # / Function
65
- return function( elements, $1, $' )
66
- when Namespace::NAMESPLIT # Element name
67
- name = $2
68
- ns = $1
69
- rest = $'
70
- elements.delete_if do |element|
71
- !(element.kind_of? Element and
72
- (element.expanded_name == name or
73
- (element.name == name and
74
- element.namespace == Functions.namespace_context[ns])))
75
- end
76
- return filter( elements, rest )
77
- when /^\/\[/u
78
- matches = []
79
- elements.each do |element|
80
- matches |= predicate( element.to_a, path[1..-1] ) if element.kind_of? Element
81
- end
82
- return matches
83
- when /^\[/u # Predicate
84
- return predicate( elements, path )
85
- when /^\/?\.\.\./u # Ancestor
86
- return axe( elements, "ancestor", $' )
87
- when /^\/?\.\./u # Parent
88
- return filter( elements.collect{|e|e.parent}, $' )
89
- when /^\/?\./u # Self
90
- return filter( elements, $' )
91
- when /^\*/u # Any
92
- results = []
93
- elements.each do |element|
94
- results |= filter( [element], $' ) if element.kind_of? Element
95
- #if element.kind_of? Element
96
- # children = element.to_a
97
- # children.delete_if { |child| !child.kind_of?(Element) }
98
- # results |= filter( children, $' )
99
- #end
100
- end
101
- return results
102
- end
103
- return []
104
- end
46
+ # Given an array of nodes it filters the array based on the path. The
47
+ # result is that when this method returns, the array will contain elements
48
+ # which match the path
49
+ def QuickPath::filter elements, path
50
+ return elements if path.nil? or path == '' or elements.size == 0
51
+ case path
52
+ when /^\/\//u # Descendant
53
+ return axe( elements, "descendant-or-self", $' )
54
+ when /^\/?\b(\w[-\w]*)\b::/u # Axe
55
+ return axe( elements, $1, $' )
56
+ when /^\/(?=\b([:!\w][-\.\w]*:)?[-!\*\.\w]*\b([^:(]|$)|\*)/u # Child
57
+ rest = $'
58
+ results = []
59
+ elements.each do |element|
60
+ results |= filter( element.to_a, rest )
61
+ end
62
+ return results
63
+ when /^\/?(\w[-\w]*)\(/u # / Function
64
+ return function( elements, $1, $' )
65
+ when Namespace::NAMESPLIT # Element name
66
+ name = $2
67
+ ns = $1
68
+ rest = $'
69
+ elements.delete_if do |element|
70
+ !(element.kind_of? Element and
71
+ (element.expanded_name == name or
72
+ (element.name == name and
73
+ element.namespace == Functions.namespace_context[ns])))
74
+ end
75
+ return filter( elements, rest )
76
+ when /^\/\[/u
77
+ matches = []
78
+ elements.each do |element|
79
+ matches |= predicate( element.to_a, path[1..-1] ) if element.kind_of? Element
80
+ end
81
+ return matches
82
+ when /^\[/u # Predicate
83
+ return predicate( elements, path )
84
+ when /^\/?\.\.\./u # Ancestor
85
+ return axe( elements, "ancestor", $' )
86
+ when /^\/?\.\./u # Parent
87
+ return filter( elements.collect{|e|e.parent}, $' )
88
+ when /^\/?\./u # Self
89
+ return filter( elements, $' )
90
+ when /^\*/u # Any
91
+ results = []
92
+ elements.each do |element|
93
+ results |= filter( [element], $' ) if element.kind_of? Element
94
+ #if element.kind_of? Element
95
+ # children = element.to_a
96
+ # children.delete_if { |child| !child.kind_of?(Element) }
97
+ # results |= filter( children, $' )
98
+ #end
99
+ end
100
+ return results
101
+ end
102
+ return []
103
+ end
105
104
 
106
- def QuickPath::axe( elements, axe_name, rest )
107
- matches = []
108
- matches = filter( elements.dup, rest ) if axe_name =~ /-or-self$/u
109
- case axe_name
110
- when /^descendant/u
111
- elements.each do |element|
112
- matches |= filter( element.to_a, "descendant-or-self::#{rest}" ) if element.kind_of? Element
113
- end
114
- when /^ancestor/u
115
- elements.each do |element|
116
- while element.parent
117
- matches << element.parent
118
- element = element.parent
119
- end
120
- end
121
- matches = filter( matches, rest )
122
- when "self"
123
- matches = filter( elements, rest )
124
- when "child"
125
- elements.each do |element|
126
- matches |= filter( element.to_a, rest ) if element.kind_of? Element
127
- end
128
- when "attribute"
129
- elements.each do |element|
130
- matches << element.attributes[ rest ] if element.kind_of? Element
131
- end
132
- when "parent"
133
- matches = filter(elements.collect{|element| element.parent}.uniq, rest)
134
- when "following-sibling"
135
- matches = filter(elements.collect{|element| element.next_sibling}.uniq,
136
- rest)
137
- when "previous-sibling"
138
- matches = filter(elements.collect{|element|
139
- element.previous_sibling}.uniq, rest )
140
- end
141
- return matches.uniq
142
- end
105
+ def QuickPath::axe( elements, axe_name, rest )
106
+ matches = []
107
+ matches = filter( elements.dup, rest ) if axe_name =~ /-or-self$/u
108
+ case axe_name
109
+ when /^descendant/u
110
+ elements.each do |element|
111
+ matches |= filter( element.to_a, "descendant-or-self::#{rest}" ) if element.kind_of? Element
112
+ end
113
+ when /^ancestor/u
114
+ elements.each do |element|
115
+ while element.parent
116
+ matches << element.parent
117
+ element = element.parent
118
+ end
119
+ end
120
+ matches = filter( matches, rest )
121
+ when "self"
122
+ matches = filter( elements, rest )
123
+ when "child"
124
+ elements.each do |element|
125
+ matches |= filter( element.to_a, rest ) if element.kind_of? Element
126
+ end
127
+ when "attribute"
128
+ elements.each do |element|
129
+ matches << element.attributes[ rest ] if element.kind_of? Element
130
+ end
131
+ when "parent"
132
+ matches = filter(elements.collect{|element| element.parent}.uniq, rest)
133
+ when "following-sibling"
134
+ matches = filter(elements.collect{|element| element.next_sibling}.uniq,
135
+ rest)
136
+ when "previous-sibling"
137
+ matches = filter(elements.collect{|element|
138
+ element.previous_sibling}.uniq, rest )
139
+ end
140
+ return matches.uniq
141
+ end
143
142
 
144
- # A predicate filters a node-set with respect to an axis to produce a
145
- # new node-set. For each node in the node-set to be filtered, the
146
- # PredicateExpr is evaluated with that node as the context node, with
147
- # the number of nodes in the node-set as the context size, and with the
148
- # proximity position of the node in the node-set with respect to the
149
- # axis as the context position; if PredicateExpr evaluates to true for
150
- # that node, the node is included in the new node-set; otherwise, it is
151
- # not included.
152
- #
153
- # A PredicateExpr is evaluated by evaluating the Expr and converting
154
- # the result to a boolean. If the result is a number, the result will
155
- # be converted to true if the number is equal to the context position
156
- # and will be converted to false otherwise; if the result is not a
157
- # number, then the result will be converted as if by a call to the
158
- # boolean function. Thus a location path para[3] is equivalent to
159
- # para[position()=3].
160
- def QuickPath::predicate( elements, path )
161
- ind = 1
162
- bcount = 1
163
- while bcount > 0
164
- bcount += 1 if path[ind] == ?[
165
- bcount -= 1 if path[ind] == ?]
166
- ind += 1
167
- end
168
- ind -= 1
169
- predicate = path[1..ind-1]
170
- rest = path[ind+1..-1]
143
+ OPERAND_ = '((?=(?:(?!and|or).)*[^\s<>=])[^\s<>=]+)'
144
+ # A predicate filters a node-set with respect to an axis to produce a
145
+ # new node-set. For each node in the node-set to be filtered, the
146
+ # PredicateExpr is evaluated with that node as the context node, with
147
+ # the number of nodes in the node-set as the context size, and with the
148
+ # proximity position of the node in the node-set with respect to the
149
+ # axis as the context position; if PredicateExpr evaluates to true for
150
+ # that node, the node is included in the new node-set; otherwise, it is
151
+ # not included.
152
+ #
153
+ # A PredicateExpr is evaluated by evaluating the Expr and converting
154
+ # the result to a boolean. If the result is a number, the result will
155
+ # be converted to true if the number is equal to the context position
156
+ # and will be converted to false otherwise; if the result is not a
157
+ # number, then the result will be converted as if by a call to the
158
+ # boolean function. Thus a location path para[3] is equivalent to
159
+ # para[position()=3].
160
+ def QuickPath::predicate( elements, path )
161
+ ind = 1
162
+ bcount = 1
163
+ while bcount > 0
164
+ bcount += 1 if path[ind] == ?[
165
+ bcount -= 1 if path[ind] == ?]
166
+ ind += 1
167
+ end
168
+ ind -= 1
169
+ predicate = path[1..ind-1]
170
+ rest = path[ind+1..-1]
171
171
 
172
- # have to change 'a [=<>] b [=<>] c' into 'a [=<>] b and b [=<>] c'
173
- predicate.gsub!( /([^\s(and)(or)<>=]+)\s*([<>=])\s*([^\s(and)(or)<>=]+)\s*([<>=])\s*([^\s(and)(or)<>=]+)/u ) {
174
- "#$1 #$2 #$3 and #$3 #$4 #$5"
175
- }
176
- # Let's do some Ruby trickery to avoid some work:
177
- predicate.gsub!( /&/u, "&&" )
178
- predicate.gsub!( /=/u, "==" )
179
- predicate.gsub!( /@(\w[-\w.]*)/u ) {
180
- "attribute(\"#$1\")"
181
- }
182
- predicate.gsub!( /\bmod\b/u, "%" )
183
- predicate.gsub!( /\b(\w[-\w.]*\()/u ) {
184
- fname = $1
185
- fname.gsub( /-/u, "_" )
186
- }
187
-
188
- Functions.pair = [ 0, elements.size ]
189
- results = []
190
- elements.each do |element|
191
- Functions.pair[0] += 1
192
- Functions.node = element
193
- res = eval( predicate )
194
- case res
195
- when true
196
- results << element
197
- when Fixnum
198
- results << element if Functions.pair[0] == res
199
- when String
200
- results << element
201
- end
202
- end
203
- return filter( results, rest )
204
- end
172
+ # have to change 'a [=<>] b [=<>] c' into 'a [=<>] b and b [=<>] c'
173
+ #
174
+ predicate.gsub!(
175
+ /#{OPERAND_}\s*([<>=])\s*#{OPERAND_}\s*([<>=])\s*#{OPERAND_}/u,
176
+ '\1 \2 \3 and \3 \4 \5' )
177
+ # Let's do some Ruby trickery to avoid some work:
178
+ predicate.gsub!( /&/u, "&&" )
179
+ predicate.gsub!( /=/u, "==" )
180
+ predicate.gsub!( /@(\w[-\w.]*)/u, 'attribute("\1")' )
181
+ predicate.gsub!( /\bmod\b/u, "%" )
182
+ predicate.gsub!( /\b(\w[-\w.]*\()/u ) {
183
+ fname = $1
184
+ fname.gsub( /-/u, "_" )
185
+ }
205
186
 
206
- def QuickPath::attribute( name )
207
- return Functions.node.attributes[name] if Functions.node.kind_of? Element
208
- end
187
+ Functions.pair = [ 0, elements.size ]
188
+ results = []
189
+ elements.each do |element|
190
+ Functions.pair[0] += 1
191
+ Functions.node = element
192
+ res = eval( predicate )
193
+ case res
194
+ when true
195
+ results << element
196
+ when Fixnum
197
+ results << element if Functions.pair[0] == res
198
+ when String
199
+ results << element
200
+ end
201
+ end
202
+ return filter( results, rest )
203
+ end
209
204
 
210
- def QuickPath::name()
211
- return Functions.node.name if Functions.node.kind_of? Element
212
- end
205
+ def QuickPath::attribute( name )
206
+ return Functions.node.attributes[name] if Functions.node.kind_of? Element
207
+ end
213
208
 
214
- def QuickPath::method_missing( id, *args )
215
- begin
216
- Functions.send( id.id2name, *args )
217
- rescue Exception
218
- raise "METHOD: #{id.id2name}(#{args.join ', '})\n#{$!.message}"
219
- end
220
- end
209
+ def QuickPath::name()
210
+ return Functions.node.name if Functions.node.kind_of? Element
211
+ end
221
212
 
222
- def QuickPath::function( elements, fname, rest )
223
- args = parse_args( elements, rest )
224
- Functions.pair = [0, elements.size]
225
- results = []
226
- elements.each do |element|
227
- Functions.pair[0] += 1
228
- Functions.node = element
229
- res = Functions.send( fname, *args )
230
- case res
231
- when true
232
- results << element
233
- when Fixnum
234
- results << element if Functions.pair[0] == res
235
- end
236
- end
237
- return results
238
- end
213
+ def QuickPath::method_missing( id, *args )
214
+ begin
215
+ Functions.send( id.id2name, *args )
216
+ rescue Exception
217
+ raise "METHOD: #{id.id2name}(#{args.join ', '})\n#{$!.message}"
218
+ end
219
+ end
239
220
 
240
- def QuickPath::parse_args( element, string )
241
- # /.*?(?:\)|,)/
242
- arguments = []
243
- buffer = ""
244
- while string and string != ""
245
- c = string[0]
246
- string.sub!(/^./u, "")
247
- case c
248
- when ?,
249
- # if depth = 1, then we start a new argument
250
- arguments << evaluate( buffer )
251
- #arguments << evaluate( string[0..count] )
252
- when ?(
253
- # start a new method call
254
- function( element, buffer, string )
255
- buffer = ""
256
- when ?)
257
- # close the method call and return arguments
258
- return arguments
259
- else
260
- buffer << c
261
- end
262
- end
263
- ""
264
- end
265
- end
221
+ def QuickPath::function( elements, fname, rest )
222
+ args = parse_args( elements, rest )
223
+ Functions.pair = [0, elements.size]
224
+ results = []
225
+ elements.each do |element|
226
+ Functions.pair[0] += 1
227
+ Functions.node = element
228
+ res = Functions.send( fname, *args )
229
+ case res
230
+ when true
231
+ results << element
232
+ when Fixnum
233
+ results << element if Functions.pair[0] == res
234
+ end
235
+ end
236
+ return results
237
+ end
238
+
239
+ def QuickPath::parse_args( element, string )
240
+ # /.*?(?:\)|,)/
241
+ arguments = []
242
+ buffer = ""
243
+ while string and string != ""
244
+ c = string[0]
245
+ string.sub!(/^./u, "")
246
+ case c
247
+ when ?,
248
+ # if depth = 1, then we start a new argument
249
+ arguments << evaluate( buffer )
250
+ #arguments << evaluate( string[0..count] )
251
+ when ?(
252
+ # start a new method call
253
+ function( element, buffer, string )
254
+ buffer = ""
255
+ when ?)
256
+ # close the method call and return arguments
257
+ return arguments
258
+ else
259
+ buffer << c
260
+ end
261
+ end
262
+ ""
263
+ end
264
+ end
266
265
  end