fidothe/saxon-rb

View on GitHub
lib/saxon/item_type/value_to_ruby.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
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