xmpp4r 0.3
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.
- data/COPYING +340 -0
- data/ChangeLog +28 -0
- data/LICENSE +59 -0
- data/README +20 -0
- data/Rakefile +103 -0
- data/UPDATING +40 -0
- data/data/doc/xmpp4r/examples/advanced/adventure/README +57 -0
- data/data/doc/xmpp4r/examples/advanced/adventure/adventure.rb +23 -0
- data/data/doc/xmpp4r/examples/advanced/adventure/adventuremuc.rb +136 -0
- data/data/doc/xmpp4r/examples/advanced/adventure/cube.xml +15 -0
- data/data/doc/xmpp4r/examples/advanced/adventure/tower.xml +69 -0
- data/data/doc/xmpp4r/examples/advanced/adventure/world.rb +425 -0
- data/data/doc/xmpp4r/examples/advanced/fileserve.conf +11 -0
- data/data/doc/xmpp4r/examples/advanced/fileserve.rb +344 -0
- data/data/doc/xmpp4r/examples/advanced/getonline.rb +56 -0
- data/data/doc/xmpp4r/examples/advanced/gtkmucclient.rb +315 -0
- data/data/doc/xmpp4r/examples/advanced/migrate.rb +89 -0
- data/data/doc/xmpp4r/examples/advanced/minimuc.rb +266 -0
- data/data/doc/xmpp4r/examples/advanced/recvfile.rb +83 -0
- data/data/doc/xmpp4r/examples/advanced/rosterdiscovery.rb +130 -0
- data/data/doc/xmpp4r/examples/advanced/sendfile.conf +10 -0
- data/data/doc/xmpp4r/examples/advanced/sendfile.rb +72 -0
- data/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr.rb +51 -0
- data/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr_jabber.rb +43 -0
- data/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr_test.rb +10 -0
- data/data/doc/xmpp4r/examples/advanced/versionpoll.rb +90 -0
- data/data/doc/xmpp4r/examples/advanced/xmpping.rb +134 -0
- data/data/doc/xmpp4r/examples/advanced/xmppingrc.sample +9 -0
- data/data/doc/xmpp4r/examples/basic/change_password.rb +41 -0
- data/data/doc/xmpp4r/examples/basic/client.rb +68 -0
- data/data/doc/xmpp4r/examples/basic/component.rb +11 -0
- data/data/doc/xmpp4r/examples/basic/echo_nonthreaded.rb +32 -0
- data/data/doc/xmpp4r/examples/basic/echo_threaded.rb +32 -0
- data/data/doc/xmpp4r/examples/basic/jabbersend.rb +41 -0
- data/data/doc/xmpp4r/examples/basic/mass_sender.rb +67 -0
- data/data/doc/xmpp4r/examples/basic/mucinfo.rb +39 -0
- data/data/doc/xmpp4r/examples/basic/mucsimplebot.rb +83 -0
- data/data/doc/xmpp4r/examples/basic/register.rb +25 -0
- data/data/doc/xmpp4r/examples/basic/remove_registration.rb +18 -0
- data/data/doc/xmpp4r/examples/basic/roster.rb +42 -0
- data/data/doc/xmpp4r/examples/basic/rosterprint.rb +50 -0
- data/data/doc/xmpp4r/examples/basic/rosterrename.rb +34 -0
- data/data/doc/xmpp4r/examples/basic/rosterwatch.rb +172 -0
- data/data/doc/xmpp4r/examples/basic/send_vcard.rb +68 -0
- data/data/doc/xmpp4r/examples/basic/versionbot.rb +75 -0
- data/data/doc/xmpp4r/examples/buggy/jabber2jabber/jabber2jabber.rb +18 -0
- data/data/doc/xmpp4r/examples/buggy/miniedgarr_cgi.rb +192 -0
- data/data/doc/xmpp4r/examples/buggy/miniedgarr_watch.rb +82 -0
- data/lib/callbacks.rb +122 -0
- data/lib/xmpp4r/authenticationfailure.rb +13 -0
- data/lib/xmpp4r/bytestreams/helper/filetransfer.rb +315 -0
- data/lib/xmpp4r/bytestreams/helper/ibb/base.rb +256 -0
- data/lib/xmpp4r/bytestreams/helper/ibb/initiator.rb +30 -0
- data/lib/xmpp4r/bytestreams/helper/ibb/target.rb +46 -0
- data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/base.rb +151 -0
- data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/initiator.rb +85 -0
- data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/server.rb +178 -0
- data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/socks5.rb +56 -0
- data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb +61 -0
- data/lib/xmpp4r/bytestreams/iq/bytestreams.rb +177 -0
- data/lib/xmpp4r/bytestreams/iq/si.rb +221 -0
- data/lib/xmpp4r/bytestreams.rb +11 -0
- data/lib/xmpp4r/client.rb +249 -0
- data/lib/xmpp4r/component.rb +103 -0
- data/lib/xmpp4r/connection.rb +166 -0
- data/lib/xmpp4r/dataforms/x/data.rb +248 -0
- data/lib/xmpp4r/dataforms.rb +1 -0
- data/lib/xmpp4r/debuglog.rb +34 -0
- data/lib/xmpp4r/delay/x/delay.rb +100 -0
- data/lib/xmpp4r/delay.rb +1 -0
- data/lib/xmpp4r/discovery/iq/discoinfo.rb +225 -0
- data/lib/xmpp4r/discovery/iq/discoitems.rb +162 -0
- data/lib/xmpp4r/discovery.rb +2 -0
- data/lib/xmpp4r/error.rb +230 -0
- data/lib/xmpp4r/errorexception.rb +32 -0
- data/lib/xmpp4r/feature_negotiation/iq/feature.rb +42 -0
- data/lib/xmpp4r/feature_negotiation.rb +1 -0
- data/lib/xmpp4r/idgenerator.rb +37 -0
- data/lib/xmpp4r/iq.rb +229 -0
- data/lib/xmpp4r/jid.rb +167 -0
- data/lib/xmpp4r/message.rb +171 -0
- data/lib/xmpp4r/muc/helper/mucbrowser.rb +107 -0
- data/lib/xmpp4r/muc/helper/mucclient.rb +382 -0
- data/lib/xmpp4r/muc/helper/simplemucclient.rb +222 -0
- data/lib/xmpp4r/muc/x/muc.rb +98 -0
- data/lib/xmpp4r/muc/x/mucuserinvite.rb +58 -0
- data/lib/xmpp4r/muc/x/mucuseritem.rb +148 -0
- data/lib/xmpp4r/muc.rb +6 -0
- data/lib/xmpp4r/presence.rb +255 -0
- data/lib/xmpp4r/query.rb +43 -0
- data/lib/xmpp4r/rexmladdons.rb +826 -0
- data/lib/xmpp4r/roster/helper/roster.rb +514 -0
- data/lib/xmpp4r/roster/iq/roster.rb +244 -0
- data/lib/xmpp4r/roster/x/roster.rb +155 -0
- data/lib/xmpp4r/roster.rb +4 -0
- data/lib/xmpp4r/sasl.rb +167 -0
- data/lib/xmpp4r/stream.rb +543 -0
- data/lib/xmpp4r/streamparser.rb +77 -0
- data/lib/xmpp4r/vcard/helper/vcard.rb +86 -0
- data/lib/xmpp4r/vcard/iq/vcard.rb +102 -0
- data/lib/xmpp4r/vcard.rb +3 -0
- data/lib/xmpp4r/version/helper/responder.rb +71 -0
- data/lib/xmpp4r/version/helper/simpleresponder.rb +44 -0
- data/lib/xmpp4r/version/iq/version.rb +118 -0
- data/lib/xmpp4r/version.rb +3 -0
- data/lib/xmpp4r/x.rb +43 -0
- data/lib/xmpp4r/xmlstanza.rb +174 -0
- data/lib/xmpp4r/xmpp4r.rb +16 -0
- data/lib/xmpp4r.rb +122 -0
- data/setup.rb +1360 -0
- data/test/bytestreams/tc_ibb.rb +186 -0
- data/test/bytestreams/tc_socks5bytestreams.rb +57 -0
- data/test/delay/tc_xdelay.rb +51 -0
- data/test/lib/clienttester.rb +110 -0
- data/test/muc/tc_muc_mucclient.rb +569 -0
- data/test/muc/tc_muc_simplemucclient.rb +72 -0
- data/test/roster/.tc_helper.rb.swp +0 -0
- data/test/roster/tc_helper.rb +389 -0
- data/test/roster/tc_iqqueryroster.rb +140 -0
- data/test/roster/tc_xroster.rb +70 -0
- data/test/tc_callbacks.rb +128 -0
- data/test/tc_class_names.rb +129 -0
- data/test/tc_client.rb +30 -0
- data/test/tc_error.rb +103 -0
- data/test/tc_idgenerator.rb +30 -0
- data/test/tc_iq.rb +109 -0
- data/test/tc_iqquery.rb +31 -0
- data/test/tc_jid.rb +202 -0
- data/test/tc_message.rb +114 -0
- data/test/tc_presence.rb +148 -0
- data/test/tc_stream.rb +182 -0
- data/test/tc_streamError.rb +87 -0
- data/test/tc_streamSend.rb +59 -0
- data/test/tc_streamThreaded.rb +168 -0
- data/test/tc_xmlstanza.rb +76 -0
- data/test/ts_xmpp4r.rb +34 -0
- data/test/vcard/tc_iqvcard.rb +52 -0
- data/test/version/tc_helper.rb +46 -0
- data/test/version/tc_iqqueryversion.rb +96 -0
- data/tools/doctoweb.bash +30 -0
- data/tools/gen_requires.bash +10 -0
- metadata +232 -0
|
@@ -0,0 +1,826 @@
|
|
|
1
|
+
# =XMPP4R - XMPP Library for Ruby
|
|
2
|
+
# License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
|
|
3
|
+
# Website::http://home.gna.org/xmpp4r/
|
|
4
|
+
|
|
5
|
+
require 'rexml/document'
|
|
6
|
+
require 'rexml/parsers/xpathparser'
|
|
7
|
+
require 'rexml/source'
|
|
8
|
+
|
|
9
|
+
# Turn $VERBOSE off to suppress warnings about redefinition
|
|
10
|
+
oldverbose = $VERBOSE
|
|
11
|
+
$VERBOSE = false
|
|
12
|
+
|
|
13
|
+
# REXML module. This file only adds a few methods to the REXML module, to
|
|
14
|
+
# ease the coding.
|
|
15
|
+
module REXML
|
|
16
|
+
# this class adds a few helper methods to REXML::Element
|
|
17
|
+
class Element
|
|
18
|
+
##
|
|
19
|
+
# Replaces or add a child element of name <tt>e</tt> with text <tt>t</tt>.
|
|
20
|
+
def replace_element_text(e, t)
|
|
21
|
+
el = first_element(e)
|
|
22
|
+
if el.nil?
|
|
23
|
+
el = REXML::Element::new(e)
|
|
24
|
+
add_element(el)
|
|
25
|
+
end
|
|
26
|
+
if t
|
|
27
|
+
el.text = t
|
|
28
|
+
end
|
|
29
|
+
self
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
##
|
|
33
|
+
# Returns first element of name <tt>e</tt>
|
|
34
|
+
def first_element(e)
|
|
35
|
+
each_element(e) { |el| return el }
|
|
36
|
+
return nil
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
##
|
|
40
|
+
# Returns text of first element of name <tt>e</tt>
|
|
41
|
+
def first_element_text(e)
|
|
42
|
+
el = first_element(e)
|
|
43
|
+
if el
|
|
44
|
+
return el.text
|
|
45
|
+
else
|
|
46
|
+
return nil
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# This method does exactly the same thing as add(), but it can be
|
|
51
|
+
# overriden by subclasses to provide on-the-fly object creations.
|
|
52
|
+
# For example, if you import a REXML::Element of name 'plop', and you
|
|
53
|
+
# have a Plop class that subclasses REXML::Element, with typed_add you
|
|
54
|
+
# can get your REXML::Element to be "magically" converted to Plop.
|
|
55
|
+
def typed_add(e)
|
|
56
|
+
add(e)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
##
|
|
60
|
+
# import this element's children and attributes
|
|
61
|
+
def import(xmlelement)
|
|
62
|
+
if @name and @name != xmlelement.name
|
|
63
|
+
raise "Trying to import an #{xmlelement.name} to a #{@name} !"
|
|
64
|
+
end
|
|
65
|
+
add_attributes(xmlelement.attributes.clone)
|
|
66
|
+
@context = xmlelement.context
|
|
67
|
+
xmlelement.each do |e|
|
|
68
|
+
if e.kind_of? REXML::Element
|
|
69
|
+
typed_add(e.deep_clone)
|
|
70
|
+
else # text element, probably.
|
|
71
|
+
add(e.clone)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
self
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def self.import(xmlelement)
|
|
78
|
+
self.new.import(xmlelement)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
##
|
|
82
|
+
# Deletes one or more children elements,
|
|
83
|
+
# not just one like REXML::Element#delete_element
|
|
84
|
+
def delete_elements(element)
|
|
85
|
+
while(delete_element(element)) do end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# very dirty fix for the :progress problem in REXML from Ruby 1.8.3
|
|
91
|
+
# http://www.germane-software.com/projects/rexml/ticket/34
|
|
92
|
+
class IOSource
|
|
93
|
+
def position
|
|
94
|
+
0
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def current_line
|
|
98
|
+
[0, 0, ""]
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
############################################################################
|
|
103
|
+
# The XPath parser has bugs. Here is a patch.
|
|
104
|
+
############################################################################
|
|
105
|
+
# You don't want to use this class. Really. Use XPath, which is a wrapper
|
|
106
|
+
# for this class. Believe me. You don't want to poke around in here.
|
|
107
|
+
# There is strange, dark magic at work in this code. Beware. Go back! Go
|
|
108
|
+
# back while you still can!
|
|
109
|
+
class XPathParser
|
|
110
|
+
include XMLTokens
|
|
111
|
+
# LITERAL = /^'([^']*)'|^"([^"]*)"/u
|
|
112
|
+
|
|
113
|
+
def initialize( )
|
|
114
|
+
@parser = REXML::Parsers::XPathParser.new
|
|
115
|
+
@namespaces = {}
|
|
116
|
+
@variables = {}
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def namespaces=( namespaces={} )
|
|
120
|
+
Functions::namespace_context = namespaces
|
|
121
|
+
@namespaces = namespaces
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def variables=( vars={} )
|
|
125
|
+
Functions::variables = vars
|
|
126
|
+
@variables = vars
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def parse path, nodeset
|
|
130
|
+
#puts "#"*40
|
|
131
|
+
path_stack = @parser.parse( path )
|
|
132
|
+
#puts "PARSE: #{path} => #{path_stack.inspect}"
|
|
133
|
+
#puts "PARSE: nodeset = #{nodeset.inspect}"
|
|
134
|
+
match( path_stack, nodeset )
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def get_first path, nodeset
|
|
138
|
+
#puts "#"*40
|
|
139
|
+
path_stack = @parser.parse( path )
|
|
140
|
+
#puts "PARSE: #{path} => #{path_stack.inspect}"
|
|
141
|
+
#puts "PARSE: nodeset = #{nodeset.inspect}"
|
|
142
|
+
first( path_stack, nodeset )
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def predicate path, nodeset
|
|
146
|
+
path_stack = @parser.parse( path )
|
|
147
|
+
expr( path_stack, nodeset )
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def []=( variable_name, value )
|
|
151
|
+
@variables[ variable_name ] = value
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# Performs a depth-first (document order) XPath search, and returns the
|
|
156
|
+
# first match. This is the fastest, lightest way to return a single result.
|
|
157
|
+
def first( path_stack, node )
|
|
158
|
+
#puts "#{depth}) Entering match( #{path.inspect}, #{tree.inspect} )"
|
|
159
|
+
return nil if path.size == 0
|
|
160
|
+
|
|
161
|
+
case path[0]
|
|
162
|
+
when :document
|
|
163
|
+
# do nothing
|
|
164
|
+
return first( path[1..-1], node )
|
|
165
|
+
when :child
|
|
166
|
+
for c in node.children
|
|
167
|
+
#puts "#{depth}) CHILD checking #{name(c)}"
|
|
168
|
+
r = first( path[1..-1], c )
|
|
169
|
+
#puts "#{depth}) RETURNING #{r.inspect}" if r
|
|
170
|
+
return r if r
|
|
171
|
+
end
|
|
172
|
+
when :qname
|
|
173
|
+
name = path[2]
|
|
174
|
+
#puts "#{depth}) QNAME #{name(tree)} == #{name} (path => #{path.size})"
|
|
175
|
+
if node.name == name
|
|
176
|
+
#puts "#{depth}) RETURNING #{tree.inspect}" if path.size == 3
|
|
177
|
+
return node if path.size == 3
|
|
178
|
+
return first( path[3..-1], node )
|
|
179
|
+
else
|
|
180
|
+
return nil
|
|
181
|
+
end
|
|
182
|
+
when :descendant_or_self
|
|
183
|
+
r = first( path[1..-1], node )
|
|
184
|
+
return r if r
|
|
185
|
+
for c in node.children
|
|
186
|
+
r = first( path, c )
|
|
187
|
+
return r if r
|
|
188
|
+
end
|
|
189
|
+
when :node
|
|
190
|
+
return first( path[1..-1], node )
|
|
191
|
+
when :any
|
|
192
|
+
return first( path[1..-1], node )
|
|
193
|
+
end
|
|
194
|
+
return nil
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def match( path_stack, nodeset )
|
|
199
|
+
#puts "MATCH: path_stack = #{path_stack.inspect}"
|
|
200
|
+
#puts "MATCH: nodeset = #{nodeset.inspect}"
|
|
201
|
+
r = expr( path_stack, nodeset )
|
|
202
|
+
#puts "MAIN EXPR => #{r.inspect}"
|
|
203
|
+
r
|
|
204
|
+
|
|
205
|
+
#while ( path_stack.size > 0 and nodeset.size > 0 )
|
|
206
|
+
# #puts "MATCH: #{path_stack.inspect} '#{nodeset.collect{|n|n.class}.inspect}'"
|
|
207
|
+
# nodeset = expr( path_stack, nodeset )
|
|
208
|
+
# #puts "NODESET: #{nodeset.inspect}"
|
|
209
|
+
# #puts "PATH_STACK: #{path_stack.inspect}"
|
|
210
|
+
#end
|
|
211
|
+
#nodeset
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
private
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
# Expr takes a stack of path elements and a set of nodes (either a Parent
|
|
218
|
+
# or an Array and returns an Array of matching nodes
|
|
219
|
+
ALL = [ :attribute, :element, :text, :processing_instruction, :comment ] unless defined?(ALL)
|
|
220
|
+
ELEMENTS = [ :element ] unless defined?(ELEMENTS)
|
|
221
|
+
def expr( path_stack, nodeset, context=nil )
|
|
222
|
+
#puts "#"*15
|
|
223
|
+
#puts "In expr with #{path_stack.inspect}"
|
|
224
|
+
#puts "Returning" if path_stack.length == 0 || nodeset.length == 0
|
|
225
|
+
node_types = ELEMENTS
|
|
226
|
+
return nodeset if path_stack.length == 0 || nodeset.length == 0
|
|
227
|
+
while path_stack.length > 0
|
|
228
|
+
#puts "Path stack = #{path_stack.inspect}"
|
|
229
|
+
#puts "Nodeset is #{nodeset.inspect}"
|
|
230
|
+
case (op = path_stack.shift)
|
|
231
|
+
when :document
|
|
232
|
+
nodeset = [ nodeset[0].root_node ]
|
|
233
|
+
#puts ":document, nodeset = #{nodeset.inspect}"
|
|
234
|
+
|
|
235
|
+
when :qname
|
|
236
|
+
#puts "IN QNAME"
|
|
237
|
+
prefix = path_stack.shift
|
|
238
|
+
name = path_stack.shift
|
|
239
|
+
default_ns = @namespaces[prefix]
|
|
240
|
+
default_ns = default_ns ? default_ns : ''
|
|
241
|
+
nodeset.delete_if do |node|
|
|
242
|
+
ns = default_ns
|
|
243
|
+
# FIXME: This DOUBLES the time XPath searches take
|
|
244
|
+
ns = node.namespace( prefix ) if node.node_type == :element and ns == ''
|
|
245
|
+
#puts "NS = #{ns.inspect}"
|
|
246
|
+
#puts "node.node_type == :element => #{node.node_type == :element}"
|
|
247
|
+
if node.node_type == :element
|
|
248
|
+
#puts "node.name == #{name} => #{node.name == name}"
|
|
249
|
+
if node.name == name
|
|
250
|
+
#puts "node.namespace == #{ns.inspect} => #{node.namespace == ns}"
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
!(node.node_type == :element and
|
|
254
|
+
node.name == name and
|
|
255
|
+
node.namespace == ns )
|
|
256
|
+
end
|
|
257
|
+
node_types = ELEMENTS
|
|
258
|
+
|
|
259
|
+
when :any
|
|
260
|
+
#puts "ANY 1: nodeset = #{nodeset.inspect}"
|
|
261
|
+
#puts "ANY 1: node_types = #{node_types.inspect}"
|
|
262
|
+
nodeset.delete_if { |node| !node_types.include?(node.node_type) }
|
|
263
|
+
#puts "ANY 2: nodeset = #{nodeset.inspect}"
|
|
264
|
+
|
|
265
|
+
when :self
|
|
266
|
+
# This space left intentionally blank
|
|
267
|
+
|
|
268
|
+
when :processing_instruction
|
|
269
|
+
target = path_stack.shift
|
|
270
|
+
nodeset.delete_if do |node|
|
|
271
|
+
(node.node_type != :processing_instruction) or
|
|
272
|
+
( target!='' and ( node.target != target ) )
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
when :text
|
|
276
|
+
nodeset.delete_if { |node| node.node_type != :text }
|
|
277
|
+
|
|
278
|
+
when :comment
|
|
279
|
+
nodeset.delete_if { |node| node.node_type != :comment }
|
|
280
|
+
|
|
281
|
+
when :node
|
|
282
|
+
# This space left intentionally blank
|
|
283
|
+
node_types = ALL
|
|
284
|
+
|
|
285
|
+
when :child
|
|
286
|
+
new_nodeset = []
|
|
287
|
+
nt = nil
|
|
288
|
+
for node in nodeset
|
|
289
|
+
nt = node.node_type
|
|
290
|
+
new_nodeset += node.children if nt == :element or nt == :document
|
|
291
|
+
end
|
|
292
|
+
nodeset = new_nodeset
|
|
293
|
+
node_types = ELEMENTS
|
|
294
|
+
|
|
295
|
+
when :literal
|
|
296
|
+
literal = path_stack.shift
|
|
297
|
+
if literal =~ /^\d+(\.\d+)?$/
|
|
298
|
+
return ($1 ? literal.to_f : literal.to_i)
|
|
299
|
+
end
|
|
300
|
+
return literal
|
|
301
|
+
|
|
302
|
+
when :attribute
|
|
303
|
+
new_nodeset = []
|
|
304
|
+
case path_stack.shift
|
|
305
|
+
when :qname
|
|
306
|
+
prefix = path_stack.shift
|
|
307
|
+
name = path_stack.shift
|
|
308
|
+
for element in nodeset
|
|
309
|
+
if element.node_type == :element
|
|
310
|
+
#puts element.name
|
|
311
|
+
attr = element.attribute( name, @namespaces[prefix] )
|
|
312
|
+
new_nodeset << attr if attr
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
when :any
|
|
316
|
+
#puts "ANY"
|
|
317
|
+
for element in nodeset
|
|
318
|
+
if element.node_type == :element
|
|
319
|
+
new_nodeset += element.attributes.to_a
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
nodeset = new_nodeset
|
|
324
|
+
|
|
325
|
+
when :parent
|
|
326
|
+
#puts "PARENT 1: nodeset = #{nodeset}"
|
|
327
|
+
nodeset = nodeset.collect{|n| n.parent}.compact
|
|
328
|
+
#nodeset = expr(path_stack.dclone, nodeset.collect{|n| n.parent}.compact)
|
|
329
|
+
#puts "PARENT 2: nodeset = #{nodeset.inspect}"
|
|
330
|
+
node_types = ELEMENTS
|
|
331
|
+
|
|
332
|
+
when :ancestor
|
|
333
|
+
new_nodeset = []
|
|
334
|
+
for node in nodeset
|
|
335
|
+
while node.parent
|
|
336
|
+
node = node.parent
|
|
337
|
+
new_nodeset << node unless new_nodeset.include? node
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
nodeset = new_nodeset
|
|
341
|
+
node_types = ELEMENTS
|
|
342
|
+
|
|
343
|
+
when :ancestor_or_self
|
|
344
|
+
new_nodeset = []
|
|
345
|
+
for node in nodeset
|
|
346
|
+
if node.node_type == :element
|
|
347
|
+
new_nodeset << node
|
|
348
|
+
while ( node.parent )
|
|
349
|
+
node = node.parent
|
|
350
|
+
new_nodeset << node unless new_nodeset.include? node
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
nodeset = new_nodeset
|
|
355
|
+
node_types = ELEMENTS
|
|
356
|
+
|
|
357
|
+
when :predicate
|
|
358
|
+
new_nodeset = []
|
|
359
|
+
subcontext = { :size => nodeset.size }
|
|
360
|
+
pred = path_stack.shift
|
|
361
|
+
nodeset.each_with_index { |node, index|
|
|
362
|
+
subcontext[ :node ] = node
|
|
363
|
+
#puts "PREDICATE SETTING CONTEXT INDEX TO #{index+1}"
|
|
364
|
+
subcontext[ :index ] = index+1
|
|
365
|
+
pc = pred.dclone
|
|
366
|
+
#puts "#{node.hash}) Recursing with #{pred.inspect} and [#{node.inspect}]"
|
|
367
|
+
result = expr( pc, [node], subcontext )
|
|
368
|
+
result = result[0] if result.kind_of? Array and result.length == 1
|
|
369
|
+
#puts "#{node.hash}) Result = #{result.inspect} (#{result.class.name})"
|
|
370
|
+
if result.kind_of? Numeric
|
|
371
|
+
#puts "Adding node #{node.inspect}" if result == (index+1)
|
|
372
|
+
new_nodeset << node if result == (index+1)
|
|
373
|
+
elsif result.instance_of? Array
|
|
374
|
+
#puts "Adding node #{node.inspect}" if result.size > 0
|
|
375
|
+
new_nodeset << node if result.size > 0
|
|
376
|
+
else
|
|
377
|
+
#puts "Adding node #{node.inspect}" if result
|
|
378
|
+
new_nodeset << node if result
|
|
379
|
+
end
|
|
380
|
+
}
|
|
381
|
+
#puts "New nodeset = #{new_nodeset.inspect}"
|
|
382
|
+
#puts "Path_stack = #{path_stack.inspect}"
|
|
383
|
+
nodeset = new_nodeset
|
|
384
|
+
=begin
|
|
385
|
+
predicate = path_stack.shift
|
|
386
|
+
ns = nodeset.clone
|
|
387
|
+
result = expr( predicate, ns )
|
|
388
|
+
#puts "Result = #{result.inspect} (#{result.class.name})"
|
|
389
|
+
#puts "nodeset = #{nodeset.inspect}"
|
|
390
|
+
if result.kind_of? Array
|
|
391
|
+
nodeset = result.zip(ns).collect{|m,n| n if m}.compact
|
|
392
|
+
else
|
|
393
|
+
nodeset = result ? nodeset : []
|
|
394
|
+
end
|
|
395
|
+
#puts "Outgoing NS = #{nodeset.inspect}"
|
|
396
|
+
=end
|
|
397
|
+
|
|
398
|
+
when :descendant_or_self
|
|
399
|
+
rv = descendant_or_self( path_stack, nodeset )
|
|
400
|
+
path_stack.clear
|
|
401
|
+
nodeset = rv
|
|
402
|
+
node_types = ELEMENTS
|
|
403
|
+
|
|
404
|
+
when :descendant
|
|
405
|
+
results = []
|
|
406
|
+
nt = nil
|
|
407
|
+
for node in nodeset
|
|
408
|
+
nt = node.node_type
|
|
409
|
+
results += expr( path_stack.dclone.unshift( :descendant_or_self ),
|
|
410
|
+
node.children ) if nt == :element or nt == :document
|
|
411
|
+
end
|
|
412
|
+
nodeset = results
|
|
413
|
+
node_types = ELEMENTS
|
|
414
|
+
|
|
415
|
+
when :following_sibling
|
|
416
|
+
#puts "FOLLOWING_SIBLING 1: nodeset = #{nodeset}"
|
|
417
|
+
results = []
|
|
418
|
+
for node in nodeset
|
|
419
|
+
all_siblings = node.parent.children
|
|
420
|
+
current_index = all_siblings.index( node )
|
|
421
|
+
following_siblings = all_siblings[ current_index+1 .. -1 ]
|
|
422
|
+
results += expr( path_stack.dclone, following_siblings )
|
|
423
|
+
end
|
|
424
|
+
#puts "FOLLOWING_SIBLING 2: nodeset = #{nodeset}"
|
|
425
|
+
nodeset = results
|
|
426
|
+
|
|
427
|
+
when :preceding_sibling
|
|
428
|
+
results = []
|
|
429
|
+
for node in nodeset
|
|
430
|
+
all_siblings = node.parent.children
|
|
431
|
+
current_index = all_siblings.index( node )
|
|
432
|
+
preceding_siblings = all_siblings[ 0 .. current_index-1 ].reverse
|
|
433
|
+
#results += expr( path_stack.dclone, preceding_siblings )
|
|
434
|
+
end
|
|
435
|
+
nodeset = preceding_siblings
|
|
436
|
+
node_types = ELEMENTS
|
|
437
|
+
|
|
438
|
+
when :preceding
|
|
439
|
+
new_nodeset = []
|
|
440
|
+
for node in nodeset
|
|
441
|
+
new_nodeset += preceding( node )
|
|
442
|
+
end
|
|
443
|
+
#puts "NEW NODESET => #{new_nodeset.inspect}"
|
|
444
|
+
nodeset = new_nodeset
|
|
445
|
+
node_types = ELEMENTS
|
|
446
|
+
|
|
447
|
+
when :following
|
|
448
|
+
new_nodeset = []
|
|
449
|
+
for node in nodeset
|
|
450
|
+
new_nodeset += following( node )
|
|
451
|
+
end
|
|
452
|
+
nodeset = new_nodeset
|
|
453
|
+
node_types = ELEMENTS
|
|
454
|
+
|
|
455
|
+
when :namespace
|
|
456
|
+
new_set = []
|
|
457
|
+
for node in nodeset
|
|
458
|
+
new_nodeset << node.namespace if node.node_type == :element or node.node_type == :attribute
|
|
459
|
+
end
|
|
460
|
+
nodeset = new_nodeset
|
|
461
|
+
|
|
462
|
+
when :variable
|
|
463
|
+
var_name = path_stack.shift
|
|
464
|
+
return @variables[ var_name ]
|
|
465
|
+
|
|
466
|
+
# :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq
|
|
467
|
+
when :eq, :neq, :lt, :lteq, :gt, :gteq, :and, :or
|
|
468
|
+
left = expr( path_stack.shift, nodeset, context )
|
|
469
|
+
#puts "LEFT => #{left.inspect} (#{left.class.name})"
|
|
470
|
+
right = expr( path_stack.shift, nodeset, context )
|
|
471
|
+
#puts "RIGHT => #{right.inspect} (#{right.class.name})"
|
|
472
|
+
res = equality_relational_compare( left, op, right )
|
|
473
|
+
#puts "RES => #{res.inspect}"
|
|
474
|
+
return res
|
|
475
|
+
|
|
476
|
+
when :div
|
|
477
|
+
left = Functions::number(expr(path_stack.shift, nodeset, context)).to_f
|
|
478
|
+
right = Functions::number(expr(path_stack.shift, nodeset, context)).to_f
|
|
479
|
+
return (left / right)
|
|
480
|
+
|
|
481
|
+
when :mod
|
|
482
|
+
left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
|
|
483
|
+
right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
|
|
484
|
+
return (left % right)
|
|
485
|
+
|
|
486
|
+
when :mult
|
|
487
|
+
left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
|
|
488
|
+
right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
|
|
489
|
+
return (left * right)
|
|
490
|
+
|
|
491
|
+
when :plus
|
|
492
|
+
left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
|
|
493
|
+
right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
|
|
494
|
+
return (left + right)
|
|
495
|
+
|
|
496
|
+
when :minus
|
|
497
|
+
left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
|
|
498
|
+
right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
|
|
499
|
+
return (left - right)
|
|
500
|
+
|
|
501
|
+
when :union
|
|
502
|
+
left = expr( path_stack.shift, nodeset, context )
|
|
503
|
+
right = expr( path_stack.shift, nodeset, context )
|
|
504
|
+
return (left | right)
|
|
505
|
+
|
|
506
|
+
when :neg
|
|
507
|
+
res = expr( path_stack, nodeset, context )
|
|
508
|
+
return -(res.to_f)
|
|
509
|
+
|
|
510
|
+
when :not
|
|
511
|
+
when :function
|
|
512
|
+
func_name = path_stack.shift.tr('-','_')
|
|
513
|
+
arguments = path_stack.shift
|
|
514
|
+
#puts "FUNCTION 0: #{func_name}(#{arguments.collect{|a|a.inspect}.join(', ')})"
|
|
515
|
+
subcontext = context ? nil : { :size => nodeset.size }
|
|
516
|
+
|
|
517
|
+
res = []
|
|
518
|
+
cont = context
|
|
519
|
+
nodeset.each_with_index { |n, i|
|
|
520
|
+
if subcontext
|
|
521
|
+
subcontext[:node] = n
|
|
522
|
+
subcontext[:index] = i
|
|
523
|
+
cont = subcontext
|
|
524
|
+
end
|
|
525
|
+
arg_clone = arguments.dclone
|
|
526
|
+
args = arg_clone.collect { |arg|
|
|
527
|
+
#puts "FUNCTION 1: Calling expr( #{arg.inspect}, [#{n.inspect}] )"
|
|
528
|
+
expr( arg, [n], cont )
|
|
529
|
+
}
|
|
530
|
+
#puts "FUNCTION 2: #{func_name}(#{args.collect{|a|a.inspect}.join(', ')})"
|
|
531
|
+
Functions.context = cont
|
|
532
|
+
res << Functions.send( func_name, *args )
|
|
533
|
+
#puts "FUNCTION 3: #{res[-1].inspect}"
|
|
534
|
+
}
|
|
535
|
+
return res
|
|
536
|
+
|
|
537
|
+
end
|
|
538
|
+
end # while
|
|
539
|
+
#puts "EXPR returning #{nodeset.inspect}"
|
|
540
|
+
return nodeset
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
##########################################################
|
|
545
|
+
# FIXME
|
|
546
|
+
# The next two methods are BAD MOJO!
|
|
547
|
+
# This is my achilles heel. If anybody thinks of a better
|
|
548
|
+
# way of doing this, be my guest. This really sucks, but
|
|
549
|
+
# it took me three days to get it to work at all.
|
|
550
|
+
# ########################################################
|
|
551
|
+
|
|
552
|
+
def descendant_or_self( path_stack, nodeset )
|
|
553
|
+
rs = []
|
|
554
|
+
d_o_s( path_stack, nodeset, rs )
|
|
555
|
+
#puts "RS = #{rs.collect{|n|n.to_s}.inspect}"
|
|
556
|
+
document_order(rs.flatten.compact)
|
|
557
|
+
#rs.flatten.compact
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
def d_o_s( p, ns, r )
|
|
561
|
+
#puts "IN DOS with #{ns.inspect}; ALREADY HAVE #{r.inspect}"
|
|
562
|
+
nt = nil
|
|
563
|
+
ns.each_index do |i|
|
|
564
|
+
n = ns[i]
|
|
565
|
+
#puts "P => #{p.inspect}"
|
|
566
|
+
x = expr( p.dclone, [ n ] )
|
|
567
|
+
nt = n.node_type
|
|
568
|
+
d_o_s( p, n.children, x ) if nt == :element or nt == :document and n.children.size > 0
|
|
569
|
+
r.concat(x) if x.size > 0
|
|
570
|
+
end
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
# Reorders an array of nodes so that they are in document order
|
|
575
|
+
# It tries to do this efficiently.
|
|
576
|
+
#
|
|
577
|
+
# FIXME: I need to get rid of this, but the issue is that most of the XPath
|
|
578
|
+
# interpreter functions as a filter, which means that we lose context going
|
|
579
|
+
# in and out of function calls. If I knew what the index of the nodes was,
|
|
580
|
+
# I wouldn't have to do this. Maybe add a document IDX for each node?
|
|
581
|
+
# Problems with mutable documents. Or, rewrite everything.
|
|
582
|
+
def document_order( array_of_nodes )
|
|
583
|
+
new_arry = []
|
|
584
|
+
array_of_nodes.each { |node|
|
|
585
|
+
node_idx = []
|
|
586
|
+
np = node.node_type == :attribute ? node.element : node
|
|
587
|
+
while np.parent and np.parent.node_type == :element
|
|
588
|
+
node_idx << np.parent.index( np )
|
|
589
|
+
np = np.parent
|
|
590
|
+
end
|
|
591
|
+
new_arry << [ node_idx.reverse, node ]
|
|
592
|
+
}
|
|
593
|
+
#puts "new_arry = #{new_arry.inspect}"
|
|
594
|
+
new_arry.sort{ |s1, s2| s1[0] <=> s2[0] }.collect{ |s| s[1] }
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
def recurse( nodeset, &block )
|
|
599
|
+
for node in nodeset
|
|
600
|
+
yield node
|
|
601
|
+
recurse( node, &block ) if node.node_type == :element
|
|
602
|
+
end
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
# Builds a nodeset of all of the preceding nodes of the supplied node,
|
|
608
|
+
# in reverse document order
|
|
609
|
+
# preceding:: includes every element in the document that precedes this node,
|
|
610
|
+
# except for ancestors
|
|
611
|
+
def preceding( node )
|
|
612
|
+
#puts "IN PRECEDING"
|
|
613
|
+
ancestors = []
|
|
614
|
+
p = node.parent
|
|
615
|
+
while p
|
|
616
|
+
ancestors << p
|
|
617
|
+
p = p.parent
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
acc = []
|
|
621
|
+
p = preceding_node_of( node )
|
|
622
|
+
#puts "P = #{p.inspect}"
|
|
623
|
+
while p
|
|
624
|
+
if ancestors.include? p
|
|
625
|
+
ancestors.delete(p)
|
|
626
|
+
else
|
|
627
|
+
acc << p
|
|
628
|
+
end
|
|
629
|
+
p = preceding_node_of( p )
|
|
630
|
+
#puts "P = #{p.inspect}"
|
|
631
|
+
end
|
|
632
|
+
acc
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
def preceding_node_of( node )
|
|
636
|
+
#puts "NODE: #{node.inspect}"
|
|
637
|
+
#puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}"
|
|
638
|
+
#puts "PARENT NODE: #{node.parent}"
|
|
639
|
+
psn = node.previous_sibling_node
|
|
640
|
+
if psn.nil?
|
|
641
|
+
if node.parent.nil? or node.parent.class == Document
|
|
642
|
+
return nil
|
|
643
|
+
end
|
|
644
|
+
return node.parent
|
|
645
|
+
#psn = preceding_node_of( node.parent )
|
|
646
|
+
end
|
|
647
|
+
while psn and psn.kind_of? Element and psn.children.size > 0
|
|
648
|
+
psn = psn.children[-1]
|
|
649
|
+
end
|
|
650
|
+
psn
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
def following( node )
|
|
654
|
+
#puts "IN PRECEDING"
|
|
655
|
+
acc = []
|
|
656
|
+
p = next_sibling_node( node )
|
|
657
|
+
#puts "P = #{p.inspect}"
|
|
658
|
+
while p
|
|
659
|
+
acc << p
|
|
660
|
+
p = following_node_of( p )
|
|
661
|
+
#puts "P = #{p.inspect}"
|
|
662
|
+
end
|
|
663
|
+
acc
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
def following_node_of( node )
|
|
667
|
+
#puts "NODE: #{node.inspect}"
|
|
668
|
+
#puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}"
|
|
669
|
+
#puts "PARENT NODE: #{node.parent}"
|
|
670
|
+
if node.kind_of? Element and node.children.size > 0
|
|
671
|
+
return node.children[0]
|
|
672
|
+
end
|
|
673
|
+
return next_sibling_node(node)
|
|
674
|
+
end
|
|
675
|
+
|
|
676
|
+
def next_sibling_node(node)
|
|
677
|
+
psn = node.next_sibling_node
|
|
678
|
+
while psn.nil?
|
|
679
|
+
if node.parent.nil? or node.parent.class == Document
|
|
680
|
+
return nil
|
|
681
|
+
end
|
|
682
|
+
node = node.parent
|
|
683
|
+
psn = node.next_sibling_node
|
|
684
|
+
#puts "psn = #{psn.inspect}"
|
|
685
|
+
end
|
|
686
|
+
return psn
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
def norm b
|
|
690
|
+
case b
|
|
691
|
+
when true, false
|
|
692
|
+
return b
|
|
693
|
+
when 'true', 'false'
|
|
694
|
+
return Functions::boolean( b )
|
|
695
|
+
when /^\d+(\.\d+)?$/
|
|
696
|
+
return Functions::number( b )
|
|
697
|
+
else
|
|
698
|
+
return Functions::string( b )
|
|
699
|
+
end
|
|
700
|
+
end
|
|
701
|
+
|
|
702
|
+
def equality_relational_compare( set1, op, set2 )
|
|
703
|
+
#puts "EQ_REL_COMP(#{set1.inspect} #{op.inspect} #{set2.inspect})"
|
|
704
|
+
if set1.kind_of? Array and set2.kind_of? Array
|
|
705
|
+
#puts "#{set1.size} & #{set2.size}"
|
|
706
|
+
if set1.size == 1 and set2.size == 1
|
|
707
|
+
set1 = set1[0]
|
|
708
|
+
set2 = set2[0]
|
|
709
|
+
elsif set1.size == 0 or set2.size == 0
|
|
710
|
+
nd = set1.size==0 ? set2 : set1
|
|
711
|
+
rv = nd.collect { |il| compare( il, op, nil ) }
|
|
712
|
+
#puts "RV = #{rv.inspect}"
|
|
713
|
+
return rv
|
|
714
|
+
else
|
|
715
|
+
res = []
|
|
716
|
+
enum = SyncEnumerator.new( set1, set2 ).each { |i1, i2|
|
|
717
|
+
#puts "i1 = #{i1.inspect} (#{i1.class.name})"
|
|
718
|
+
#puts "i2 = #{i2.inspect} (#{i2.class.name})"
|
|
719
|
+
i1 = norm( i1 )
|
|
720
|
+
i2 = norm( i2 )
|
|
721
|
+
res << compare( i1, op, i2 )
|
|
722
|
+
}
|
|
723
|
+
return res
|
|
724
|
+
end
|
|
725
|
+
end
|
|
726
|
+
#puts "EQ_REL_COMP: #{set1.inspect} (#{set1.class.name}), #{op}, #{set2.inspect} (#{set2.class.name})"
|
|
727
|
+
#puts "COMPARING VALUES"
|
|
728
|
+
# If one is nodeset and other is number, compare number to each item
|
|
729
|
+
# in nodeset s.t. number op number(string(item))
|
|
730
|
+
# If one is nodeset and other is string, compare string to each item
|
|
731
|
+
# in nodeset s.t. string op string(item)
|
|
732
|
+
# If one is nodeset and other is boolean, compare boolean to each item
|
|
733
|
+
# in nodeset s.t. boolean op boolean(item)
|
|
734
|
+
if set1.kind_of? Array or set2.kind_of? Array
|
|
735
|
+
#puts "ISA ARRAY"
|
|
736
|
+
if set1.kind_of? Array
|
|
737
|
+
a = set1
|
|
738
|
+
b = set2
|
|
739
|
+
else
|
|
740
|
+
a = set2
|
|
741
|
+
b = set1
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
case b
|
|
745
|
+
when true, false
|
|
746
|
+
return a.collect {|v| compare( Functions::boolean(v), op, b ) }
|
|
747
|
+
when Numeric
|
|
748
|
+
return a.collect {|v| compare( Functions::number(v), op, b )}
|
|
749
|
+
when /^\d+(\.\d+)?$/
|
|
750
|
+
b = Functions::number( b )
|
|
751
|
+
#puts "B = #{b.inspect}"
|
|
752
|
+
return a.collect {|v| compare( Functions::number(v), op, b )}
|
|
753
|
+
else
|
|
754
|
+
#puts "Functions::string( #{b}(#{b.class.name}) ) = #{Functions::string(b)}"
|
|
755
|
+
b = Functions::string( b )
|
|
756
|
+
return a.collect { |v| compare( Functions::string(v), op, b ) }
|
|
757
|
+
end
|
|
758
|
+
else
|
|
759
|
+
# If neither is nodeset,
|
|
760
|
+
# If op is = or !=
|
|
761
|
+
# If either boolean, convert to boolean
|
|
762
|
+
# If either number, convert to number
|
|
763
|
+
# Else, convert to string
|
|
764
|
+
# Else
|
|
765
|
+
# Convert both to numbers and compare
|
|
766
|
+
s1 = set1.to_s
|
|
767
|
+
s2 = set2.to_s
|
|
768
|
+
#puts "EQ_REL_COMP: #{set1}=>#{s1}, #{set2}=>#{s2}"
|
|
769
|
+
if s1 == 'true' or s1 == 'false' or s2 == 'true' or s2 == 'false'
|
|
770
|
+
#puts "Functions::boolean(#{set1})=>#{Functions::boolean(set1)}"
|
|
771
|
+
#puts "Functions::boolean(#{set2})=>#{Functions::boolean(set2)}"
|
|
772
|
+
set1 = Functions::boolean( set1 )
|
|
773
|
+
set2 = Functions::boolean( set2 )
|
|
774
|
+
else
|
|
775
|
+
if op == :eq or op == :neq
|
|
776
|
+
if s1 =~ /^\d+(\.\d+)?$/ or s2 =~ /^\d+(\.\d+)?$/
|
|
777
|
+
set1 = Functions::number( s1 )
|
|
778
|
+
set2 = Functions::number( s2 )
|
|
779
|
+
else
|
|
780
|
+
set1 = Functions::string( set1 )
|
|
781
|
+
set2 = Functions::string( set2 )
|
|
782
|
+
end
|
|
783
|
+
else
|
|
784
|
+
set1 = Functions::number( set1 )
|
|
785
|
+
set2 = Functions::number( set2 )
|
|
786
|
+
end
|
|
787
|
+
end
|
|
788
|
+
#puts "EQ_REL_COMP: #{set1} #{op} #{set2}"
|
|
789
|
+
#puts ">>> #{compare( set1, op, set2 )}"
|
|
790
|
+
return compare( set1, op, set2 )
|
|
791
|
+
end
|
|
792
|
+
return false
|
|
793
|
+
end
|
|
794
|
+
|
|
795
|
+
def compare a, op, b
|
|
796
|
+
#puts "COMPARE #{a.inspect}(#{a.class.name}) #{op} #{b.inspect}(#{b.class.name})"
|
|
797
|
+
case op
|
|
798
|
+
when :eq
|
|
799
|
+
a == b
|
|
800
|
+
when :neq
|
|
801
|
+
a != b
|
|
802
|
+
when :lt
|
|
803
|
+
a < b
|
|
804
|
+
when :lteq
|
|
805
|
+
a <= b
|
|
806
|
+
when :gt
|
|
807
|
+
a > b
|
|
808
|
+
when :gteq
|
|
809
|
+
a >= b
|
|
810
|
+
when :and
|
|
811
|
+
a and b
|
|
812
|
+
when :or
|
|
813
|
+
a or b
|
|
814
|
+
else
|
|
815
|
+
false
|
|
816
|
+
end
|
|
817
|
+
end
|
|
818
|
+
end
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
############################################################################
|
|
822
|
+
end
|
|
823
|
+
|
|
824
|
+
# Restore the old $VERBOSE setting
|
|
825
|
+
$VERBOSE = oldverbose
|
|
826
|
+
|