fidothe/saxon-rb

View on GitHub
lib/saxon/xdm/value.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
97%
require_relative '../s9api'
require_relative 'sequence_like'

module Saxon
  module XDM
    # An XPath Data Model Value object, representing a Sequence.
    class Value
      include XDM::SequenceLike
      include Enumerable

      class << self
        # Create a new XDM::Value sequence containing the items passed in as a Ruby enumerable.
        #
        # @param items [Enumerable] A list of members
        # @return [Saxon::XDM::Value] The XDM value
        def create(*items)
          items = items.flatten
          case items.size
          when 0
            XDM.EmptySequence()
          when 1
            if value = maybe_xdm_value(items.first)
              return value
            end
            XDM.Item(items.first)
          else
            new(Saxon::S9API::XdmValue.new(wrap_items(items)))
          end
        end

        private

        def maybe_xdm_value(value)
          return value if value.is_a?(self)
          return value if value === XDM.EmptySequence()
          return XDM.EmptySequence() if value.instance_of?(Saxon::S9API::XdmEmptySequence)
          return check_for_empty_or_single_item_value(value) if value.instance_of?(Saxon::S9API::XdmValue)
          false
        end

        def check_for_empty_or_single_item_value(s9_value)
          case s9_value.size
          when 0
            XDM.EmptySequence()
          when 1
            XDM.Item(s9_value.item_at(0))
          else
            new(s9_value)
          end
        end

        def wrap_items(items)
          result = []
          items.map { |item|
            wrap_item(item, result)
          }
          result
        end

        def wrap_item(item, result)
          if item.respond_to?(:sequence_enum)
            item.sequence_enum.each do |item|
              result << item.to_java
            end
          elsif item.respond_to?(:each)
            item.each do |item|
              result << XDM.Item(item).to_java
            end
          else
            result << XDM.Item(item).to_java
          end
        end
      end

      attr_reader :s9_xdm_value
      private :s9_xdm_value

      # @api private
      def initialize(s9_xdm_value)
        @s9_xdm_value = s9_xdm_value
      end

      # @return [Fixnum] The size of the sequence
      def size
        s9_xdm_value.size
      end

      # Calls the given block once for each Item in the sequence, passing that
      # item as a parameter. Returns the value itself.
      #
      # If no block is given, an Enumerator is returned.
      #
      # @overload
      #   @yield [item] The current XDM Item
      #   @yieldparam item [Saxon::XDM::AtomicValue, Saxon::XDM::Node] the item.
      def each(&block)
        to_enum.each(&block)
      end

      # @return [Saxon::S9API::XdmValue] The underlying Saxon Java XDM valuee object.
      def to_java
        @s9_xdm_value
      end

      # Compare this XDM::Value with another. Currently this requires iterating
      # across the sequence, and the other sequence and comparing each member
      # with the corresponding member in the other sequence.
      #
      # @param other [Saxon::XDM::Value] The XDM::Value to be compare against
      # @return [Boolean] whether the two XDM::Values are equal
      def ==(other)
        return false unless other.is_a?(XDM::Value)
        return false unless other.size == size
        not_okay = to_enum.zip(other.to_enum).find { |mine, theirs|
          mine != theirs
        }
        not_okay.nil? || !not_okay
      end

      alias_method :eql?, :==

      # The hash code for the XDM::Value
      # @return [Fixnum] The hash code
      def hash
        @hash ||= to_a.hash
      end

      # Returns a lazy Enumerator over the sequence
      # @return [Enumerator::Lazy] the enumerator
      def to_enum
        s9_xdm_value.enum_for(:each).lazy.map { |s9_xdm_item|
          XDM.Item(s9_xdm_item)
        }.each
      end

      alias_method :enum_for, :to_enum

      # Returns an enumerator over the Sequence
      def sequence_enum
        to_enum
      end

      # @return [Integer] the size of the sequence
      def sequence_size
        s9_xdm_value.size
      end
    end

    # Placeholder class for Saxon Items that we haven't gotten to yet
    class XDM::UnhandledItem
      def initialize(s9_xdm_item)
        @s9_xdm_item = s9_xdm_item
      end

      # Return the underlying s9api XdmItem
      def to_java
        @s9_xdm_item
      end
    end
  end
end