rubysl-rexml 1.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|