fidothe/saxon-rb

View on GitHub
lib/saxon/sequence_type.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
94%
require_relative './item_type'
require_relative './occurrence_indicator'

module Saxon
  # Represents a type definition for an XDM Sequence: an {ItemType} plus an
  # {OccurrenceIndicator} as a restriction on the cardinality (length) of the
  # sequence. Used most often to define variables in XPath or XSLT, plus in
  # extension function definition.
  class SequenceType
    class << self
      # Generate a {SequenceType} from a type declaration string (see
      # {.from_type_decl}), or some combination of type name/Ruby class (see
      # {ItemType.get_type}), and an {OccurrenceIndicator} or symbol referencing
      # an OccurenceIndicator (one of +:zero_or_more+, +:one_or_more+,
      # +:zero_or_one+, or +:one+)
      #
      # @return [Saxon::SequenceType] the resulting SequenceType
      def create(type_name, occurrence_indicator = nil)
        case type_name
        when SequenceType
          return type_name
        when S9API::SequenceType
          return new(type_name)
        else
          check_for_complete_decl!(type_name, occurrence_indicator)
          return from_type_decl(type_name) if type_name.is_a?(String) && occurrence_indicator.nil?
          item_type = ItemType.get_type(type_name)
          occurrence_indicator = OccurrenceIndicator.get_indicator(occurrence_indicator)
          new(item_type, occurrence_indicator)
        end
      end

      # Generate a {SequenceType} from a declaration string following the rules
      # of parameter and function +as=+ declarations in XSLT, like
      # <tt><xsl:variable ... as="xs:string+"/></tt>
      #
      # @param type_decl [String] the declaration string
      # @return [Saxon::SequenceType] the resulting SequenceType
      def from_type_decl(type_decl)
        occurence_char = type_decl[-1]
        occurence = case occurence_char
        when '?'
          new(ItemType.get_type(type_decl[0..-2]), OccurrenceIndicator.zero_or_one)
        when '+'
          new(ItemType.get_type(type_decl[0..-2]), OccurrenceIndicator.one_or_more)
        when '*'
          new(ItemType.get_type(type_decl[0..-2]), OccurrenceIndicator.zero_or_more)
        else
          new(ItemType.get_type(type_decl), OccurrenceIndicator.one)
        end
      end

      private

      def check_for_complete_decl!(type_name, occurrence_indicator)
        return true if occurrence_indicator.nil?
        if type_name_is_complete_decl?(type_name)
          raise ArgumentError, "Cannot pass a complete type declaration (#{type_name}) and an OccurrenceIndicator"
        end
      end

      def type_name_is_complete_decl?(type_name)
        !!(type_name.is_a?(String) && type_name.match(/[+*?]$/))
      end
    end

    # @overload initialize(item_type, occurrence_indicator)
    #   creates new instance using item type and occurrence indicator
    #   @param item_type [Saxon::ItemType] the sequence's item type
    #   @param occurrence_indicator [net.sf.saxon.s9api.OccurrenceIndicator] the
    #     occurrence indicator (cardinality) of the sequence
    # @overload initialize(s9_sequence_type)
    #   create a new instance by wrapping one of Saxon's underlying Java
    #   +SequenceType+s
    #   @param s9_sequence_type [net.sf.saxon.s9api.SequenceType]
    # @return [Saxon::SequenceType] the new SequenceType
    def initialize(item_type, occurrence_indicator = nil)
      if occurrence_indicator.nil? && !item_type.is_a?(S9API::SequenceType)
        raise ArgumentError, "Expected a Java s9api.SequenceType when handed a single argument, but got a #{item_type.class}"
      end

      if occurrence_indicator.nil?
        @s9_sequence_type = item_type
      else
        @item_type = item_type
        @occurrence_indicator = occurrence_indicator
      end
    end

    # @return [Saxon::ItemType] the type's ItemType
    def item_type
      @item_type ||= Saxon::ItemType.get_type(s9_sequence_type.getItemType)
    end

    # @return [net.sf.saxon.s9api.OccurrenceIndicator] the type's OccurrenceIndicator
    def occurrence_indicator
      @occurrence_indicator ||= s9_sequence_type.getOccurrenceIndicator
    end

    # @return [net.sf.saxon.s9api.SequenceType] the underlying Saxon SequenceType
    def to_java
      s9_sequence_type
    end

    # Compare equal with another SequenceType
    # @param other [Saxon::SequenceType]
    # @return [Boolean] the result of comparing +self+ and +other+
    def ==(other)
      return false if other.class != self.class
      item_type == other.item_type && occurrence_indicator == other.occurrence_indicator
    end
    alias_method :eql?, :==

    # Generated hash code for use as keys in Hashes
    def hash
      @hash ||= item_type.hash ^ occurrence_indicator.hash
    end

    private

    def s9_sequence_type
      @s9_sequence_type ||= S9API::SequenceType.makeSequenceType(item_type.to_java, occurrence_indicator.to_java)
    end
  end

  # Convenience wrapper for {SequenceType.create}
  # @see SequenceType.create
  def self.SequenceType(*args)
    SequenceType.create(*args)
  end
end