source: extensions/gsdl-video/trunk/installed/cmdline/lib/ruby/1.8/rexml/functions.rb@ 18425

Last change on this file since 18425 was 18425, checked in by davidb, 15 years ago

Video extension to Greenstone

File size: 10.9 KB
Line 
1module REXML
2 # If you add a method, keep in mind two things:
3 # (1) the first argument will always be a list of nodes from which to
4 # filter. In the case of context methods (such as position), the function
5 # should return an array with a value for each child in the array.
6 # (2) all method calls from XML will have "-" replaced with "_".
7 # Therefore, in XML, "local-name()" is identical (and actually becomes)
8 # "local_name()"
9 module Functions
10 @@context = nil
11 @@namespace_context = {}
12 @@variables = {}
13
14 def Functions::namespace_context=(x) ; @@namespace_context=x ; end
15 def Functions::variables=(x) ; @@variables=x ; end
16 def Functions::namespace_context ; @@namespace_context ; end
17 def Functions::variables ; @@variables ; end
18
19 def Functions::context=(value); @@context = value; end
20
21 def Functions::text( )
22 if @@context[:node].node_type == :element
23 return @@context[:node].find_all{|n| n.node_type == :text}.collect{|n| n.value}
24 elsif @@context[:node].node_type == :text
25 return @@context[:node].value
26 else
27 return false
28 end
29 end
30
31 def Functions::last( )
32 @@context[:size]
33 end
34
35 def Functions::position( )
36 @@context[:index]
37 end
38
39 def Functions::count( node_set )
40 node_set.size
41 end
42
43 # Since REXML is non-validating, this method is not implemented as it
44 # requires a DTD
45 def Functions::id( object )
46 end
47
48 # UNTESTED
49 def Functions::local_name( node_set=nil )
50 get_namespace( node_set ) do |node|
51 return node.local_name
52 end
53 end
54
55 def Functions::namespace_uri( node_set=nil )
56 get_namespace( node_set ) {|node| node.namespace}
57 end
58
59 def Functions::name( node_set=nil )
60 get_namespace( node_set ) do |node|
61 node.expanded_name
62 end
63 end
64
65 # Helper method.
66 def Functions::get_namespace( node_set = nil )
67 if node_set == nil
68 yield @@context[:node] if defined? @@context[:node].namespace
69 else
70 if node_set.respond_to? :each
71 node_set.each { |node| yield node if defined? node.namespace }
72 elsif node_set.respond_to? :namespace
73 yield node_set
74 end
75 end
76 end
77
78 # A node-set is converted to a string by returning the string-value of the
79 # node in the node-set that is first in document order. If the node-set is
80 # empty, an empty string is returned.
81 #
82 # A number is converted to a string as follows
83 #
84 # NaN is converted to the string NaN
85 #
86 # positive zero is converted to the string 0
87 #
88 # negative zero is converted to the string 0
89 #
90 # positive infinity is converted to the string Infinity
91 #
92 # negative infinity is converted to the string -Infinity
93 #
94 # if the number is an integer, the number is represented in decimal form
95 # as a Number with no decimal point and no leading zeros, preceded by a
96 # minus sign (-) if the number is negative
97 #
98 # otherwise, the number is represented in decimal form as a Number
99 # including a decimal point with at least one digit before the decimal
100 # point and at least one digit after the decimal point, preceded by a
101 # minus sign (-) if the number is negative; there must be no leading zeros
102 # before the decimal point apart possibly from the one required digit
103 # immediately before the decimal point; beyond the one required digit
104 # after the decimal point there must be as many, but only as many, more
105 # digits as are needed to uniquely distinguish the number from all other
106 # IEEE 754 numeric values.
107 #
108 # The boolean false value is converted to the string false. The boolean
109 # true value is converted to the string true.
110 #
111 # An object of a type other than the four basic types is converted to a
112 # string in a way that is dependent on that type.
113 def Functions::string( object=nil )
114 #object = @context unless object
115 if object.instance_of? Array
116 string( object[0] )
117 elsif defined? object.node_type
118 if object.node_type == :attribute
119 object.value
120 elsif object.node_type == :element || object.node_type == :document
121 string_value(object)
122 else
123 object.to_s
124 end
125 elsif object.nil?
126 return ""
127 else
128 object.to_s
129 end
130 end
131
132 def Functions::string_value( o )
133 rv = ""
134 o.children.each { |e|
135 if e.node_type == :text
136 rv << e.to_s
137 elsif e.node_type == :element
138 rv << string_value( e )
139 end
140 }
141 rv
142 end
143
144 # UNTESTED
145 def Functions::concat( *objects )
146 objects.join
147 end
148
149 # Fixed by Mike Stok
150 def Functions::starts_with( string, test )
151 string(string).index(string(test)) == 0
152 end
153
154 # Fixed by Mike Stok
155 def Functions::contains( string, test )
156 string(string).include?(string(test))
157 end
158
159 # Kouhei fixed this
160 def Functions::substring_before( string, test )
161 ruby_string = string(string)
162 ruby_index = ruby_string.index(string(test))
163 if ruby_index.nil?
164 ""
165 else
166 ruby_string[ 0...ruby_index ]
167 end
168 end
169
170 # Kouhei fixed this too
171 def Functions::substring_after( string, test )
172 ruby_string = string(string)
173 test_string = string(test)
174 return $1 if ruby_string =~ /#{test}(.*)/
175 ""
176 end
177
178 # Take equal portions of Mike Stok and Sean Russell; mix
179 # vigorously, and pour into a tall, chilled glass. Serves 10,000.
180 def Functions::substring( string, start, length=nil )
181 ruby_string = string(string)
182 ruby_length = if length.nil?
183 ruby_string.length.to_f
184 else
185 number(length)
186 end
187 ruby_start = number(start)
188
189 # Handle the special cases
190 return '' if (
191 ruby_length.nan? or
192 ruby_start.nan? or
193 ruby_start.infinite?
194 )
195
196 infinite_length = ruby_length.infinite? == 1
197 ruby_length = ruby_string.length if infinite_length
198
199 # Now, get the bounds. The XPath bounds are 1..length; the ruby bounds
200 # are 0..length. Therefore, we have to offset the bounds by one.
201 ruby_start = ruby_start.round - 1
202 ruby_length = ruby_length.round
203
204 if ruby_start < 0
205 ruby_length += ruby_start unless infinite_length
206 ruby_start = 0
207 end
208 return '' if ruby_length <= 0
209 ruby_string[ruby_start,ruby_length]
210 end
211
212 # UNTESTED
213 def Functions::string_length( string )
214 string(string).length
215 end
216
217 # UNTESTED
218 def Functions::normalize_space( string=nil )
219 string = string(@@context[:node]) if string.nil?
220 if string.kind_of? Array
221 string.collect{|x| string.to_s.strip.gsub(/\s+/um, ' ') if string}
222 else
223 string.to_s.strip.gsub(/\s+/um, ' ')
224 end
225 end
226
227 # This is entirely Mike Stok's beast
228 def Functions::translate( string, tr1, tr2 )
229 from = string(tr1)
230 to = string(tr2)
231
232 # the map is our translation table.
233 #
234 # if a character occurs more than once in the
235 # from string then we ignore the second &
236 # subsequent mappings
237 #
238 # if a charactcer maps to nil then we delete it
239 # in the output. This happens if the from
240 # string is longer than the to string
241 #
242 # there's nothing about - or ^ being special in
243 # http://www.w3.org/TR/xpath#function-translate
244 # so we don't build ranges or negated classes
245
246 map = Hash.new
247 0.upto(from.length - 1) { |pos|
248 from_char = from[pos]
249 unless map.has_key? from_char
250 map[from_char] =
251 if pos < to.length
252 to[pos]
253 else
254 nil
255 end
256 end
257 }
258
259 string(string).unpack('U*').collect { |c|
260 if map.has_key? c then map[c] else c end
261 }.compact.pack('U*')
262 end
263
264 # UNTESTED
265 def Functions::boolean( object=nil )
266 if object.kind_of? String
267 if object =~ /\d+/u
268 return object.to_f != 0
269 else
270 return object.size > 0
271 end
272 elsif object.kind_of? Array
273 object = object.find{|x| x and true}
274 end
275 return object ? true : false
276 end
277
278 # UNTESTED
279 def Functions::not( object )
280 not boolean( object )
281 end
282
283 # UNTESTED
284 def Functions::true( )
285 true
286 end
287
288 # UNTESTED
289 def Functions::false( )
290 false
291 end
292
293 # UNTESTED
294 def Functions::lang( language )
295 lang = false
296 node = @@context[:node]
297 attr = nil
298 until node.nil?
299 if node.node_type == :element
300 attr = node.attributes["xml:lang"]
301 unless attr.nil?
302 lang = compare_language(string(language), attr)
303 break
304 else
305 end
306 end
307 node = node.parent
308 end
309 lang
310 end
311
312 def Functions::compare_language lang1, lang2
313 lang2.downcase.index(lang1.downcase) == 0
314 end
315
316 # a string that consists of optional whitespace followed by an optional
317 # minus sign followed by a Number followed by whitespace is converted to
318 # the IEEE 754 number that is nearest (according to the IEEE 754
319 # round-to-nearest rule) to the mathematical value represented by the
320 # string; any other string is converted to NaN
321 #
322 # boolean true is converted to 1; boolean false is converted to 0
323 #
324 # a node-set is first converted to a string as if by a call to the string
325 # function and then converted in the same way as a string argument
326 #
327 # an object of a type other than the four basic types is converted to a
328 # number in a way that is dependent on that type
329 def Functions::number( object=nil )
330 object = @@context[:node] unless object
331 case object
332 when true
333 Float(1)
334 when false
335 Float(0)
336 when Array
337 number(string( object ))
338 when Numeric
339 object.to_f
340 else
341 str = string( object )
342 #puts "STRING OF #{object.inspect} = #{str}"
343 # If XPath ever gets scientific notation...
344 #if str =~ /^\s*-?(\d*\.?\d+|\d+\.)([Ee]\d*)?\s*$/
345 if str =~ /^\s*-?(\d*\.?\d+|\d+\.)\s*$/
346 str.to_f
347 else
348 (0.0 / 0.0)
349 end
350 end
351 end
352
353 def Functions::sum( nodes )
354 nodes = [nodes] unless nodes.kind_of? Array
355 nodes.inject(0) { |r,n| r += number(string(n)) }
356 end
357
358 def Functions::floor( number )
359 number(number).floor
360 end
361
362 def Functions::ceiling( number )
363 number(number).ceil
364 end
365
366 def Functions::round( number )
367 begin
368 number(number).round
369 rescue FloatDomainError
370 number(number)
371 end
372 end
373
374 def Functions::processing_instruction( node )
375 node.node_type == :processing_instruction
376 end
377
378 def Functions::method_missing( id )
379 puts "METHOD MISSING #{id.id2name}"
380 XPath.match( @@context[:node], id.id2name )
381 end
382 end
383end
Note: See TracBrowser for help on using the repository browser.