Redesign datatype reflection API
This commit is contained in:
parent
042ff1c1b8
commit
fa35b65d9f
23 changed files with 431 additions and 233 deletions
|
@ -207,11 +207,15 @@ defmodule RDF do
|
||||||
defdelegate bnode(), to: BlankNode, as: :new
|
defdelegate bnode(), to: BlankNode, as: :new
|
||||||
defdelegate bnode(id), to: BlankNode, as: :new
|
defdelegate bnode(id), to: BlankNode, as: :new
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Checks if the given value is a RDF literal.
|
||||||
|
"""
|
||||||
|
def literal?(%Literal{}), do: true
|
||||||
|
def literal?(_), do: false
|
||||||
|
|
||||||
defdelegate literal(value), to: Literal, as: :new
|
defdelegate literal(value), to: Literal, as: :new
|
||||||
defdelegate literal(value, opts), to: Literal, as: :new
|
defdelegate literal(value, opts), to: Literal, as: :new
|
||||||
|
|
||||||
defdelegate literal?(literal), to: Literal.Datatype.Registry
|
|
||||||
|
|
||||||
defdelegate triple(s, p, o), to: Triple, as: :new
|
defdelegate triple(s, p, o), to: Triple, as: :new
|
||||||
defdelegate triple(tuple), to: Triple, as: :new
|
defdelegate triple(tuple), to: Triple, as: :new
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
defmodule RDF.Guards do
|
defmodule RDF.Guards do
|
||||||
defguard maybe_ns_term(term)
|
import RDF.Utils.Guards
|
||||||
when is_atom(term) and term not in [nil, true, false]
|
|
||||||
|
defguard maybe_ns_term(term) when maybe_module(term)
|
||||||
end
|
end
|
||||||
|
|
|
@ -103,16 +103,16 @@ defmodule RDF.Literal do
|
||||||
def coerce(%NaiveDateTime{} = value), do: RDF.XSD.DateTime.new(value)
|
def coerce(%NaiveDateTime{} = value), do: RDF.XSD.DateTime.new(value)
|
||||||
def coerce(%URI{} = value), do: RDF.XSD.AnyURI.new(value)
|
def coerce(%URI{} = value), do: RDF.XSD.AnyURI.new(value)
|
||||||
|
|
||||||
# Although the following catch-all-clause for all structs could handle the core datatypes
|
# Although the following catch-all-clause for all structs could handle the builtin datatypes
|
||||||
# we're generating dedicated clauses for them here, as they are approx. 15% faster
|
# we're generating dedicated clauses for them here, as they are approx. 15% faster
|
||||||
Enum.each(Datatype.Registry.core_datatypes(), fn datatype ->
|
Enum.each(Datatype.Registry.builtin_datatypes(), fn datatype ->
|
||||||
def coerce(%unquote(datatype){} = datatype_literal) do
|
def coerce(%unquote(datatype){} = datatype_literal) do
|
||||||
%__MODULE__{literal: datatype_literal}
|
%__MODULE__{literal: datatype_literal}
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
def coerce(%_datatype{} = datatype_literal) do
|
def coerce(%datatype{} = datatype_literal) do
|
||||||
if Datatype.Registry.literal?(datatype_literal) do
|
if Datatype.Registry.datatype_struct?(datatype) do
|
||||||
%__MODULE__{literal: datatype_literal}
|
%__MODULE__{literal: datatype_literal}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -148,6 +148,23 @@ defmodule RDF.Literal do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns if the given value is a `RDF.Literal` or `RDF.Literal.Datatype` struct.
|
||||||
|
|
||||||
|
If you simply want to check for a `RDF.Literal` use pattern matching or `RDF.literal?/1`.
|
||||||
|
This function is a bit slower than those and most of the time only needed when
|
||||||
|
implementing `RDF.Literal.Datatype`s where you have to deal with the raw,
|
||||||
|
i.e. unwrapped `RDF.Literal.Datatype` structs.
|
||||||
|
"""
|
||||||
|
defdelegate datatype?(value), to: RDF.Literal.Datatype.Registry, as: :datatype?
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns if the literal uses the `RDF.Literal.Generic` datatype or on of the dedicated builtin or custom `RDF.Literal.Datatype`s.
|
||||||
|
"""
|
||||||
|
@spec generic?(t) :: boolean
|
||||||
|
def generic?(%__MODULE__{literal: %RDF.Literal.Generic{}}), do: true
|
||||||
|
def generic?(%__MODULE__{}), do: false
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns if a literal is a language-tagged literal.
|
Returns if a literal is a language-tagged literal.
|
||||||
|
|
||||||
|
@ -178,7 +195,7 @@ defmodule RDF.Literal do
|
||||||
"""
|
"""
|
||||||
@spec simple?(t) :: boolean
|
@spec simple?(t) :: boolean
|
||||||
def simple?(%__MODULE__{literal: %RDF.XSD.String{}}), do: true
|
def simple?(%__MODULE__{literal: %RDF.XSD.String{}}), do: true
|
||||||
def simple?(%__MODULE__{} = _), do: false
|
def simple?(%__MODULE__{}), do: false
|
||||||
|
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -192,11 +209,7 @@ defmodule RDF.Literal do
|
||||||
@spec plain?(t) :: boolean
|
@spec plain?(t) :: boolean
|
||||||
def plain?(%__MODULE__{literal: %RDF.XSD.String{}}), do: true
|
def plain?(%__MODULE__{literal: %RDF.XSD.String{}}), do: true
|
||||||
def plain?(%__MODULE__{literal: %LangString{}}), do: true
|
def plain?(%__MODULE__{literal: %LangString{}}), do: true
|
||||||
def plain?(%__MODULE__{} = _), do: false
|
def plain?(%__MODULE__{}), do: false
|
||||||
|
|
||||||
@spec typed?(t) :: boolean
|
|
||||||
def typed?(literal), do: not plain?(literal)
|
|
||||||
|
|
||||||
|
|
||||||
############################################################################
|
############################################################################
|
||||||
# functions delegating to the RDF.Literal.Datatype of a RDF.Literal
|
# functions delegating to the RDF.Literal.Datatype of a RDF.Literal
|
||||||
|
|
|
@ -34,6 +34,17 @@ defmodule RDF.Literal.Datatype do
|
||||||
"""
|
"""
|
||||||
@callback do_cast(literal | any) :: Literal.t() | nil
|
@callback do_cast(literal | any) :: Literal.t() | nil
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Checks if the given `RDF.Literal` has the datatype for which the `RDF.Literal.Datatype` is implemented or is derived from it.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
iex> RDF.XSD.byte(42) |> RDF.XSD.Integer.datatype?()
|
||||||
|
true
|
||||||
|
|
||||||
|
"""
|
||||||
|
@callback datatype?(Literal.t | t | literal) :: boolean
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
The datatype IRI of the given `RDF.Literal`.
|
The datatype IRI of the given `RDF.Literal`.
|
||||||
"""
|
"""
|
||||||
|
@ -139,6 +150,15 @@ defmodule RDF.Literal.Datatype do
|
||||||
|
|
||||||
defdelegate get(id), to: Literal.Datatype.Registry, as: :datatype
|
defdelegate get(id), to: Literal.Datatype.Registry, as: :datatype
|
||||||
|
|
||||||
|
@doc !"""
|
||||||
|
As opposed to RDF.Literal.valid?/1 this function operates on the datatype structs ...
|
||||||
|
|
||||||
|
It's meant for internal use only and doesn't perform checks if the struct
|
||||||
|
passed is actually a `RDF.Literal.Datatype` struct.
|
||||||
|
"""
|
||||||
|
def valid?(%datatype{} = datatype_literal), do: datatype.valid?(datatype_literal)
|
||||||
|
|
||||||
|
|
||||||
defmacro __using__(opts) do
|
defmacro __using__(opts) do
|
||||||
name = Keyword.fetch!(opts, :name)
|
name = Keyword.fetch!(opts, :name)
|
||||||
id = Keyword.fetch!(opts, :id)
|
id = Keyword.fetch!(opts, :id)
|
||||||
|
@ -157,6 +177,13 @@ defmodule RDF.Literal.Datatype do
|
||||||
quote do
|
quote do
|
||||||
@behaviour unquote(__MODULE__)
|
@behaviour unquote(__MODULE__)
|
||||||
|
|
||||||
|
@doc !"""
|
||||||
|
This function is just used to check if a module is a RDF.Literal.Datatype.
|
||||||
|
|
||||||
|
See `RDF.Literal.Datatype.Registry.is_rdf_literal_datatype?/1`.
|
||||||
|
"""
|
||||||
|
def __rdf_literal_datatype_indicator__, do: true
|
||||||
|
|
||||||
@name unquote(name)
|
@name unquote(name)
|
||||||
@impl unquote(__MODULE__)
|
@impl unquote(__MODULE__)
|
||||||
def name, do: @name
|
def name, do: @name
|
||||||
|
@ -165,6 +192,16 @@ defmodule RDF.Literal.Datatype do
|
||||||
@impl unquote(__MODULE__)
|
@impl unquote(__MODULE__)
|
||||||
def id, do: @id
|
def id, do: @id
|
||||||
|
|
||||||
|
# RDF.XSD.Datatypes offers another default implementation, but since it is
|
||||||
|
# still in a macro implementation defoverridable doesn't work
|
||||||
|
unless RDF.XSD.Datatype in @behaviour do
|
||||||
|
@impl unquote(__MODULE__)
|
||||||
|
def datatype?(%Literal{literal: literal}), do: datatype?(literal)
|
||||||
|
def datatype?(%datatype{}), do: datatype?(datatype)
|
||||||
|
def datatype?(__MODULE__), do: true
|
||||||
|
def datatype?(_), do: false
|
||||||
|
end
|
||||||
|
|
||||||
@impl unquote(__MODULE__)
|
@impl unquote(__MODULE__)
|
||||||
def datatype(%Literal{literal: literal}), do: datatype(literal)
|
def datatype(%Literal{literal: literal}), do: datatype(literal)
|
||||||
def datatype(%__MODULE__{}), do: @id
|
def datatype(%__MODULE__{}), do: @id
|
||||||
|
@ -227,8 +264,8 @@ defmodule RDF.Literal.Datatype do
|
||||||
def equal_value?(_, nil), do: nil
|
def equal_value?(_, nil), do: nil
|
||||||
def equal_value?(left, right) do
|
def equal_value?(left, right) do
|
||||||
cond do
|
cond do
|
||||||
not RDF.literal?(right) and not RDF.term?(right) -> equal_value?(left, Literal.coerce(right))
|
not Literal.datatype?(right) and not RDF.term?(right) -> equal_value?(left, Literal.coerce(right))
|
||||||
not RDF.literal?(left) and not RDF.term?(left) -> equal_value?(Literal.coerce(left), right)
|
not Literal.datatype?(left) and not RDF.term?(left) -> equal_value?(Literal.coerce(left), right)
|
||||||
true -> do_equal_value?(left, right)
|
true -> do_equal_value?(left, right)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -256,6 +293,9 @@ defmodule RDF.Literal.Datatype do
|
||||||
|> new()
|
|> new()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# This is a private RDF.Literal constructor, which should be used to build
|
||||||
|
# the RDF.Literals from the datatype literal structs instead of the
|
||||||
|
# RDF.Literal/new/1, to bypass the unnecessary datatype checks.
|
||||||
defp literal(datatype_literal), do: %Literal{literal: datatype_literal}
|
defp literal(datatype_literal), do: %Literal{literal: datatype_literal}
|
||||||
|
|
||||||
defoverridable datatype: 1,
|
defoverridable datatype: 1,
|
||||||
|
|
|
@ -5,34 +5,162 @@ defmodule RDF.Literal.Datatype.Registry do
|
||||||
alias RDF.Literal.Datatype.Registry.Registration
|
alias RDF.Literal.Datatype.Registry.Registration
|
||||||
|
|
||||||
import RDF.Guards
|
import RDF.Guards
|
||||||
|
import RDF.Utils.Guards
|
||||||
|
|
||||||
@core_datatypes [RDF.LangString | Enum.to_list(XSD.datatypes())]
|
@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 """
|
@doc """
|
||||||
All core `RDF.Literal.Datatype` modules.
|
Returns a list of all builtin `RDF.Literal.Datatype` modules.
|
||||||
"""
|
"""
|
||||||
@spec core_datatypes :: Enum.t
|
@spec builtin_datatypes :: [RDF.Literal.Datatype.t]
|
||||||
def core_datatypes, do: @core_datatypes
|
def builtin_datatypes, do: @builtin_datatypes
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Checks if the given module is core datatype.
|
Checks if the given module is a builtin datatype.
|
||||||
"""
|
|
||||||
@spec core_datatype?(module) :: boolean
|
|
||||||
def core_datatype?(module), do: module in @core_datatypes
|
|
||||||
|
|
||||||
@doc """
|
Note: This doesn't include `RDF.Literal.Generic`.
|
||||||
Checks if the given module is a core datatype or a registered custom datatype implementing the `RDF.Literal.Datatype` behaviour.
|
|
||||||
"""
|
"""
|
||||||
@spec datatype?(module) :: boolean
|
@spec builtin_datatype?(module) :: boolean
|
||||||
def datatype?(module) do
|
def builtin_datatype?(module)
|
||||||
core_datatype?(module) or implements_behaviour?(module, Literal.Datatype)
|
|
||||||
|
for datatype <- @builtin_datatypes do
|
||||||
|
def builtin_datatype?(unquote(datatype)), do: true
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec literal?(module) :: boolean
|
def builtin_datatype?(_), do: false
|
||||||
def literal?(%Literal{}), do: true
|
|
||||||
def literal?(%Literal.Generic{}), do: true
|
@doc """
|
||||||
def literal?(%datatype{}), do: datatype?(datatype)
|
Checks if the given module is a builtin datatype or a registered custom datatype implementing the `RDF.Literal.Datatype` behaviour.
|
||||||
def literal?(_), do: false
|
"""
|
||||||
|
@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 """
|
@doc """
|
||||||
Returns the `RDF.Literal.Datatype` for a datatype IRI.
|
Returns the `RDF.Literal.Datatype` for a datatype IRI.
|
||||||
|
@ -50,15 +178,31 @@ defmodule RDF.Literal.Datatype.Registry do
|
||||||
def xsd_datatype(id) do
|
def xsd_datatype(id) do
|
||||||
datatype = datatype(id)
|
datatype = datatype(id)
|
||||||
|
|
||||||
if datatype && implements_behaviour?(datatype, XSD.Datatype) do
|
if datatype && is_xsd_datatype?(datatype) do
|
||||||
datatype
|
datatype
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp implements_behaviour?(module, behaviour) do
|
# TODO: Find a better/faster solution for checking datatype modules which includes unknown custom datatypes.
|
||||||
module.module_info[:attributes]
|
# Although checking for the presence of a function via __info__(:functions) is
|
||||||
|> Keyword.get_values(:behaviour)
|
# the fastest way to reflect a module type on average over the positive and negative
|
||||||
|> List.flatten()
|
# case (being roughly comparable to a map access), we would still have to rescue
|
||||||
|> Enum.member?(behaviour)
|
# 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
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,6 +9,8 @@ defmodule RDF.LangString do
|
||||||
name: "langString",
|
name: "langString",
|
||||||
id: RDF.Utils.Bootstrapping.rdf_iri("langString")
|
id: RDF.Utils.Bootstrapping.rdf_iri("langString")
|
||||||
|
|
||||||
|
import RDF.Utils.Guards
|
||||||
|
|
||||||
alias RDF.Literal.Datatype
|
alias RDF.Literal.Datatype
|
||||||
alias RDF.Literal
|
alias RDF.Literal
|
||||||
|
|
||||||
|
@ -21,7 +23,7 @@ defmodule RDF.LangString do
|
||||||
@spec new(any, String.t | atom | keyword) :: Literal.t
|
@spec new(any, String.t | atom | keyword) :: Literal.t
|
||||||
def new(value, language_or_opts \\ [])
|
def new(value, language_or_opts \\ [])
|
||||||
def new(value, language) when is_binary(language), do: new(value, language: language)
|
def new(value, language) when is_binary(language), do: new(value, language: language)
|
||||||
def new(value, language) when is_atom(language), do: new(value, language: language)
|
def new(value, language) when is_ordinary_atom(language), do: new(value, language: language)
|
||||||
def new(value, opts) do
|
def new(value, opts) do
|
||||||
%Literal{
|
%Literal{
|
||||||
literal: %__MODULE__{
|
literal: %__MODULE__{
|
||||||
|
@ -33,7 +35,7 @@ defmodule RDF.LangString do
|
||||||
|
|
||||||
defp normalize_language(nil), do: nil
|
defp normalize_language(nil), do: nil
|
||||||
defp normalize_language(""), do: nil
|
defp normalize_language(""), do: nil
|
||||||
defp normalize_language(language) when is_atom(language), do: language |> to_string() |> normalize_language()
|
defp normalize_language(language) when is_ordinary_atom(language), do: language |> to_string() |> normalize_language()
|
||||||
defp normalize_language(language), do: String.downcase(language)
|
defp normalize_language(language), do: String.downcase(language)
|
||||||
|
|
||||||
@impl RDF.Literal.Datatype
|
@impl RDF.Literal.Datatype
|
||||||
|
|
6
lib/rdf/utils/guards.ex
Normal file
6
lib/rdf/utils/guards.ex
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
defmodule RDF.Utils.Guards do
|
||||||
|
defguard is_ordinary_atom(term)
|
||||||
|
when is_atom(term) and term not in [nil, true, false]
|
||||||
|
|
||||||
|
defguard maybe_module(term) when is_ordinary_atom(term)
|
||||||
|
end
|
|
@ -5,19 +5,9 @@ defmodule RDF.XSD do
|
||||||
see <https://www.w3.org/TR/xmlschema-2/>
|
see <https://www.w3.org/TR/xmlschema-2/>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
alias __MODULE__
|
import RDF.Utils.Guards
|
||||||
alias RDF.{IRI, Literal}
|
|
||||||
|
|
||||||
@datatypes [
|
alias __MODULE__
|
||||||
XSD.Boolean,
|
|
||||||
XSD.String,
|
|
||||||
XSD.Date,
|
|
||||||
XSD.Time,
|
|
||||||
XSD.DateTime,
|
|
||||||
XSD.AnyURI
|
|
||||||
]
|
|
||||||
|> MapSet.new()
|
|
||||||
|> MapSet.union(XSD.Numeric.datatypes())
|
|
||||||
|
|
||||||
@facets [
|
@facets [
|
||||||
XSD.Facets.MinInclusive,
|
XSD.Facets.MinInclusive,
|
||||||
|
@ -32,39 +22,15 @@ defmodule RDF.XSD do
|
||||||
|
|
||||||
@facets_by_name Map.new(@facets, fn facet -> {facet.name(), facet} end)
|
@facets_by_name Map.new(@facets, fn facet -> {facet.name(), facet} end)
|
||||||
|
|
||||||
def facet(name) when is_atom(name), do: @facets_by_name[to_string(name)]
|
def facet(name) when is_ordinary_atom(name), do: @facets_by_name[to_string(name)]
|
||||||
def facet(name), do: @facets_by_name[name]
|
def facet(name), do: @facets_by_name[name]
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
The list of all XSD datatypes.
|
Returns if the given value is a `RDF.XSD.Datatype` struct or `RDF.Literal` with a `RDF.XSD.Datatype`.
|
||||||
"""
|
"""
|
||||||
@spec datatypes() :: Enum.t()
|
defdelegate datatype?(value), to: RDF.Literal.Datatype.Registry, as: :xsd_datatype?
|
||||||
def datatypes(), do: @datatypes
|
|
||||||
|
|
||||||
@datatypes_by_name Map.new(@datatypes, fn datatype -> {datatype.name(), datatype} end)
|
for datatype <- RDF.Literal.Datatype.Registry.builtin_xsd_datatypes() do
|
||||||
@datatypes_by_iri Map.new(@datatypes, fn datatype -> {datatype.id(), datatype} end)
|
|
||||||
|
|
||||||
def datatype_by_name(name) when is_atom(name), do: @datatypes_by_name[to_string(name)]
|
|
||||||
def datatype_by_name(name), do: @datatypes_by_name[name]
|
|
||||||
def datatype_by_iri(iri) when is_binary(iri), do: @datatypes_by_iri[IRI.new(iri)]
|
|
||||||
def datatype_by_iri(%IRI{} = iri), do: @datatypes_by_iri[iri]
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Returns if a given datatype is a XSD datatype.
|
|
||||||
"""
|
|
||||||
def datatype?(datatype), do: datatype in @datatypes
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Returns if a given argument is a `RDF.XSD.datatype` literal.
|
|
||||||
"""
|
|
||||||
def literal?(%Literal{literal: %datatype{}}), do: datatype?(datatype)
|
|
||||||
def literal?(%datatype{}), do: datatype?(datatype)
|
|
||||||
def literal?(_), do: false
|
|
||||||
|
|
||||||
@doc false
|
|
||||||
def valid?(%datatype{} = datatype_literal), do: datatype.valid?(datatype_literal)
|
|
||||||
|
|
||||||
for datatype <- @datatypes do
|
|
||||||
defdelegate unquote(String.to_atom(datatype.name))(value), to: datatype, as: :new
|
defdelegate unquote(String.to_atom(datatype.name))(value), to: datatype, as: :new
|
||||||
defdelegate unquote(String.to_atom(datatype.name))(value, opts), to: datatype, as: :new
|
defdelegate unquote(String.to_atom(datatype.name))(value, opts), to: datatype, as: :new
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ defmodule RDF.XSD.Datatype do
|
||||||
|
|
||||||
@type comparison_result :: :lt | :gt | :eq
|
@type comparison_result :: :lt | :gt | :eq
|
||||||
|
|
||||||
|
import RDF.Utils.Guards
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns if the `RDF.XSD.Datatype` is a primitive datatype.
|
Returns if the `RDF.XSD.Datatype` is a primitive datatype.
|
||||||
|
@ -34,15 +35,13 @@ defmodule RDF.XSD.Datatype do
|
||||||
@callback base_primitive :: t()
|
@callback base_primitive :: t()
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Checks if a `RDF.XSD.Datatype` is directly or indirectly derived from another `RDF.XSD.Datatype`.
|
Checks if the `RDF.XSD.Datatype` is directly or indirectly derived from the given `RDF.XSD.Datatype`.
|
||||||
|
|
||||||
|
Note that this is just a basic datatype reflection function on the module level
|
||||||
|
and does not work with `RDF.Literal`s. See `c:RDF.Literal.Datatype.datatype?/1` instead.
|
||||||
"""
|
"""
|
||||||
@callback derived_from?(t()) :: boolean
|
@callback derived_from?(t()) :: boolean
|
||||||
|
|
||||||
@doc """
|
|
||||||
Checks if the datatype of a given literal is derived from a `RDF.XSD.Datatype`.
|
|
||||||
"""
|
|
||||||
@callback derived?(RDF.XSD.Literal.t()) :: boolean
|
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
The set of applicable facets of a `RDF.XSD.Datatype`.
|
The set of applicable facets of a `RDF.XSD.Datatype`.
|
||||||
"""
|
"""
|
||||||
|
@ -95,18 +94,21 @@ defmodule RDF.XSD.Datatype do
|
||||||
uncanonical_lexical: RDF.XSD.Datatype.uncanonical_lexical()
|
uncanonical_lexical: RDF.XSD.Datatype.uncanonical_lexical()
|
||||||
}
|
}
|
||||||
|
|
||||||
@impl unquote(__MODULE__)
|
@doc !"""
|
||||||
def derived_from?(datatype)
|
This function is just used to check if a module is a RDF.XSD.Datatype.
|
||||||
|
|
||||||
def derived_from?(__MODULE__), do: true
|
See `RDF.Literal.Datatype.Registry.is_xsd_datatype?/1`.
|
||||||
|
"""
|
||||||
|
def __xsd_datatype_indicator__, do: true
|
||||||
|
|
||||||
def derived_from?(datatype) do
|
@impl RDF.Literal.Datatype
|
||||||
base = base()
|
def datatype?(%RDF.Literal{literal: literal}), do: datatype?(literal)
|
||||||
not is_nil(base) and base.derived_from?(datatype)
|
def datatype?(%datatype{}), do: datatype?(datatype)
|
||||||
|
def datatype?(__MODULE__), do: true
|
||||||
|
def datatype?(datatype) when maybe_module(datatype) do
|
||||||
|
RDF.XSD.datatype?(datatype) and datatype.derived_from?(__MODULE__)
|
||||||
end
|
end
|
||||||
|
def datatype?(_), do: false
|
||||||
@impl unquote(__MODULE__)
|
|
||||||
def derived?(literal), do: RDF.XSD.Datatype.derived_from?(literal, __MODULE__)
|
|
||||||
|
|
||||||
# Dialyzer causes a warning on all primitives since the facet_conform?/2 call
|
# Dialyzer causes a warning on all primitives since the facet_conform?/2 call
|
||||||
# always returns true there, so the other branch is unnecessary. This could
|
# always returns true there, so the other branch is unnecessary. This could
|
||||||
|
@ -252,14 +254,4 @@ defmodule RDF.XSD.Datatype do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec base_primitive(t()) :: t()
|
|
||||||
def base_primitive(%RDF.Literal{literal: literal}), do: base_primitive(literal)
|
|
||||||
def base_primitive(%datatype{}), do: base_primitive(datatype)
|
|
||||||
def base_primitive(datatype), do: datatype.base_primitive()
|
|
||||||
|
|
||||||
@spec derived_from?(t() | literal() | RDF.Literal.t(), t()) :: boolean
|
|
||||||
def derived_from?(%RDF.Literal{literal: literal}, super_datatype), do: derived_from?(literal, super_datatype)
|
|
||||||
def derived_from?(%datatype{}, super_datatype), do: derived_from?(datatype, super_datatype)
|
|
||||||
def derived_from?(datatype, super_datatype) when is_atom(datatype), do: datatype.derived_from?(super_datatype)
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,6 +23,9 @@ defmodule RDF.XSD.Datatype.Primitive do
|
||||||
@impl RDF.XSD.Datatype
|
@impl RDF.XSD.Datatype
|
||||||
def base_primitive, do: __MODULE__
|
def base_primitive, do: __MODULE__
|
||||||
|
|
||||||
|
@impl RDF.XSD.Datatype
|
||||||
|
def derived_from?(_), do: false
|
||||||
|
|
||||||
@impl RDF.XSD.Datatype
|
@impl RDF.XSD.Datatype
|
||||||
def init_valid_lexical(value, lexical, opts)
|
def init_valid_lexical(value, lexical, opts)
|
||||||
def init_valid_lexical(_value, nil, _opts), do: nil
|
def init_valid_lexical(_value, nil, _opts), do: nil
|
||||||
|
@ -40,8 +43,8 @@ defmodule RDF.XSD.Datatype.Primitive do
|
||||||
|
|
||||||
@impl RDF.Literal.Datatype
|
@impl RDF.Literal.Datatype
|
||||||
def do_cast(value) do
|
def do_cast(value) do
|
||||||
if RDF.XSD.literal?(value) do
|
if RDF.Literal.datatype?(value) do
|
||||||
if derived?(value) do
|
if datatype?(value) do
|
||||||
build_valid(value.value, value.uncanonical_lexical, [])
|
build_valid(value.value, value.uncanonical_lexical, [])
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|
|
@ -20,6 +20,10 @@ defmodule RDF.XSD.Datatype.Restriction do
|
||||||
@impl RDF.XSD.Datatype
|
@impl RDF.XSD.Datatype
|
||||||
def base_primitive, do: @base.base_primitive()
|
def base_primitive, do: @base.base_primitive()
|
||||||
|
|
||||||
|
@impl RDF.XSD.Datatype
|
||||||
|
def derived_from?(@base), do: true
|
||||||
|
def derived_from?(datatype), do: @base.derived_from?(datatype)
|
||||||
|
|
||||||
@impl RDF.XSD.Datatype
|
@impl RDF.XSD.Datatype
|
||||||
def applicable_facets, do: @base.applicable_facets()
|
def applicable_facets, do: @base.applicable_facets()
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ defmodule RDF.XSD.Boolean do
|
||||||
end
|
end
|
||||||
|
|
||||||
def do_cast(literal_or_value) do
|
def do_cast(literal_or_value) do
|
||||||
if RDF.XSD.Numeric.literal?(literal_or_value) do
|
if RDF.XSD.Numeric.datatype?(literal_or_value) do
|
||||||
new(literal_or_value.value not in [0, 0.0, :nan])
|
new(literal_or_value.value not in [0, 0.0, :nan])
|
||||||
else
|
else
|
||||||
super(literal_or_value)
|
super(literal_or_value)
|
||||||
|
@ -82,7 +82,7 @@ defmodule RDF.XSD.Boolean do
|
||||||
def ebv(%datatype{} = literal) do
|
def ebv(%datatype{} = literal) do
|
||||||
if RDF.XSD.Numeric.datatype?(datatype) do
|
if RDF.XSD.Numeric.datatype?(datatype) do
|
||||||
if datatype.valid?(literal) and
|
if datatype.valid?(literal) and
|
||||||
not (literal.value == 0 or literal.value == :nan),
|
not (datatype.value(literal) == 0 or datatype.value(literal) == :nan),
|
||||||
do: RDF.XSD.Boolean.Value.true(),
|
do: RDF.XSD.Boolean.Value.true(),
|
||||||
else: RDF.XSD.Boolean.Value.false()
|
else: RDF.XSD.Boolean.Value.false()
|
||||||
end
|
end
|
||||||
|
|
|
@ -107,8 +107,8 @@ defmodule RDF.XSD.Decimal do
|
||||||
|
|
||||||
def digit_count(literal) do
|
def digit_count(literal) do
|
||||||
cond do
|
cond do
|
||||||
RDF.XSD.Integer.derived?(literal) -> RDF.XSD.Integer.digit_count(literal)
|
RDF.XSD.Integer.datatype?(literal) -> RDF.XSD.Integer.digit_count(literal)
|
||||||
derived?(literal) -> do_digit_count(literal)
|
datatype?(literal) -> do_digit_count(literal)
|
||||||
true -> nil
|
true -> nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -132,8 +132,8 @@ defmodule RDF.XSD.Decimal do
|
||||||
|
|
||||||
def fraction_digit_count(literal) do
|
def fraction_digit_count(literal) do
|
||||||
cond do
|
cond do
|
||||||
RDF.XSD.Integer.derived?(literal) -> 0
|
RDF.XSD.Integer.datatype?(literal) -> 0
|
||||||
derived?(literal) -> do_fraction_digit_count(literal)
|
datatype?(literal) -> do_fraction_digit_count(literal)
|
||||||
true -> nil
|
true -> nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -74,7 +74,7 @@ defmodule RDF.XSD.Integer do
|
||||||
"""
|
"""
|
||||||
@spec digit_count(RDF.XSD.Literal.t()) :: non_neg_integer | nil
|
@spec digit_count(RDF.XSD.Literal.t()) :: non_neg_integer | nil
|
||||||
def digit_count(%datatype{} = literal) do
|
def digit_count(%datatype{} = literal) do
|
||||||
if derived?(literal) and datatype.valid?(literal) do
|
if datatype?(literal) and datatype.valid?(literal) do
|
||||||
literal
|
literal
|
||||||
|> datatype.canonical()
|
|> datatype.canonical()
|
||||||
|> datatype.lexical()
|
|> datatype.lexical()
|
||||||
|
|
|
@ -3,67 +3,13 @@ defmodule RDF.XSD.Numeric do
|
||||||
Collection of functions for numeric literals.
|
Collection of functions for numeric literals.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@type t :: module
|
||||||
|
|
||||||
alias Elixir.Decimal, as: D
|
alias Elixir.Decimal, as: D
|
||||||
|
|
||||||
import Kernel, except: [abs: 1, floor: 1, ceil: 1]
|
import Kernel, except: [abs: 1, floor: 1, ceil: 1]
|
||||||
|
|
||||||
@datatypes MapSet.new([
|
defdelegate datatype?(value), to: RDF.Literal.Datatype.Registry, as: :numeric_datatype?
|
||||||
RDF.XSD.Decimal,
|
|
||||||
RDF.XSD.Integer,
|
|
||||||
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.Double,
|
|
||||||
RDF.XSD.Float
|
|
||||||
])
|
|
||||||
|
|
||||||
@type t ::
|
|
||||||
RDF.XSD.Decimal.t()
|
|
||||||
| RDF.XSD.Integer.t()
|
|
||||||
| RDF.XSD.Long.t()
|
|
||||||
| RDF.XSD.Int.t()
|
|
||||||
| RDF.XSD.Short.t()
|
|
||||||
| RDF.XSD.Byte.t()
|
|
||||||
| RDF.XSD.NonNegativeInteger.t()
|
|
||||||
| RDF.XSD.PositiveInteger.t()
|
|
||||||
| RDF.XSD.UnsignedLong.t()
|
|
||||||
| RDF.XSD.UnsignedInt.t()
|
|
||||||
| RDF.XSD.UnsignedShort.t()
|
|
||||||
| RDF.XSD.UnsignedByte.t()
|
|
||||||
| RDF.XSD.NonPositiveInteger.t()
|
|
||||||
| RDF.XSD.NegativeInteger.t()
|
|
||||||
| RDF.XSD.Double.t()
|
|
||||||
| RDF.XSD.Float.t()
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
The set of all numeric datatypes.
|
|
||||||
"""
|
|
||||||
@spec datatypes() :: Enum.t
|
|
||||||
def datatypes(), do: @datatypes
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Returns if a given datatype is a numeric datatype.
|
|
||||||
"""
|
|
||||||
@spec datatype?(RDF.XSD.Datatype.t() | any) :: boolean
|
|
||||||
def datatype?(datatype), do: datatype in @datatypes
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Returns if a given XSD literal has a numeric datatype.
|
|
||||||
"""
|
|
||||||
@spec literal?(RDF.Literal.t() | any) :: boolean
|
|
||||||
def literal?(literal)
|
|
||||||
def literal?(%RDF.Literal{literal: literal}), do: literal?(literal)
|
|
||||||
def literal?(%datatype{}), do: datatype?(datatype)
|
|
||||||
def literal?(_), do: false
|
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Tests for numeric value equality of two numeric XSD datatyped literals.
|
Tests for numeric value equality of two numeric XSD datatyped literals.
|
||||||
|
@ -346,8 +292,8 @@ defmodule RDF.XSD.Numeric do
|
||||||
|
|
||||||
def abs(value) do
|
def abs(value) do
|
||||||
cond do
|
cond do
|
||||||
literal?(value) ->
|
datatype?(value) ->
|
||||||
if RDF.XSD.valid?(value) do
|
if RDF.Literal.Datatype.valid?(value) do
|
||||||
%datatype{} = value
|
%datatype{} = value
|
||||||
|
|
||||||
case value.value do
|
case value.value do
|
||||||
|
@ -367,7 +313,7 @@ defmodule RDF.XSD.Numeric do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
RDF.XSD.literal?(value) ->
|
RDF.Literal.datatype?(value) ->
|
||||||
nil
|
nil
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
|
@ -424,8 +370,8 @@ defmodule RDF.XSD.Numeric do
|
||||||
|
|
||||||
def round(value, precision) do
|
def round(value, precision) do
|
||||||
cond do
|
cond do
|
||||||
literal?(value) ->
|
datatype?(value) ->
|
||||||
if RDF.XSD.valid?(value) do
|
if RDF.Literal.Datatype.valid?(value) do
|
||||||
if precision < 0 do
|
if precision < 0 do
|
||||||
value.value
|
value.value
|
||||||
|> new_decimal()
|
|> new_decimal()
|
||||||
|
@ -437,7 +383,7 @@ defmodule RDF.XSD.Numeric do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
RDF.XSD.literal?(value) ->
|
RDF.Literal.datatype?(value) ->
|
||||||
nil
|
nil
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
|
@ -494,12 +440,12 @@ defmodule RDF.XSD.Numeric do
|
||||||
|
|
||||||
def ceil(value) do
|
def ceil(value) do
|
||||||
cond do
|
cond do
|
||||||
literal?(value) ->
|
datatype?(value) ->
|
||||||
if RDF.XSD.valid?(value) do
|
if RDF.Literal.Datatype.valid?(value) do
|
||||||
literal(value)
|
literal(value)
|
||||||
end
|
end
|
||||||
|
|
||||||
RDF.XSD.literal?(value) ->
|
RDF.Literal.datatype?(value) ->
|
||||||
nil
|
nil
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
|
@ -550,10 +496,10 @@ defmodule RDF.XSD.Numeric do
|
||||||
|
|
||||||
def floor(value) do
|
def floor(value) do
|
||||||
cond do
|
cond do
|
||||||
literal?(value) ->
|
datatype?(value) ->
|
||||||
if RDF.XSD.valid?(value), do: literal(value)
|
if RDF.Literal.Datatype.valid?(value), do: literal(value)
|
||||||
|
|
||||||
RDF.XSD.literal?(value) ->
|
RDF.Literal.datatype?(value) ->
|
||||||
nil
|
nil
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
|
@ -578,8 +524,8 @@ defmodule RDF.XSD.Numeric do
|
||||||
cond do
|
cond do
|
||||||
is_nil(left) -> nil
|
is_nil(left) -> nil
|
||||||
is_nil(right) -> nil
|
is_nil(right) -> nil
|
||||||
not RDF.XSD.literal?(left) -> arithmetic_operation(op, RDF.Literal.coerce(left), right, fun)
|
not RDF.Literal.datatype?(left) -> arithmetic_operation(op, RDF.Literal.coerce(left), right, fun)
|
||||||
not RDF.XSD.literal?(right) -> arithmetic_operation(op, left, RDF.Literal.coerce(right), fun)
|
not RDF.Literal.datatype?(right) -> arithmetic_operation(op, left, RDF.Literal.coerce(right), fun)
|
||||||
true -> false
|
true -> false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -65,7 +65,7 @@ defmodule RDF.XSD.String do
|
||||||
end
|
end
|
||||||
|
|
||||||
def do_cast(%datatype{} = literal) do
|
def do_cast(%datatype{} = literal) do
|
||||||
if RDF.XSD.datatype?(datatype) do
|
if RDF.Literal.Datatype.Registry.xsd_datatype_struct?(datatype) do
|
||||||
default_canonical_cast(literal, datatype)
|
default_canonical_cast(literal, datatype)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,9 +33,14 @@ defmodule RDF.XSD.Datatype.Test.Case do
|
||||||
@invalid unquote(invalid)
|
@invalid unquote(invalid)
|
||||||
|
|
||||||
test "registration" do
|
test "registration" do
|
||||||
assert unquote(datatype) in XSD.datatypes()
|
assert unquote(datatype) in RDF.Literal.Datatype.Registry.builtin_datatypes()
|
||||||
assert XSD.datatype_by_name(unquote(datatype_name)) == unquote(datatype)
|
assert unquote(datatype) in RDF.Literal.Datatype.Registry.builtin_xsd_datatypes()
|
||||||
assert XSD.datatype_by_iri(unquote(datatype_iri)) == unquote(datatype)
|
|
||||||
|
assert unquote(datatype) |> RDF.Literal.Datatype.Registry.builtin_datatype?()
|
||||||
|
assert unquote(datatype) |> RDF.Literal.Datatype.Registry.builtin_xsd_datatype?()
|
||||||
|
|
||||||
|
assert RDF.Literal.Datatype.get(unquote(datatype_iri)) == unquote(datatype)
|
||||||
|
assert XSD.Datatype.get(unquote(datatype_iri)) == unquote(datatype)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "primitive/0" do
|
test "primitive/0" do
|
||||||
|
@ -59,7 +64,7 @@ defmodule RDF.XSD.Datatype.Test.Case do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "derived_from?/1" do
|
test "derived_from?/1" do
|
||||||
assert unquote(datatype).derived_from?(unquote(datatype)) == true
|
assert unquote(datatype).derived_from?(unquote(datatype)) == false
|
||||||
|
|
||||||
unless unquote(primitive) do
|
unless unquote(primitive) do
|
||||||
assert unquote(datatype).derived_from?(unquote(base)) == true
|
assert unquote(datatype).derived_from?(unquote(base)) == true
|
||||||
|
@ -67,6 +72,26 @@ defmodule RDF.XSD.Datatype.Test.Case do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "datatype?/1" do
|
||||||
|
test "with itself" do
|
||||||
|
assert unquote(datatype).datatype?(unquote(datatype)) == true
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with non-RDF values" do
|
||||||
|
assert unquote(datatype).datatype?(self()) == false
|
||||||
|
assert unquote(datatype).datatype?(Elixir.Enum) == false
|
||||||
|
assert unquote(datatype).datatype?(:foo) == false
|
||||||
|
end
|
||||||
|
|
||||||
|
unless unquote(primitive) do
|
||||||
|
test "on a base datatype" do
|
||||||
|
# We're using apply here to suppress "nil.datatype?/1 is undefined" warnings caused by the primitives
|
||||||
|
assert apply(unquote(base), :datatype?, [unquote(datatype)]) == true
|
||||||
|
assert apply(unquote(base_primitive), :datatype?, [unquote(datatype)]) == true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "applicable_facets/0" do
|
test "applicable_facets/0" do
|
||||||
assert MapSet.new(unquote(datatype).applicable_facets()) ==
|
assert MapSet.new(unquote(datatype).applicable_facets()) ==
|
||||||
MapSet.new(unquote(applicable_facets))
|
MapSet.new(unquote(applicable_facets))
|
||||||
|
@ -88,10 +113,36 @@ defmodule RDF.XSD.Datatype.Test.Case do
|
||||||
assert unquote(datatype).id() == RDF.iri(unquote(datatype_iri))
|
assert unquote(datatype).id() == RDF.iri(unquote(datatype_iri))
|
||||||
end
|
end
|
||||||
|
|
||||||
test "language/1" do
|
describe "general datatype?/1" do
|
||||||
Enum.each(@valid, fn {input, _} ->
|
test "on the exact same datatype" do
|
||||||
assert (unquote(datatype).new(input) |> unquote(datatype).language()) == nil
|
assert (unquote(datatype).datatype?(unquote(datatype))) == true
|
||||||
end)
|
Enum.each(@valid, fn {input, _} ->
|
||||||
|
literal = unquote(datatype).new(input)
|
||||||
|
assert (unquote(datatype).datatype?(literal)) == true
|
||||||
|
assert (unquote(datatype).datatype?(literal.literal)) == true
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
unless unquote(primitive) do
|
||||||
|
test "on the base datatype" do
|
||||||
|
assert (unquote(base).datatype?(unquote(datatype))) == true
|
||||||
|
Enum.each(@valid, fn {input, _} ->
|
||||||
|
literal = unquote(datatype).new(input)
|
||||||
|
assert (unquote(base).datatype?(literal)) == true
|
||||||
|
assert (unquote(base).datatype?(literal.literal)) == true
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "on the base primitive datatype" do
|
||||||
|
assert (unquote(base_primitive).datatype?(unquote(datatype))) == true
|
||||||
|
Enum.each(@valid, fn {input, _} ->
|
||||||
|
literal = unquote(datatype).new(input)
|
||||||
|
assert (unquote(base_primitive).datatype?(literal)) == true
|
||||||
|
assert (unquote(base_primitive).datatype?(literal.literal)) == true
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "datatype/1" do
|
test "datatype/1" do
|
||||||
|
@ -100,6 +151,12 @@ defmodule RDF.XSD.Datatype.Test.Case do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "language/1" do
|
||||||
|
Enum.each(@valid, fn {input, _} ->
|
||||||
|
assert (unquote(datatype).new(input) |> unquote(datatype).language()) == nil
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
describe "general new" do
|
describe "general new" do
|
||||||
Enum.each(@valid, fn {input, {value, lexical, _}} ->
|
Enum.each(@valid, fn {input, {value, lexical, _}} ->
|
||||||
expected = %RDF.Literal{
|
expected = %RDF.Literal{
|
||||||
|
|
|
@ -90,6 +90,15 @@ defmodule RDF.Literal.GenericTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "datatype?/1" do
|
||||||
|
assert Generic.datatype?(Generic) == true
|
||||||
|
Enum.each @valid, fn {input, {_, datatype}} ->
|
||||||
|
literal = Generic.new(input, datatype: datatype)
|
||||||
|
assert Generic.datatype?(literal) == true
|
||||||
|
assert Generic.datatype?(literal.literal) == true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "datatype/1" do
|
test "datatype/1" do
|
||||||
Enum.each @valid, fn {input, {_, datatype}} ->
|
Enum.each @valid, fn {input, {_, datatype}} ->
|
||||||
assert (Generic.new(input, datatype: datatype) |> Generic.datatype()) == RDF.iri(datatype)
|
assert (Generic.new(input, datatype: datatype) |> Generic.datatype()) == RDF.iri(datatype)
|
||||||
|
|
|
@ -100,6 +100,15 @@ defmodule RDF.LangStringTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "datatype?/1" do
|
||||||
|
assert LangString.datatype?(LangString) == true
|
||||||
|
Enum.each @valid, fn {input, {_, language}} ->
|
||||||
|
literal = LangString.new(input, language: language)
|
||||||
|
assert LangString.datatype?(literal) == true
|
||||||
|
assert LangString.datatype?(literal.literal) == true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "datatype/1" do
|
test "datatype/1" do
|
||||||
Enum.each @valid, fn {input, {_, language}} ->
|
Enum.each @valid, fn {input, {_, language}} ->
|
||||||
assert (LangString.new(input, language: language) |> LangString.datatype()) == RDF.iri(LangString.id())
|
assert (LangString.new(input, language: language) |> LangString.datatype()) == RDF.iri(LangString.id())
|
||||||
|
|
|
@ -4,6 +4,7 @@ defmodule RDF.Literal.Datatype.RegistryTest do
|
||||||
alias RDF.TestDatatypes.Age
|
alias RDF.TestDatatypes.Age
|
||||||
alias RDF.Literal.Datatype
|
alias RDF.Literal.Datatype
|
||||||
alias RDF.NS
|
alias RDF.NS
|
||||||
|
alias RDF.TestDatatypes
|
||||||
|
|
||||||
@unsupported_xsd_datatypes ~w[
|
@unsupported_xsd_datatypes ~w[
|
||||||
ENTITIES
|
ENTITIES
|
||||||
|
@ -37,8 +38,8 @@ defmodule RDF.Literal.Datatype.RegistryTest do
|
||||||
|
|
||||||
|
|
||||||
describe "datatype/1" do
|
describe "datatype/1" do
|
||||||
test "core datatypes" do
|
test "builtin datatypes" do
|
||||||
Enum.each(Datatype.Registry.core_datatypes(), fn datatype ->
|
Enum.each(Datatype.Registry.builtin_datatypes(), fn datatype ->
|
||||||
assert datatype == Datatype.Registry.datatype(datatype.id)
|
assert datatype == Datatype.Registry.datatype(datatype.id)
|
||||||
assert datatype == Datatype.Registry.datatype(to_string(datatype.id))
|
assert datatype == Datatype.Registry.datatype(to_string(datatype.id))
|
||||||
end)
|
end)
|
||||||
|
@ -67,21 +68,40 @@ defmodule RDF.Literal.Datatype.RegistryTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "xsd_datatype/1" do
|
test "datatype?/1" do
|
||||||
test "when a core XSD datatype with the given IRI exists" do
|
assert Datatype.Registry.datatype?(XSD.string("foo"))
|
||||||
assert XSD.String = Datatype.Registry.xsd_datatype(NS.XSD.string)
|
assert Datatype.Registry.datatype?(~L"foo"en)
|
||||||
end
|
assert Datatype.Registry.datatype?(XSD.integer(42))
|
||||||
|
assert Datatype.Registry.datatype?(XSD.byte(42))
|
||||||
|
assert Datatype.Registry.datatype?(TestDatatypes.Age.new(42))
|
||||||
|
assert Datatype.Registry.datatype?(RDF.literal("foo", datatype: "http://example.com"))
|
||||||
|
refute Datatype.Registry.datatype?(~r/foo/)
|
||||||
|
refute Datatype.Registry.datatype?(:foo)
|
||||||
|
refute Datatype.Registry.datatype?(42)
|
||||||
|
end
|
||||||
|
|
||||||
test "when a custom XSD datatype with the given IRI exists" do
|
test "xsd_datatype?/1" do
|
||||||
assert Age = Datatype.Registry.xsd_datatype(EX.Age)
|
assert Datatype.Registry.xsd_datatype?(XSD.string("foo"))
|
||||||
end
|
assert Datatype.Registry.xsd_datatype?(XSD.integer(42))
|
||||||
|
assert Datatype.Registry.xsd_datatype?(XSD.byte(42))
|
||||||
|
assert Datatype.Registry.xsd_datatype?(TestDatatypes.Age.new(42))
|
||||||
|
refute Datatype.Registry.xsd_datatype?(~L"foo"en)
|
||||||
|
refute Datatype.Registry.xsd_datatype?(RDF.literal("foo", datatype: "http://example.com"))
|
||||||
|
refute Datatype.Registry.xsd_datatype?(~r/foo/)
|
||||||
|
refute Datatype.Registry.xsd_datatype?(:foo)
|
||||||
|
refute Datatype.Registry.xsd_datatype?(42)
|
||||||
|
end
|
||||||
|
|
||||||
test "when datatype with the given IRI exists, but it is not an XSD datatype" do
|
|
||||||
refute Datatype.Registry.xsd_datatype(RDF.langString)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "when no datatype with the given IRI exists" do
|
test "numeric_datatype?/1" do
|
||||||
refute Datatype.Registry.xsd_datatype(EX.foo)
|
assert Datatype.Registry.numeric_datatype?(XSD.integer(42))
|
||||||
end
|
assert Datatype.Registry.numeric_datatype?(XSD.byte(42))
|
||||||
|
assert Datatype.Registry.numeric_datatype?(TestDatatypes.Age.new(42))
|
||||||
|
refute Datatype.Registry.numeric_datatype?(XSD.string("foo"))
|
||||||
|
refute Datatype.Registry.numeric_datatype?(~L"foo"en)
|
||||||
|
refute Datatype.Registry.numeric_datatype?(RDF.literal("foo", datatype: "http://example.com"))
|
||||||
|
refute Datatype.Registry.numeric_datatype?(~r/foo/)
|
||||||
|
refute Datatype.Registry.numeric_datatype?(:foo)
|
||||||
|
refute Datatype.Registry.numeric_datatype?(42)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,8 +30,8 @@ defmodule RDF.LiteralTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "with core datatype literals" do
|
test "with builtin datatype literals" do
|
||||||
Enum.each Datatype.Registry.core_datatypes(), fn datatype ->
|
Enum.each Datatype.Registry.builtin_datatypes(), fn datatype ->
|
||||||
datatype_literal = datatype.new("foo").literal
|
datatype_literal = datatype.new("foo").literal
|
||||||
assert %Literal{literal: ^datatype_literal} = Literal.new(datatype_literal)
|
assert %Literal{literal: ^datatype_literal} = Literal.new(datatype_literal)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
defmodule RDF.XSD.DatatypeTest do
|
|
||||||
use ExUnit.Case
|
|
||||||
|
|
||||||
alias RDF.XSD
|
|
||||||
|
|
||||||
test "base_primitive/1" do
|
|
||||||
assert XSD.integer(42) |> XSD.Datatype.base_primitive() == XSD.Integer
|
|
||||||
assert XSD.non_negative_integer(42) |> XSD.Datatype.base_primitive() == XSD.Integer
|
|
||||||
assert XSD.positive_integer(42) |> XSD.Datatype.base_primitive() == XSD.Integer
|
|
||||||
end
|
|
||||||
|
|
||||||
test "derived_from?/2" do
|
|
||||||
assert XSD.integer(42) |> XSD.Datatype.derived_from?(XSD.Integer)
|
|
||||||
assert XSD.non_negative_integer(42) |> XSD.Datatype.derived_from?(XSD.Integer)
|
|
||||||
assert XSD.positive_integer(42) |> XSD.Datatype.derived_from?(XSD.Integer)
|
|
||||||
assert XSD.positive_integer(42) |> XSD.Datatype.derived_from?(XSD.NonNegativeInteger)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -3,8 +3,8 @@ defmodule RDF.XSDTest do
|
||||||
|
|
||||||
doctest RDF.XSD
|
doctest RDF.XSD
|
||||||
|
|
||||||
test "Datatype constructor alias functions" do
|
test "builtin datatype constructor alias functions" do
|
||||||
Enum.each(XSD.datatypes(), fn datatype ->
|
Enum.each(RDF.Literal.Datatype.Registry.builtin_xsd_datatypes(), fn datatype ->
|
||||||
assert apply(XSD, String.to_atom(datatype.name), [1]) == datatype.new(1)
|
assert apply(XSD, String.to_atom(datatype.name), [1]) == datatype.new(1)
|
||||||
assert apply(XSD, String.to_atom(Macro.underscore(datatype.name)), [1]) == datatype.new(1)
|
assert apply(XSD, String.to_atom(Macro.underscore(datatype.name)), [1]) == datatype.new(1)
|
||||||
end)
|
end)
|
||||||
|
|
Loading…
Reference in a new issue