| Module | REXML::Functions |
| In: |
|
If you add a method, keep in mind two things: (1) the first argument will always be a list of nodes from which to filter. In the case of context methods (such as position), the function should return an array with a value for each child in the array. (2) all method calls from XML will have "-" replaced with "_". Therefore, in XML, "local-name()" is identical (and actually becomes) "local_name()"
Methods
Public Class methods
UNTESTED
255: def Functions::boolean( object=nil ) 256: if object.kind_of? String 257: if object =~ /\d+/u 258: return object.to_f != 0 259: else 260: return object.size > 0 261: end 262: elsif object.kind_of? Array 263: object = object.find{|x| x and true} 264: end 265: return object ? true : false 266: end
302: def Functions::compare_language lang1, lang2 303: lang2.downcase.index(lang1.downcase) == 0 304: end
Fixed by Mike Stok
142: def Functions::contains( string, test ) 143: string(string).include? string(test) 144: end
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.namespace 71: yield node_set 72: else 73: return unless node_set.kind_of? Enumerable 74: node_set.each { |node| yield node if defined? node.namespace } 75: end 76: end 77: end
UNTESTED
284: def Functions::lang( language ) 285: lang = false 286: node = @@context[:node] 287: attr = nil 288: until node.nil? 289: if node.node_type == :element 291: unless attr.nil? 292: lang = compare_language(string(language), attr) 293: break 294: else 295: end 296: end 297: node = node.parent 298: end 299: lang 300: end
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
366: def Functions::method_missing( id ) 367: puts "METHOD MISSING #{id.id2name}" 368: XPath.match( @@context[:node], id.id2name ) 369: end
59: def Functions::name( node_set=nil ) 60: get_namespace( node_set ) do |node| 61: node.expanded_name 62: end 63: end
55: def Functions::namespace_uri( node_set=nil ) 56: get_namespace( node_set ) {|node| node.namespace} 57: end
UNTESTED
208: def Functions::normalize_space( string=nil ) 209: string = string(@@context[:node]) if string.nil? 210: if string.kind_of? Array 211: string.collect{|x| string.to_s.strip.gsub(/\s+/um, ' ') if string} 212: else 213: string.to_s.strip.gsub(/\s+/um, ' ') 214: end 215: end
a string that consists of optional whitespace followed by an optional minus sign followed by a Number followed by whitespace is converted to the IEEE 754 number that is nearest (according to the IEEE 754 round-to-nearest rule) to the mathematical value represented by the string; any other string is converted to NaN
boolean true is converted to 1; boolean false is converted to 0
a node-set is first converted to a string as if by a call to the string function and then converted in the same way as a string argument
an object of a type other than the four basic types is converted to a number in a way that is dependent on that type
319: def Functions::number( object=nil ) 320: object = @@context[:node] unless object 321: case object 322: when true 323: Float(1) 324: when false 325: Float(0) 326: when Array 327: number(string( object )) 328: when Numeric 329: object.to_f 330: else 331: str = string( object ) 332: #puts "STRING OF #{object.inspect} = #{str}" 333: if str =~ /^\d+/ 334: object.to_s.to_f 335: else 336: (0.0 / 0.0) 337: end 338: end 339: end
362: def Functions::processing_instruction( node ) 363: node.node_type == :processing_instruction 364: end
354: def Functions::round( number ) 355: begin 356: number(number).round 357: rescue FloatDomainError 358: number(number) 359: end 360: end
Fixed by Mike Stok
137: def Functions::starts_with( string, test ) 138: string(string).index(string(test)) == 0 139: end
A node-set is converted to a string by returning the string-value of the node in the node-set that is first in document order. If the node-set is empty, an empty string is returned.
A number is converted to a string as follows
NaN is converted to the string NaN
positive zero is converted to the string 0
negative zero is converted to the string 0
positive infinity is converted to the string Infinity
negative infinity is converted to the string -Infinity
if the number is an integer, the number is represented in decimal form as a Number with no decimal point and no leading zeros, preceded by a minus sign (-) if the number is negative
otherwise, the number is represented in decimal form as a Number including a decimal point with at least one digit before the decimal point and at least one digit after the decimal point, preceded by a minus sign (-) if the number is negative; there must be no leading zeros before the decimal point apart possibly from the one required digit immediately before the decimal point; beyond the one required digit after the decimal point there must be as many, but only as many, more digits as are needed to uniquely distinguish the number from all other IEEE 754 numeric values.
The boolean false value is converted to the string false. The boolean true value is converted to the string true.
An object of a type other than the four basic types is converted to a string in a way that is dependent on that type.
114: def Functions::string( object=nil ) 115: #object = @context unless object 116: if object.instance_of? Array 117: string( object[0] ) 118: elsif defined? object.node_type 119: if object.node_type == :attribute 120: object.value 121: elsif object.node_type == :element 122: object.text 123: else 124: object.to_s 125: end 126: else 127: object.to_s 128: end 129: end
Take equal portions of Mike Stok and Sean Russell; mix vigorously, and pour into a tall, chilled glass. Serves 10,000.
170: def Functions::substring( string, start, length=nil ) 171: ruby_string = string(string) 172: ruby_length = if length.nil? 173: ruby_string.length.to_f 174: else 175: number(length) 176: end 177: ruby_start = number(start) 178: 179: # Handle the special cases 180: return '' if ( 181: ruby_length.nan? or 182: ruby_start.nan? or 183: ruby_start.infinite? 184: ) 185: 186: infinite_length = ruby_length.infinite? == 1 187: ruby_length = ruby_string.length if infinite_length 188: 189: # Now, get the bounds. The XPath bounds are 1..length; the ruby bounds 190: # are 0..length. Therefore, we have to offset the bounds by one. 191: ruby_start = ruby_start.round - 1 192: ruby_length = ruby_length.round 193: 194: if ruby_start < 0 195: ruby_length += ruby_start unless infinite_length 196: ruby_start = 0 197: end 198: return '' if ruby_length <= 0 199: ruby_string[ruby_start,ruby_length] 200: end
Kouhei fixed this too
158: def Functions::substring_after( string, test ) 159: ruby_string = string(string) 160: ruby_index = ruby_string.index(string(test)) 161: if ruby_index.nil? 162: "" 163: else 164: ruby_string[ ruby_index+1..-1 ] 165: end 166: end
Kouhei fixed this
147: def Functions::substring_before( string, test ) 148: ruby_string = string(string) 149: ruby_index = ruby_string.index(string(test)) 150: if ruby_index.nil? 151: "" 152: else 153: ruby_string[ 0...ruby_index ] 154: end 155: end
341: def Functions::sum( nodes ) 342: nodes = [nodes] unless nodes.kind_of? Array 343: nodes.inject(0) { |r,n| r += number(string(n)) } 344: end
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
This is entirely Mike Stok’s beast
218: def Functions::translate( string, tr1, tr2 ) 219: from = string(tr1) 220: to = string(tr2) 221: 222: # the map is our translation table. 223: # 224: # if a character occurs more than once in the 225: # from string then we ignore the second & 226: # subsequent mappings 227: # 228: # if a charactcer maps to nil then we delete it 229: # in the output. This happens if the from 230: # string is longer than the to string 231: # 232: # there's nothing about - or ^ being special in 233: # http://www.w3.org/TR/xpath#function-translate 234: # so we don't build ranges or negated classes 235: 236: map = Hash.new 237: 0.upto(from.length - 1) { |pos| 238: from_char = from[pos] 239: unless map.has_key? from_char 240: map[from_char] = 241: if pos < to.length 242: to[pos] 243: else 244: nil 245: end 246: end 247: } 248: 249: string(string).unpack('U*').collect { |c| 250: if map.has_key? c then map[c] else c end 251: }.compact.pack('U*') 252: end