lib/saxon/item_type/value_to_ruby.rb
require 'bigdecimal'
module Saxon
class ItemType
# A collection of lamba-like objects for converting XDM::AtomicValues into
# appropriate Ruby values
module ValueToRuby
# Regexp patterns to assist in converting XDM typed values into Ruby
# native types
module Patterns
# @see https://www.w3.org/TR/xmlschema-2/#dateTime-lexical-representation
DATE_TIME = /\A(-?[0-9]{4,})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]+)?)(Z|[-+][0-9]{2}:[0-9]{2})?\z/
end
# Helper class that converts XDM Date/Time strings into something Ruby's {Time} class can
# handle
class DateTimeConvertor
# Convert an XDM Date/Time value into a native Ruby {Time} object.
# @return [Time, String] the converted Time, or the original XDM lexical
# string if conversion isn't possible
def self.call(xdm_atomic_value)
new(xdm_atomic_value).convert
end
attr_reader :timestring, :match
def initialize(xdm_atomic_value)
@timestring = xdm_atomic_value.to_s
@match = ValueToRuby::Patterns::DATE_TIME.match(@timestring)
end
# Return a {Time} instance if possible, otherwise return the string value from the XDM.
# @return [Time, String] the converted Time, or the original string
def convert
return timestring if match.nil?
Time.new(*integer_parts, decimal_part, tz_part)
end
private
def integer_parts
match[1..5].map { |n| Integer(n, 10) }
end
def decimal_part
Float(match[6])
end
def tz_part
tz_string = match[7]
tz_string == 'Z' ? '+00:00' : tz_string
end
end
# A collection of Lambdas (or objects responding to +#call+) for
# converting XDM typed values to native Ruby types.
module Convertors
# @see https://www.w3.org/TR/xmlschema-2/#integer
INTEGER = INT = SHORT = LONG = UNSIGNED_INT = UNSIGNED_SHORT = UNSIGNED_LONG = \
POSITIVE_INTEGER = NON_POSITIVE_INTEGER = NEGATIVE_INTEGER = NON_NEGATIVE_INTEGER = ->(xdm_atomic_value) {
xdm_atomic_value.to_java.getValue
}
# @see https://www.w3.org/TR/xmlschema-2/#decimal
DECIMAL = ->(xdm_atomic_value) {
BigDecimal(xdm_atomic_value.to_s)
}
# @see https://www.w3.org/TR/xmlschema-2/#float
FLOAT = DOUBLE = ->(xdm_atomic_value) {
xdm_atomic_value.to_java.getValue
}
# @see https://www.w3.org/TR/xmlschema-2/#dateTime
DATE_TIME = DATE_TIME_STAMP = ValueToRuby::DateTimeConvertor
# @see https://www.w3.org/TR/xmlschema-2/#boolean
BOOLEAN = ->(xdm_atomic_value) {
xdm_atomic_value.to_java.getValue
}
# @see https://www.w3.org/TR/xmlschema-2/#NOTATION
# @see https://www.w3.org/TR/xmlschema-2/#QName
NOTATION = QNAME = ->(xdm_atomic_value) {
Saxon::QName.new(xdm_atomic_value.to_java.getQNameValue)
}
# @see https://www.w3.org/TR/xmlschema-2/#base64Binary
BASE64_BINARY = ->(xdm_atomic_value) {
Base64.decode64(xdm_atomic_value.to_s)
}
# @see https://www.w3.org/TR/xmlschema-2/#hexBinary
HEX_BINARY = ->(xdm_atomic_value) {
bytes = []
xdm_atomic_value.to_s.scan(/../) { |x| bytes << x.hex.chr(Encoding::ASCII_8BIT) }
bytes.join
}
# @see https://www.w3.org/TR/xmlschema-2/#byte
BYTE = ->(xdm_atomic_value) {
[xdm_atomic_value.to_java.getLongValue].pack('c')
}
# @see https://www.w3.org/TR/xmlschema-2/#unsignedByte
UNSIGNED_BYTE = ->(xdm_atomic_value) {
[xdm_atomic_value.to_java.getLongValue].pack('C')
}
end
end
end
end