/tools/Ruby/lib/ruby/1.8/rexml/functions.rb

http://github.com/agross/netopenspace · Ruby · 382 lines · 247 code · 42 blank · 93 comment · 42 complexity · f08943bf9c60a4d4099111eea878c94e MD5 · raw file

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