require_relative 's9api'
module Saxon
# Represent XDM types abstractly
class ItemType
# A mapping of Ruby types to XDM type constants
TYPE_MAPPING = {
'String' => :STRING,
'Array' => :ANY_ARRAY,
'Hash' => :ANY_MAP,
'TrueClass' => :BOOLEAN,
'FalseClass' => :BOOLEAN,
'Date' => :DATE,
'DateTime' => :DATE_TIME,
'Time' => :DATE_TIME,
'BigDecimal' => :DECIMAL,
'Integer' => :INTEGER,
'Float' => :FLOAT,
'Numeric' => :NUMERIC
}.freeze
# A mapping of type names/QNames to XDM type constants
STR_MAPPING = {
'array(*)' => :ANY_ARRAY,
'xs:anyAtomicType' => :ANY_ATOMIC_VALUE,
'item()' => :ANY_ITEM,
'map(*)' => :ANY_MAP,
'node()' => :ANY_NODE,
'xs:anyURI' => :ANY_URI,
'xs:base64Binary' => :BASE64_BINARY,
'xs:boolean' => :BOOLEAN,
'xs:byte' => :BYTE,
'xs:date' => :DATE,
'xs:dateTime' => :DATE_TIME,
'xs:dateTimeStamp' => :DATE_TIME_STAMP,
'xs:dayTimeDuration' => :DAY_TIME_DURATION,
'xs:decimal' => :DECIMAL,
'xs:double' => :DOUBLE,
'xs:duration' => :DURATION,
'xs:ENTITY' => :ENTITY,
'xs:float' => :FLOAT,
'xs:gDay' => :G_DAY,
'xs:gMonth' => :G_MONTH,
'xs:gMonthDay' => :G_MONTH_DAY,
'xs:gYear' => :G_YEAR,
'xs:gYearMonth' => :G_YEAR_MONTH,
'xs:hexBinary' => :HEX_BINARY,
'xs:ID' => :ID,
'xs:IDREF' => :IDREF,
'xs:int' => :INT,
'xs:integer' => :INTEGER,
'xs:language' => :LANGUAGE,
'xs:long' => :LONG,
'xs:Name' => :NAME,
'xs:NCName' => :NCNAME,
'xs:negativeInteger' => :NEGATIVE_INTEGER,
'xs:NMTOKEN' => :NMTOKEN,
'xs:nonNegativeInteger' => :NON_NEGATIVE_INTEGER,
'xs:nonPositiveInteger' => :NON_POSITIVE_INTEGER,
'xs:normalizedString' => :NORMALIZED_STRING,
'xs:NOTATION' => :NOTATION,
'xs:numeric' => :NUMERIC,
'xs:positiveInteger' => :POSITIVE_INTEGER,
'xs:QName' => :QNAME,
'xs:short' => :SHORT,
'xs:string' => :STRING,
'xs:time' => :TIME,
'xs:token' => :TOKEN,
'xs:unsignedByte' => :UNSIGNED_BYTE,
'xs:unsignedInt' => :UNSIGNED_INT,
'xs:unsignedLong' => :UNSIGNED_LONG,
'xs:unsignedShort' => :UNSIGNED_SHORT,
'xs:untypedAtomic' => :UNTYPED_ATOMIC,
'xs:yearMonthDuration' => :YEAR_MONTH_DURATION
}.freeze
class << self
# Get an appropriate {ItemType} for a Ruby type or given a type name as a
# string
#
# @return [Saxon::ItemType]
# @overload get_type(ruby_class)
# Get an appropriate {ItemType} for object of a given Ruby class
# @param ruby_class [Class] The Ruby class to get a type for
# @overload get_type(type_name)
# Get the {ItemType} for the name
# @param type_name [String] name of the built-in {ItemType} to fetch
def get_type(arg)
new(get_s9_type(arg))
end
private
def get_s9_type(arg)
case arg
when Class
get_s9_class_mapped_type(arg)
when String
get_s9_str_mapped_type(arg)
end
end
def get_s9_class_mapped_type(klass)
class_name = klass.name
if mapped_type = TYPE_MAPPING.fetch(class_name, false)
S9API::ItemType.const_get(mapped_type)
else
raise UnmappedRubyTypeError, class_name
end
end
def get_s9_str_mapped_type(type_str)
if mapped_type = STR_MAPPING.fetch(type_str, false)
# ANY_ITEM is a method, not a constant, for reasons not entirely
# clear to me
return S9API::ItemType.ANY_ITEM if mapped_type == :ANY_ITEM
S9API::ItemType.const_get(mapped_type)
else
raise UnmappedXSDTypeNameError, type_str
end
end
end
attr_reader :s9_item_type
private :s9_item_type
# @api private
def initialize(s9_item_type)
@s9_item_type = s9_item_type
end
# Return the {QName} which represents this type
#
# @return [Saxon::QName] the {QName} of the type
def type_name
@type_name ||= Saxon::QName.new(s9_item_type.getTypeName)
end
# @return [Saxon::S9API::ItemType] The underlying Saxon Java ItemType object
def to_java
s9_item_type
end
# compares two {ItemType}s using the underlying Saxon and XDM comparision rules
# @param other [Saxon::ItemType]
# @return [Boolean]
def ==(other)
return false unless other.is_a?(ItemType)
s9_item_type.equals(other.to_java)
end
alias_method :eql?, :==
def hash
@hash ||= s9_item_type.hashCode
end
# Error raised when a Ruby class has no equivalent XDM type to be converted
# into
class UnmappedRubyTypeError < StandardError
def initialize(class_name)
@class_name = class_name
end
def to_s
"Ruby class <#{@class_name}> has no XDM type equivalent"
end
end
# Error raise when an attempt to reify an xs:* type string is
# made, but the type string doesn't match any of the built-in xs:*
# types
class UnmappedXSDTypeNameError < StandardError
def initialize(type_str)
@type_str = type_str
end
def to_s
"'#{@type_str}' is not recognised as an XSD built-in type"
end
end
class Factory
DEFAULT_SEMAPHORE = Mutex.new
attr_reader :processor
def initialize(processor)
@processor = processor
end
def s9_factory
return @s9_factory if instance_variable_defined?(:@s9_factory)
DEFAULT_SEMAPHORE.synchronize do
@s9_factory = S9API::ItemTypeFactory.new(processor.to_java)
end
end
end
end
end