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.
- checksums.yaml +4 -4
- data/.travis.yml +3 -2
- data/lib/rexml/attlistdecl.rb +56 -56
- data/lib/rexml/attribute.rb +155 -149
- data/lib/rexml/cdata.rb +48 -48
- data/lib/rexml/child.rb +82 -82
- data/lib/rexml/comment.rb +59 -59
- data/lib/rexml/doctype.rb +22 -24
- data/lib/rexml/document.rb +185 -129
- data/lib/rexml/dtd/attlistdecl.rb +7 -7
- data/lib/rexml/dtd/dtd.rb +41 -41
- data/lib/rexml/dtd/elementdecl.rb +13 -13
- data/lib/rexml/dtd/entitydecl.rb +49 -49
- data/lib/rexml/dtd/notationdecl.rb +32 -32
- data/lib/rexml/element.rb +122 -107
- data/lib/rexml/encoding.rb +37 -58
- data/lib/rexml/entity.rb +144 -144
- data/lib/rexml/formatters/default.rb +6 -4
- data/lib/rexml/formatters/pretty.rb +11 -8
- data/lib/rexml/formatters/transitive.rb +4 -3
- data/lib/rexml/functions.rb +33 -21
- data/lib/rexml/instruction.rb +49 -49
- data/lib/rexml/light/node.rb +190 -191
- data/lib/rexml/namespace.rb +39 -39
- data/lib/rexml/node.rb +38 -38
- data/lib/rexml/output.rb +17 -12
- data/lib/rexml/parent.rb +26 -25
- data/lib/rexml/parseexception.rb +4 -4
- data/lib/rexml/parsers/baseparser.rb +90 -61
- data/lib/rexml/parsers/lightparser.rb +41 -43
- data/lib/rexml/parsers/pullparser.rb +1 -1
- data/lib/rexml/parsers/sax2parser.rb +233 -198
- data/lib/rexml/parsers/streamparser.rb +6 -2
- data/lib/rexml/parsers/treeparser.rb +9 -6
- data/lib/rexml/parsers/ultralightparser.rb +40 -40
- data/lib/rexml/parsers/xpathparser.rb +51 -52
- data/lib/rexml/quickpath.rb +247 -248
- data/lib/rexml/rexml.rb +9 -10
- data/lib/rexml/sax2listener.rb +92 -92
- data/lib/rexml/security.rb +27 -0
- data/lib/rexml/source.rb +95 -50
- data/lib/rexml/streamlistener.rb +90 -90
- data/lib/rexml/syncenumerator.rb +3 -4
- data/lib/rexml/text.rb +157 -76
- data/lib/rexml/validation/relaxng.rb +18 -18
- data/lib/rexml/validation/validation.rb +5 -5
- data/lib/rexml/xmldecl.rb +59 -63
- data/lib/rexml/xmltokens.rb +14 -14
- data/lib/rexml/xpath.rb +67 -53
- data/lib/rexml/xpath_parser.rb +49 -38
- data/lib/rubysl/rexml.rb +1 -0
- data/lib/rubysl/rexml/version.rb +1 -1
- data/rubysl-rexml.gemspec +3 -1
- metadata +19 -28
- data/lib/rexml/encodings/CP-1252.rb +0 -103
- data/lib/rexml/encodings/EUC-JP.rb +0 -35
- data/lib/rexml/encodings/ICONV.rb +0 -22
- data/lib/rexml/encodings/ISO-8859-1.rb +0 -7
- data/lib/rexml/encodings/ISO-8859-15.rb +0 -72
- data/lib/rexml/encodings/SHIFT-JIS.rb +0 -37
- data/lib/rexml/encodings/SHIFT_JIS.rb +0 -1
- data/lib/rexml/encodings/UNILE.rb +0 -34
- data/lib/rexml/encodings/US-ASCII.rb +0 -30
- data/lib/rexml/encodings/UTF-16.rb +0 -35
- 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
|
-
|
28
|
-
|
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]
|
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::
|
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
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
43
|
-
|
43
|
+
string << "/" if string.size > 0
|
44
|
+
string << "@"
|
44
45
|
when :child
|
45
|
-
|
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
|
-
|
55
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
-
|
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{ |
|
336
|
-
#puts "ORING #{
|
335
|
+
predicates.each{ |pred|
|
336
|
+
#puts "ORING #{pred}"
|
337
337
|
preds = []
|
338
338
|
parsed << :predicate
|
339
339
|
parsed << preds
|
340
|
-
OrExpr(
|
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[\/\.\@\[\
|
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
|
-
|
657
|
-
|
655
|
+
inquot = false
|
656
|
+
inapos = false
|
658
657
|
depth = 1
|
659
658
|
begin
|
660
659
|
case string[ind]
|
661
660
|
when ?"
|
662
|
-
|
661
|
+
inquot = !inquot unless inapos
|
663
662
|
when ?'
|
664
|
-
|
663
|
+
inapos = !inapos unless inquot
|
665
664
|
else
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
665
|
+
unless inquot or inapos
|
666
|
+
case string[ind]
|
667
|
+
when ?(
|
668
|
+
depth += 1
|
670
669
|
if depth == 1
|
671
|
-
|
672
|
-
|
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
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
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
|
data/lib/rexml/quickpath.rb
CHANGED
@@ -2,265 +2,264 @@ require 'rexml/functions'
|
|
2
2
|
require 'rexml/xmltokens'
|
3
3
|
|
4
4
|
module REXML
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
class QuickPath
|
6
|
+
include Functions
|
7
|
+
include XMLTokens
|
8
8
|
|
9
|
-
|
9
|
+
# A base Hash object to be used when initializing a
|
10
|
+
# default empty namespaces set.
|
11
|
+
EMPTY_HASH = {}
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
13
|
+
def QuickPath::first element, path, namespaces=EMPTY_HASH
|
14
|
+
match(element, path, namespaces)[0]
|
15
|
+
end
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
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
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
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
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
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
|
-
|
207
|
-
|
208
|
-
|
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
|
-
|
211
|
-
|
212
|
-
|
205
|
+
def QuickPath::attribute( name )
|
206
|
+
return Functions.node.attributes[name] if Functions.node.kind_of? Element
|
207
|
+
end
|
213
208
|
|
214
|
-
|
215
|
-
|
216
|
-
|
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
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
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
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
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
|