208 lines
6.6 KiB
Elixir
208 lines
6.6 KiB
Elixir
defmodule RDF.Literal.Datatype.Registry do
|
|
@moduledoc false
|
|
|
|
alias RDF.{Literal, IRI, XSD, Namespace}
|
|
alias RDF.Literal.Datatype.Registry.Registration
|
|
|
|
import RDF.Guards
|
|
import RDF.Utils.Guards
|
|
|
|
@primitive_numeric_datatypes [
|
|
RDF.XSD.Integer,
|
|
RDF.XSD.Decimal,
|
|
RDF.XSD.Double
|
|
]
|
|
|
|
@builtin_numeric_datatypes @primitive_numeric_datatypes ++ [
|
|
RDF.XSD.Long,
|
|
RDF.XSD.Int,
|
|
RDF.XSD.Short,
|
|
RDF.XSD.Byte,
|
|
RDF.XSD.NonNegativeInteger,
|
|
RDF.XSD.PositiveInteger,
|
|
RDF.XSD.UnsignedLong,
|
|
RDF.XSD.UnsignedInt,
|
|
RDF.XSD.UnsignedShort,
|
|
RDF.XSD.UnsignedByte,
|
|
RDF.XSD.NonPositiveInteger,
|
|
RDF.XSD.NegativeInteger,
|
|
RDF.XSD.Float
|
|
]
|
|
|
|
@builtin_xsd_datatypes [
|
|
XSD.Boolean,
|
|
XSD.String,
|
|
XSD.Date,
|
|
XSD.Time,
|
|
XSD.DateTime,
|
|
XSD.AnyURI
|
|
] ++ @builtin_numeric_datatypes
|
|
|
|
@builtin_datatypes [RDF.LangString | @builtin_xsd_datatypes]
|
|
|
|
@doc """
|
|
Returns a list of all builtin `RDF.Literal.Datatype` modules.
|
|
"""
|
|
@spec builtin_datatypes :: [RDF.Literal.Datatype.t]
|
|
def builtin_datatypes, do: @builtin_datatypes
|
|
|
|
@doc """
|
|
Checks if the given module is a builtin datatype.
|
|
|
|
Note: This doesn't include `RDF.Literal.Generic`.
|
|
"""
|
|
@spec builtin_datatype?(module) :: boolean
|
|
def builtin_datatype?(module)
|
|
|
|
for datatype <- @builtin_datatypes do
|
|
def builtin_datatype?(unquote(datatype)), do: true
|
|
end
|
|
|
|
def builtin_datatype?(_), do: false
|
|
|
|
@doc """
|
|
Checks if the given module is a builtin datatype or a registered custom datatype implementing the `RDF.Literal.Datatype` behaviour.
|
|
"""
|
|
@spec datatype?(Literal.t | Literal.Datatype.literal | module) :: boolean
|
|
def datatype?(value)
|
|
|
|
# We assume literals were created properly which means they have a proper RDF.Literal.Datatype
|
|
def datatype?(%Literal{}), do: true
|
|
def datatype?(value), do: datatype_struct?(value)
|
|
|
|
@doc false
|
|
@spec datatype_struct?(Literal.Datatype.literal | module) :: boolean
|
|
def datatype_struct?(value)
|
|
|
|
def datatype_struct?(%datatype{}), do: datatype_struct?(datatype)
|
|
|
|
def datatype_struct?(Literal.Generic), do: true
|
|
|
|
def datatype_struct?(module) when maybe_module(module) do
|
|
builtin_datatype?(module) or is_rdf_literal_datatype?(module)
|
|
end
|
|
|
|
def datatype_struct?(_), do: false
|
|
|
|
@doc """
|
|
Returns a list of all builtin `RDF.XSD.Datatype` modules.
|
|
"""
|
|
@spec builtin_xsd_datatypes :: [RDF.Literal.Datatype.t]
|
|
def builtin_xsd_datatypes, do: @builtin_xsd_datatypes
|
|
|
|
@doc false
|
|
@spec builtin_xsd_datatype?(module) :: boolean
|
|
def builtin_xsd_datatype?(module)
|
|
|
|
for datatype <- @builtin_xsd_datatypes do
|
|
def builtin_xsd_datatype?(unquote(datatype)), do: true
|
|
end
|
|
|
|
def builtin_xsd_datatype?(_), do: false
|
|
|
|
@doc """
|
|
Checks if the given module is a builtin XSD datatype or a registered custom datatype implementing the `RDF.XSD.Datatype` behaviour.
|
|
"""
|
|
@spec xsd_datatype?(Literal.t | XSD.Datatype.literal | module) :: boolean
|
|
def xsd_datatype?(value)
|
|
def xsd_datatype?(%Literal{literal: datatype_struct}), do: xsd_datatype?(datatype_struct)
|
|
def xsd_datatype?(value), do: xsd_datatype_struct?(value)
|
|
|
|
@doc false
|
|
@spec xsd_datatype_struct?(RDF.Literal.t() | XSD.Datatype.literal | module) :: boolean
|
|
def xsd_datatype_struct?(value)
|
|
|
|
def xsd_datatype_struct?(%datatype{}), do: xsd_datatype_struct?(datatype)
|
|
|
|
def xsd_datatype_struct?(module) when maybe_module(module) do
|
|
builtin_xsd_datatype?(module) or is_xsd_datatype?(module)
|
|
end
|
|
|
|
def xsd_datatype_struct?(_), do: false
|
|
|
|
@doc """
|
|
Returns a list of all numeric datatype modules.
|
|
"""
|
|
@spec builtin_numeric_datatypes() :: [RDF.Literal.Datatype.t]
|
|
def builtin_numeric_datatypes(), do: @builtin_numeric_datatypes
|
|
|
|
@doc """
|
|
The set of all primitive numeric datatypes.
|
|
"""
|
|
@spec primitive_numeric_datatypes() :: [RDF.Literal.Datatype.t]
|
|
def primitive_numeric_datatypes(), do: @primitive_numeric_datatypes
|
|
|
|
@doc false
|
|
@spec builtin_numeric_datatype?(module) :: boolean
|
|
def builtin_numeric_datatype?(module)
|
|
|
|
for datatype <- @builtin_numeric_datatypes do
|
|
def builtin_numeric_datatype?(unquote(datatype)), do: true
|
|
end
|
|
|
|
def builtin_numeric_datatype?(_), do: false
|
|
|
|
|
|
@doc """
|
|
Returns if a given literal or datatype has or is a numeric datatype.
|
|
"""
|
|
@spec numeric_datatype?(RDF.Literal.t() | RDF.XSD.Datatype.t() | any) :: boolean
|
|
def numeric_datatype?(literal)
|
|
def numeric_datatype?(%RDF.Literal{literal: literal}), do: numeric_datatype?(literal)
|
|
def numeric_datatype?(%datatype{}), do: numeric_datatype?(datatype)
|
|
|
|
def numeric_datatype?(datatype) when maybe_module(datatype) do
|
|
builtin_numeric_datatype?(datatype) or (
|
|
xsd_datatype?(datatype) and
|
|
Enum.any?(@primitive_numeric_datatypes, fn numeric_primitive ->
|
|
datatype.derived_from?(numeric_primitive)
|
|
end)
|
|
)
|
|
end
|
|
|
|
def numeric_datatype?(_), do: false
|
|
|
|
@doc """
|
|
Returns the `RDF.Literal.Datatype` for a datatype IRI.
|
|
"""
|
|
@spec datatype(Literal.t | IRI.t | String.t) :: Literal.Datatype.t
|
|
def datatype(%Literal{} = literal), do: Literal.datatype(literal)
|
|
def datatype(%IRI{} = id), do: id |> to_string() |> datatype()
|
|
def datatype(id) when maybe_ns_term(id), do: id |> Namespace.resolve_term!() |> datatype()
|
|
def datatype(id) when is_binary(id), do: Registration.datatype(id)
|
|
|
|
@doc """
|
|
Returns the `RDF.XSD.Datatype` for a datatype IRI.
|
|
"""
|
|
@spec xsd_datatype(Literal.t | IRI.t | String.t) :: XSD.Datatype.t
|
|
def xsd_datatype(id) do
|
|
datatype = datatype(id)
|
|
|
|
if datatype && is_xsd_datatype?(datatype) do
|
|
datatype
|
|
end
|
|
end
|
|
|
|
# TODO: Find a better/faster solution for checking datatype modules which includes unknown custom datatypes.
|
|
# Although checking for the presence of a function via __info__(:functions) is
|
|
# the fastest way to reflect a module type on average over the positive and negative
|
|
# case (being roughly comparable to a map access), we would still have to rescue
|
|
# from an UndefinedFunctionError since its raised by trying to access __info__
|
|
# on plain (non-module) atoms, so we can do the check by rescueing in the first place.
|
|
# Although the positive is actually faster than the __info__(:functions) check,
|
|
# the negative is more than 7 times slower.
|
|
# (Properly checking for the behaviour attribute with module_info[:attributes]
|
|
# is more than 200 times slower.)
|
|
|
|
defp is_rdf_literal_datatype?(module) do
|
|
module.__rdf_literal_datatype_indicator__()
|
|
rescue
|
|
UndefinedFunctionError -> false
|
|
end
|
|
|
|
defp is_xsd_datatype?(module) do
|
|
module.__xsd_datatype_indicator__()
|
|
rescue
|
|
UndefinedFunctionError -> false
|
|
end
|
|
end
|