Handle derived datatypes on RDF.Literal.Datatype.equal_value?/2 properly
This commit is contained in:
parent
d247e1bf4f
commit
162e82ed47
15 changed files with 288 additions and 140 deletions
|
@ -24,9 +24,9 @@ defmodule RDF.Literal.Datatype do
|
|||
@callback new!(any, Keyword.t()) :: Literal.t()
|
||||
|
||||
@doc """
|
||||
Casts a datatype literal of one type into a datatype literal of another type.
|
||||
Callback for datatype specific castings.
|
||||
|
||||
This function is called by the auto-generated `cast/1` function on the implementations, which already deals with the basic cases.
|
||||
This callback is called by the auto-generated `cast/1` function on the implementations, which already deals with the basic cases.
|
||||
So, implementations can assume the passed argument is a valid `RDF.Literal.Datatype` struct,
|
||||
a `RDF.IRI` or a `RDF.BlankNode`.
|
||||
|
||||
|
@ -98,7 +98,10 @@ defmodule RDF.Literal.Datatype do
|
|||
@callback valid?(Literal.t() | literal | any) :: boolean
|
||||
|
||||
@doc """
|
||||
Checks if two datatype literals are equal in terms of the values of their value space.
|
||||
Callback for datatype specific `equal_value?/2` comparisons when the given literals have the same or derived datatypes.
|
||||
|
||||
This callback is called by auto-generated `equal_value?/2` function when the given literals have
|
||||
the same datatype or one is derived from the other.
|
||||
|
||||
Should return `nil` when the given arguments are not comparable as literals of this
|
||||
datatype. This behaviour is particularly important for SPARQL.ex where this
|
||||
|
@ -106,10 +109,25 @@ defmodule RDF.Literal.Datatype do
|
|||
terms are treated as errors and immediately leads to a rejection of a possible
|
||||
match.
|
||||
|
||||
This function is called by auto-generated `equal_value?/2` function on the
|
||||
implementations, which already deals with basic cases and coercion.
|
||||
See also `c:do_equal_value_different_datatypes?/2`.
|
||||
"""
|
||||
@callback do_equal_value?(literal, literal) :: boolean | nil
|
||||
@callback do_equal_value_same_or_derived_datatypes?(literal, literal) :: boolean | nil
|
||||
|
||||
@doc """
|
||||
Callback for datatype specific `equal_value?/2` comparisons when the given literals have different datatypes.
|
||||
|
||||
This callback is called by auto-generated `equal_value?/2` function when the given literals have
|
||||
different datatypes and are not derived from each other.
|
||||
|
||||
Should return `nil` when the given arguments are not comparable as literals of this
|
||||
datatype. This behaviour is particularly important for SPARQL.ex where this
|
||||
function is used for the `=` operator, where comparisons between incomparable
|
||||
terms are treated as errors and immediately leads to a rejection of a possible
|
||||
match.
|
||||
|
||||
See also `c:do_equal_value_same_or_derived_datatypes?/2`.
|
||||
"""
|
||||
@callback do_equal_value_different_datatypes?(literal, literal) :: boolean | nil
|
||||
|
||||
@doc """
|
||||
Compares two `RDF.Literal`s.
|
||||
|
@ -266,7 +284,11 @@ defmodule RDF.Literal.Datatype do
|
|||
Returns `nil` when the given arguments are not comparable as literals of this
|
||||
datatype.
|
||||
|
||||
Implementations define this equivalence relation via the `c:do_equal_value?/2` callback.
|
||||
Invalid literals are only considered equal in this relation when both have the exact same
|
||||
datatype and the same attributes (lexical form, language etc.).
|
||||
|
||||
Implementations can customize this equivalence relation via the `c:do_equal_value_different_datatypes?/2`
|
||||
and `c:do_equal_value_different_datatypes?/2` callbacks.
|
||||
"""
|
||||
def equal_value?(left, right)
|
||||
def equal_value?(left, %Literal{literal: right}), do: equal_value?(left, right)
|
||||
|
@ -275,24 +297,58 @@ defmodule RDF.Literal.Datatype do
|
|||
def equal_value?(_, nil), do: nil
|
||||
def equal_value?(left, right) do
|
||||
cond do
|
||||
not Literal.datatype?(right) and not RDF.term?(right) -> equal_value?(left, Literal.coerce(right))
|
||||
not Literal.datatype?(left) and not RDF.term?(left) -> equal_value?(Literal.coerce(left), right)
|
||||
true -> do_equal_value?(left, right)
|
||||
not Literal.datatype?(right) and not resource?(right) -> equal_value?(left, Literal.coerce(right))
|
||||
not Literal.datatype?(left) and not resource?(left) -> equal_value?(Literal.coerce(left), right)
|
||||
true ->
|
||||
left_datatype = left.__struct__
|
||||
right_datatype = right.__struct__
|
||||
left_valid = resource?(left) or left_datatype.valid?(left)
|
||||
right_valid = resource?(right) or right_datatype.valid?(right)
|
||||
|
||||
cond do
|
||||
not left_valid and not right_valid ->
|
||||
left == right
|
||||
|
||||
left_valid and right_valid ->
|
||||
case equality_path(left_datatype, right_datatype) do
|
||||
{:same_or_derived, datatype} ->
|
||||
datatype.do_equal_value_same_or_derived_datatypes?(left, right)
|
||||
{:different, datatype} ->
|
||||
datatype.do_equal_value_different_datatypes?(left, right)
|
||||
end
|
||||
|
||||
# one of the given literals is invalid
|
||||
true ->
|
||||
if left_datatype == right_datatype do
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# RDF.XSD.Datatypes offers another default implementation, but since it is
|
||||
# RDF.XSD.Datatype 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 do_equal_value?(left, right)
|
||||
def do_equal_value?(%__MODULE__{} = left, %__MODULE__{} = right), do: left == right
|
||||
def do_equal_value?(_, _), do: nil
|
||||
def do_equal_value_same_or_derived_datatypes?(left, right), do: left == right
|
||||
|
||||
defoverridable do_equal_value?: 2
|
||||
@impl unquote(__MODULE__)
|
||||
def do_equal_value_different_datatypes?(left, right), do: nil
|
||||
|
||||
defoverridable do_equal_value_same_or_derived_datatypes?: 2,
|
||||
do_equal_value_different_datatypes?: 2
|
||||
end
|
||||
|
||||
@impl unquote(__MODULE__)
|
||||
defp equality_path(left_datatype, right_datatype)
|
||||
defp equality_path(datatype, datatype), do: {:same_or_derived, datatype}
|
||||
defp equality_path(datatype, _), do: {:different, datatype}
|
||||
|
||||
# as opposed to RDF.resource? this does not try to resolve atoms
|
||||
defp resource?(%RDF.IRI{}), do: true
|
||||
defp resource?(%RDF.BlankNode{}), do: true
|
||||
defp resource?(_), do: false
|
||||
|
||||
@impl unquote(__MODULE__)
|
||||
def update(literal, fun, opts \\ [])
|
||||
def update(%Literal{literal: literal}, fun, opts), do: update(literal, fun, opts)
|
||||
def update(%__MODULE__{} = literal, fun, opts) do
|
||||
|
@ -314,6 +370,7 @@ defmodule RDF.Literal.Datatype do
|
|||
cast: 1,
|
||||
do_cast: 1,
|
||||
equal_value?: 2,
|
||||
equality_path: 2,
|
||||
update: 2,
|
||||
update: 3
|
||||
|
||||
|
|
|
@ -85,9 +85,11 @@ defmodule RDF.Literal.Generic do
|
|||
def do_cast(_), do: nil
|
||||
|
||||
@impl Datatype
|
||||
def do_equal_value?(%__MODULE__{datatype: datatype} = left,
|
||||
%__MODULE__{datatype: datatype} = right), do: left == right
|
||||
def do_equal_value?(_, _), do: nil
|
||||
def do_equal_value_same_or_derived_datatypes?(
|
||||
%{datatype: datatype} = left,
|
||||
%{datatype: datatype} = right
|
||||
), do: left == right
|
||||
def do_equal_value_same_or_derived_datatypes?(_, _), do: nil
|
||||
|
||||
@impl Datatype
|
||||
def compare(left, %Literal{literal: right}), do: compare(left, right)
|
||||
|
|
|
@ -77,6 +77,18 @@ defmodule RDF.XSD.Datatype do
|
|||
|
||||
defdelegate get(id), to: RDF.Literal.Datatype.Registry, as: :xsd_datatype
|
||||
|
||||
@doc false
|
||||
def most_specific(left, right)
|
||||
def most_specific(datatype, datatype), do: datatype
|
||||
def most_specific(left, right) do
|
||||
cond do
|
||||
left.datatype?(right) -> right
|
||||
right.datatype?(left) -> left
|
||||
true -> nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
defmacro __using__(opts) do
|
||||
quote do
|
||||
defstruct [:value, :uncanonical_lexical]
|
||||
|
@ -245,6 +257,21 @@ defmodule RDF.XSD.Datatype do
|
|||
do: datatype?(datatype) and datatype.valid?(literal)
|
||||
def valid?(_), do: false
|
||||
|
||||
@doc false
|
||||
defp equality_path(left_datatype, right_datatype)
|
||||
defp equality_path(datatype, datatype), do: {:same_or_derived, datatype}
|
||||
defp equality_path(left_datatype, right_datatype) do
|
||||
if RDF.XSD.datatype?(left_datatype) and RDF.XSD.datatype?(right_datatype) do
|
||||
if datatype = RDF.XSD.Datatype.most_specific(left_datatype, right_datatype) do
|
||||
{:same_or_derived, datatype}
|
||||
else
|
||||
{:different, left_datatype}
|
||||
end
|
||||
else
|
||||
{:different, left_datatype}
|
||||
end
|
||||
end
|
||||
|
||||
defimpl Inspect do
|
||||
"Elixir.Inspect." <> datatype_name = to_string(__MODULE__)
|
||||
@datatype_name datatype_name
|
||||
|
|
|
@ -49,21 +49,12 @@ defmodule RDF.XSD.Datatype.Primitive do
|
|||
end
|
||||
|
||||
@impl RDF.Literal.Datatype
|
||||
def do_equal_value?(left, right)
|
||||
|
||||
def do_equal_value?(
|
||||
%datatype{uncanonical_lexical: lexical1, value: nil},
|
||||
%datatype{uncanonical_lexical: lexical2, value: nil}
|
||||
) do
|
||||
lexical1 == lexical2
|
||||
def do_equal_value_same_or_derived_datatypes?(%left_datatype{} = left, %right_datatype{} = right) do
|
||||
left_datatype.value(left) == right_datatype.value(right)
|
||||
end
|
||||
|
||||
def do_equal_value?(%datatype{} = literal1, %datatype{} = literal2) do
|
||||
literal1 |> datatype.canonical() |> datatype.value() ==
|
||||
literal2 |> datatype.canonical() |> datatype.value()
|
||||
end
|
||||
|
||||
def do_equal_value?(_, _), do: nil
|
||||
@impl RDF.Literal.Datatype
|
||||
def do_equal_value_different_datatypes?(left, right), do: nil
|
||||
|
||||
@impl RDF.Literal.Datatype
|
||||
def compare(left, right)
|
||||
|
@ -88,7 +79,8 @@ defmodule RDF.XSD.Datatype.Primitive do
|
|||
do_cast: 1,
|
||||
init_valid_lexical: 3,
|
||||
init_invalid_lexical: 2,
|
||||
do_equal_value?: 2,
|
||||
do_equal_value_same_or_derived_datatypes?: 2,
|
||||
do_equal_value_different_datatypes?: 2,
|
||||
compare: 2
|
||||
|
||||
@before_compile unquote(__MODULE__)
|
||||
|
|
|
@ -71,12 +71,19 @@ defmodule RDF.XSD.Datatype.Restriction do
|
|||
end
|
||||
end
|
||||
|
||||
# TODO: This makes it impossible to define do_equal_value definitions on derivations,
|
||||
# but we need to overwrite this to reach for example the XSD.Numeric delegation.
|
||||
def equal_value?(literal1, literal2), do: @base.equal_value?(literal1, literal2)
|
||||
def equal_value?(literal1, literal2) do
|
||||
base_primitive().equal_value?(literal1, literal2)
|
||||
end
|
||||
|
||||
@impl RDF.Literal.Datatype
|
||||
def do_equal_value?(left, right), do: nil # unused; see comment on equal_value?/2
|
||||
def do_equal_value_same_or_derived_datatypes?(left, right) do
|
||||
@base.do_equal_value_same_or_derived_datatypes?(left, right)
|
||||
end
|
||||
|
||||
@impl RDF.Literal.Datatype
|
||||
def do_equal_value_different_datatypes?(left, right) do
|
||||
@base.do_equal_value_different_datatypes?(left, right)
|
||||
end
|
||||
|
||||
@impl RDF.Literal.Datatype
|
||||
def compare(left, right), do: @base.compare(left, right)
|
||||
|
@ -84,6 +91,8 @@ defmodule RDF.XSD.Datatype.Restriction do
|
|||
defoverridable canonical_mapping: 1,
|
||||
do_cast: 1,
|
||||
equal_value?: 2,
|
||||
do_equal_value_same_or_derived_datatypes?: 2,
|
||||
do_equal_value_different_datatypes?: 2,
|
||||
compare: 2
|
||||
|
||||
Module.register_attribute(__MODULE__, :facets, accumulate: true)
|
||||
|
|
|
@ -65,23 +65,24 @@ defmodule RDF.XSD.AnyURI do
|
|||
def do_cast(value), do: super(value)
|
||||
|
||||
@impl RDF.Literal.Datatype
|
||||
def do_equal_value?(literal1, literal2)
|
||||
def do_equal_value_different_datatypes?(left, right)
|
||||
|
||||
def do_equal_value?(%IRI{} = iri, %__MODULE__{} = any_uri),
|
||||
do: do_equal_value?(any_uri, iri)
|
||||
def do_equal_value_different_datatypes?(%IRI{} = iri, any_uri),
|
||||
do: do_equal_value_different_datatypes?(any_uri, iri)
|
||||
|
||||
def do_equal_value?(%__MODULE__{} = any_uri, %IRI{value: iri}),
|
||||
def do_equal_value_different_datatypes?(any_uri, %IRI{value: iri}),
|
||||
do: lexical(any_uri) == iri
|
||||
|
||||
def do_equal_value?(left, %__MODULE__{} = right) when maybe_ns_term(left),
|
||||
do: equal_value?(right, left)
|
||||
def do_equal_value_different_datatypes?(left, right) when maybe_ns_term(left),
|
||||
do: do_equal_value_different_datatypes?(right, left)
|
||||
|
||||
def do_equal_value?(%__MODULE__{} = left, right) when maybe_ns_term(right) do
|
||||
def do_equal_value_different_datatypes?(left, right) when maybe_ns_term(right) do
|
||||
case RDF.Namespace.resolve_term(right) do
|
||||
{:ok, iri} -> equal_value?(left, iri)
|
||||
{:ok, iri} -> do_equal_value_different_datatypes?(left, iri)
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
def do_equal_value?(literal1, literal2), do: super(literal1, literal2)
|
||||
def do_equal_value_different_datatypes?(literal1, literal2),
|
||||
do: super(literal1, literal2)
|
||||
end
|
||||
|
|
|
@ -166,30 +166,21 @@ defmodule RDF.XSD.Date do
|
|||
|
||||
|
||||
@impl RDF.Literal.Datatype
|
||||
def do_equal_value?(literal1, literal2)
|
||||
|
||||
def do_equal_value?(
|
||||
%__MODULE__{value: nil, uncanonical_lexical: lexical1},
|
||||
%__MODULE__{value: nil, uncanonical_lexical: lexical2}
|
||||
) do
|
||||
lexical1 == lexical2
|
||||
end
|
||||
|
||||
def do_equal_value?(%__MODULE__{value: value1}, %__MODULE__{value: value2})
|
||||
when is_nil(value1) or is_nil(value2),
|
||||
do: false
|
||||
|
||||
def do_equal_value?(%__MODULE__{value: value1}, %__MODULE__{value: value2}) do
|
||||
def do_equal_value_same_or_derived_datatypes?(left, right) do
|
||||
XSD.DateTime.equal_value?(
|
||||
comparison_normalization(value1),
|
||||
comparison_normalization(value2)
|
||||
comparison_normalization(left.value),
|
||||
comparison_normalization(right.value)
|
||||
)
|
||||
end
|
||||
|
||||
def do_equal_value?(%__MODULE__{}, %XSD.DateTime{}), do: false
|
||||
def do_equal_value?(%XSD.DateTime{}, %__MODULE__{}), do: false
|
||||
|
||||
def do_equal_value?(_, _), do: nil
|
||||
@impl RDF.Literal.Datatype
|
||||
def do_equal_value_different_datatypes?(left, right) do
|
||||
if XSD.DateTime.datatype?(left) or XSD.DateTime.datatype?(right) do
|
||||
false
|
||||
else
|
||||
super(left, right)
|
||||
end
|
||||
end
|
||||
|
||||
@impl RDF.Literal.Datatype
|
||||
def compare(left, right)
|
||||
|
|
|
@ -155,27 +155,19 @@ defmodule RDF.XSD.DateTime do
|
|||
end
|
||||
|
||||
@impl RDF.Literal.Datatype
|
||||
def do_equal_value?(literal1, literal2)
|
||||
def do_equal_value?(
|
||||
%__MODULE__{value: %type{} = value1},
|
||||
%__MODULE__{value: %type{} = value2}
|
||||
def do_equal_value_same_or_derived_datatypes?(
|
||||
%{value: %type{} = left_value},
|
||||
%{value: %type{} = right_value}
|
||||
) do
|
||||
type.compare(value1, value2) == :eq
|
||||
end
|
||||
|
||||
def do_equal_value?(
|
||||
%__MODULE__{value: nil, uncanonical_lexical: lexical1},
|
||||
%__MODULE__{value: nil, uncanonical_lexical: lexical2}
|
||||
) do
|
||||
lexical1 == lexical2
|
||||
type.compare(left_value, right_value) == :eq
|
||||
end
|
||||
|
||||
# This is another quirk for the open-world test date-2 from the SPARQL 1.0 test suite:
|
||||
# comparisons between one date with tz and another one without a tz are incomparable
|
||||
# when the unequal, but comparable and returning false when equal.
|
||||
# What's the reasoning behind this madness?
|
||||
def do_equal_value?(%__MODULE__{} = literal1, %__MODULE__{} = literal2) do
|
||||
case compare(literal1, literal2) do
|
||||
def do_equal_value_same_or_derived_datatypes?(left_literal, right_literal) do
|
||||
case compare(left_literal, right_literal) do
|
||||
:lt -> false
|
||||
:gt -> false
|
||||
# This actually can't/shouldn't happen.
|
||||
|
@ -184,12 +176,16 @@ defmodule RDF.XSD.DateTime do
|
|||
end
|
||||
end
|
||||
|
||||
def do_equal_value?(%__MODULE__{}, %XSD.Date{}), do: false
|
||||
def do_equal_value?(%XSD.Date{}, %__MODULE__{}), do: false
|
||||
|
||||
def do_equal_value?(_, _), do: nil
|
||||
|
||||
@impl RDF.Literal.Datatype
|
||||
def do_equal_value_different_datatypes?(left, right) do
|
||||
if XSD.Date.datatype?(left) or XSD.Date.datatype?(right) do
|
||||
false
|
||||
else
|
||||
super(left, right)
|
||||
end
|
||||
end
|
||||
|
||||
@impl RDF.Literal.Datatype
|
||||
def compare(left, right)
|
||||
def compare(left, %RDF.Literal{literal: right}), do: compare(left, right)
|
||||
def compare(%RDF.Literal{literal: left}, right), do: compare(left, right)
|
||||
|
|
|
@ -152,7 +152,12 @@ defmodule RDF.XSD.Decimal do
|
|||
end
|
||||
|
||||
|
||||
def equal_value?(left, right), do: XSD.Numeric.equal_value?(left, right)
|
||||
@impl RDF.Literal.Datatype
|
||||
def do_equal_value_same_or_derived_datatypes?(left, right), do: XSD.Numeric.equal_value?(left, right)
|
||||
|
||||
@impl RDF.Literal.Datatype
|
||||
def do_equal_value_different_datatypes?(left, right), do: XSD.Numeric.equal_value?(left, right)
|
||||
|
||||
|
||||
@impl RDF.Literal.Datatype
|
||||
def compare(left, right), do: XSD.Numeric.compare(left, right)
|
||||
|
|
|
@ -160,7 +160,12 @@ defmodule RDF.XSD.Double do
|
|||
end
|
||||
|
||||
|
||||
def equal_value?(left, right), do: XSD.Numeric.equal_value?(left, right)
|
||||
@impl RDF.Literal.Datatype
|
||||
def do_equal_value_same_or_derived_datatypes?(left, right), do: XSD.Numeric.equal_value?(left, right)
|
||||
|
||||
@impl RDF.Literal.Datatype
|
||||
def do_equal_value_different_datatypes?(left, right), do: XSD.Numeric.equal_value?(left, right)
|
||||
|
||||
|
||||
@impl RDF.Literal.Datatype
|
||||
def compare(left, right), do: XSD.Numeric.compare(left, right)
|
||||
|
|
|
@ -99,7 +99,11 @@ defmodule RDF.XSD.Integer do
|
|||
end
|
||||
end
|
||||
|
||||
def equal_value?(left, right), do: XSD.Numeric.equal_value?(left, right)
|
||||
@impl RDF.Literal.Datatype
|
||||
def do_equal_value_same_or_derived_datatypes?(left, right), do: XSD.Numeric.equal_value?(left, right)
|
||||
|
||||
@impl RDF.Literal.Datatype
|
||||
def do_equal_value_different_datatypes?(left, right), do: XSD.Numeric.equal_value?(left, right)
|
||||
|
||||
@impl RDF.Literal.Datatype
|
||||
def compare(left, right), do: XSD.Numeric.compare(left, right)
|
||||
|
|
|
@ -12,7 +12,7 @@ defmodule RDF.XSD.Numeric do
|
|||
|
||||
defdelegate datatype?(value), to: Literal.Datatype.Registry, as: :numeric_datatype?
|
||||
|
||||
@doc """
|
||||
@doc !"""
|
||||
Tests for numeric value equality of two numeric XSD datatyped literals.
|
||||
|
||||
see:
|
||||
|
@ -22,30 +22,21 @@ defmodule RDF.XSD.Numeric do
|
|||
"""
|
||||
@spec equal_value?(t() | any, t() | any) :: boolean
|
||||
def equal_value?(left, right)
|
||||
def equal_value?(left, %Literal{literal: right}), do: equal_value?(left, right)
|
||||
def equal_value?(%Literal{literal: left}, right), do: equal_value?(left, right)
|
||||
def equal_value?(nil, _), do: nil
|
||||
def equal_value?(_, nil), do: nil
|
||||
|
||||
def equal_value?(
|
||||
%datatype{value: nil, uncanonical_lexical: lexical1},
|
||||
%datatype{value: nil, uncanonical_lexical: lexical2}
|
||||
) do
|
||||
lexical1 == lexical2
|
||||
end
|
||||
|
||||
def equal_value?(%left_datatype{value: left}, %right_datatype{value: right})
|
||||
when left_datatype == XSD.Decimal or right_datatype == XSD.Decimal,
|
||||
do: not is_nil(left) and not is_nil(right) and equal_decimal_value?(left, right)
|
||||
|
||||
def equal_value?(%left_datatype{value: left}, %right_datatype{value: right}) do
|
||||
if datatype?(left_datatype) and datatype?(right_datatype) do
|
||||
left != :nan and right != :nan and left == right
|
||||
cond do
|
||||
XSD.Decimal.datatype?(left_datatype) or XSD.Decimal.datatype?(right_datatype) ->
|
||||
equal_decimal_value?(left, right)
|
||||
|
||||
datatype?(left_datatype) and datatype?(right_datatype) ->
|
||||
left != :nan and right != :nan and left == right
|
||||
|
||||
true ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def equal_value?(left, right),
|
||||
do: equal_value?(Literal.coerce(left), Literal.coerce(right))
|
||||
def equal_value?(_, _), do: nil
|
||||
|
||||
defp equal_decimal_value?(%D{} = left, %D{} = right), do: D.equal?(left, right)
|
||||
|
||||
|
|
|
@ -189,19 +189,11 @@ defmodule RDF.XSD.Time do
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
@impl RDF.Literal.Datatype
|
||||
def do_equal_value?(literal1, literal2)
|
||||
|
||||
def do_equal_value?(%__MODULE__{value: %_{}}, %__MODULE__{value: tz_tuple})
|
||||
when is_tuple(tz_tuple),
|
||||
do: nil
|
||||
|
||||
def do_equal_value?(%__MODULE__{value: tz_tuple}, %__MODULE__{value: %_{}})
|
||||
when is_tuple(tz_tuple),
|
||||
do: nil
|
||||
|
||||
def do_equal_value?(left, right), do: super(left, right)
|
||||
def do_equal_value_same_or_derived_datatypes?(left, right)
|
||||
def do_equal_value_same_or_derived_datatypes?(%{value: %{}}, %{value: tz_tuple}) when is_tuple(tz_tuple), do: nil
|
||||
def do_equal_value_same_or_derived_datatypes?(%{value: tz_tuple}, %{value: %{}}) when is_tuple(tz_tuple), do: nil
|
||||
def do_equal_value_same_or_derived_datatypes?(left, right), do: super(left, right)
|
||||
|
||||
@doc """
|
||||
Extracts the timezone string from a `RDF.XSD.Time` value.
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
defmodule RDF.EqualityTest do
|
||||
use RDF.Test.Case
|
||||
|
||||
alias RDF.TestDatatypes.{Initials, CustomTime, DateWithoutTz, DateTimeWithTz, Age,
|
||||
DecimalUnitInterval, DoubleUnitInterval, FloatUnitInterval}
|
||||
|
||||
describe "RDF.IRI and XSD.AnyURI" do
|
||||
@term_equal_iris [
|
||||
{RDF.iri("http://example.com/"), RDF.iri("http://example.com/")},
|
||||
|
@ -66,11 +69,13 @@ defmodule RDF.EqualityTest do
|
|||
@term_equal_strings [
|
||||
{XSD.string("foo"), XSD.string("foo")},
|
||||
{RDF.lang_string("foo", language: "de"), RDF.lang_string("foo", language: "de")},
|
||||
{Initials.new("FO"), Initials.new("FO")}
|
||||
]
|
||||
@value_equal_strings []
|
||||
@unequal_strings [
|
||||
{XSD.string("foo"), XSD.string("bar")},
|
||||
{RDF.lang_string("foo", language: "de"), RDF.lang_string("bar", language: "de")},
|
||||
{XSD.string("fo"), Initials.new("FO")}
|
||||
]
|
||||
@equal_strings_by_coercion [
|
||||
{XSD.string("foo"), "foo"}
|
||||
|
@ -152,7 +157,9 @@ defmodule RDF.EqualityTest do
|
|||
{XSD.float("1.0"), XSD.float(1.0)},
|
||||
{XSD.decimal("1.0"), XSD.decimal(1.0)},
|
||||
{XSD.decimal("-42.0"), XSD.decimal(-42.0)},
|
||||
{XSD.decimal("1.0"), XSD.decimal(1.0)}
|
||||
{XSD.decimal("1.0"), XSD.decimal(1.0)},
|
||||
{Age.new("42"), Age.new("42")},
|
||||
{DecimalUnitInterval.new("0.1"), DecimalUnitInterval.new("0.1")},
|
||||
]
|
||||
@value_equal_numerics [
|
||||
{XSD.integer("42"), XSD.non_negative_integer("42")},
|
||||
|
@ -169,6 +176,11 @@ defmodule RDF.EqualityTest do
|
|||
{XSD.integer(42), XSD.decimal(42.0)},
|
||||
{XSD.integer(42), XSD.double(42.0)},
|
||||
{XSD.integer(42), XSD.float(42.0)},
|
||||
{XSD.integer(42), Age.new(42)},
|
||||
{XSD.float(0.1), DecimalUnitInterval.new(0.1)},
|
||||
{XSD.decimal(0.1), DoubleUnitInterval.new(0.1)},
|
||||
{XSD.double(0.1), FloatUnitInterval.new(0.1)},
|
||||
{DecimalUnitInterval.new(0.1), DoubleUnitInterval.new(0.1)},
|
||||
{XSD.non_negative_integer(42), XSD.decimal(42.0)},
|
||||
{XSD.non_negative_integer(42), XSD.double(42.0)},
|
||||
{XSD.positive_integer(42), XSD.decimal(42.0)},
|
||||
|
@ -187,7 +199,8 @@ defmodule RDF.EqualityTest do
|
|||
@unequal_numerics [
|
||||
{XSD.integer(1), XSD.integer(2)},
|
||||
{XSD.integer("1"), XSD.double("1.1")},
|
||||
{XSD.integer("1"), XSD.decimal("1.1")}
|
||||
{XSD.integer("1"), XSD.decimal("1.1")},
|
||||
{DecimalUnitInterval.new(0.1), DoubleUnitInterval.new(0.2)},
|
||||
]
|
||||
@equal_numerics_by_coercion [
|
||||
{XSD.integer(42), 42},
|
||||
|
@ -214,7 +227,8 @@ defmodule RDF.EqualityTest do
|
|||
{XSD.double("foo"), XSD.double("foo")},
|
||||
{XSD.float("foo"), XSD.float("foo")},
|
||||
{XSD.non_negative_integer("foo"), XSD.non_negative_integer("foo")},
|
||||
{XSD.positive_integer("foo"), XSD.positive_integer("foo")}
|
||||
{XSD.positive_integer("foo"), XSD.positive_integer("foo")},
|
||||
{DecimalUnitInterval.new(1.1), DecimalUnitInterval.new(1.1)},
|
||||
]
|
||||
@unequal_invalid_numerics [
|
||||
{XSD.integer("foo"), XSD.integer("bar")},
|
||||
|
@ -224,7 +238,8 @@ defmodule RDF.EqualityTest do
|
|||
{XSD.double("foo"), XSD.double("bar")},
|
||||
{XSD.float("foo"), XSD.float("bar")},
|
||||
{XSD.non_negative_integer("foo"), XSD.non_negative_integer("bar")},
|
||||
{XSD.positive_integer("foo"), XSD.positive_integer("bar")}
|
||||
{XSD.positive_integer("foo"), XSD.positive_integer("bar")},
|
||||
{DecimalUnitInterval.new(1.1), DoubleUnitInterval.new(1.2)},
|
||||
]
|
||||
@incomparable_numerics [
|
||||
{XSD.integer("42"), nil},
|
||||
|
@ -250,7 +265,8 @@ defmodule RDF.EqualityTest do
|
|||
describe "XSD.DateTime" do
|
||||
@term_equal_datetimes [
|
||||
{XSD.datetime("2002-04-02T12:00:00-01:00"), XSD.datetime("2002-04-02T12:00:00-01:00")},
|
||||
{XSD.datetime("2002-04-02T12:00:00"), XSD.datetime("2002-04-02T12:00:00")}
|
||||
{XSD.datetime("2002-04-02T12:00:00"), XSD.datetime("2002-04-02T12:00:00")},
|
||||
{DateTimeWithTz.new("2002-04-02T12:00:00Z"), DateTimeWithTz.new("2002-04-02T12:00:00Z")},
|
||||
]
|
||||
@value_equal_datetimes [
|
||||
{XSD.datetime("2002-04-02T12:00:00-01:00"), XSD.datetime("2002-04-02T17:00:00+04:00")},
|
||||
|
@ -261,11 +277,16 @@ defmodule RDF.EqualityTest do
|
|||
{XSD.datetime("2010-01-01T00:00:00+00:00"), XSD.datetime("2010-01-01T00:00:00Z")},
|
||||
{XSD.datetime("2002-04-02T23:00:00+00:00"), XSD.datetime("2002-04-02T23:00:00-00:00")},
|
||||
{XSD.datetime("2010-01-01T00:00:00.0000Z"), XSD.datetime("2010-01-01T00:00:00Z")},
|
||||
{XSD.datetime("2005-04-04T24:00:00"), XSD.datetime("2005-04-05T00:00:00")}
|
||||
{XSD.datetime("2005-04-04T24:00:00"), XSD.datetime("2005-04-05T00:00:00")},
|
||||
|
||||
{DateTimeWithTz.new("2002-04-02T12:00:00-01:00"), DateTimeWithTz.new("2002-04-02T17:00:00+04:00")},
|
||||
{DateTimeWithTz.new("2002-04-02T23:00:00Z"), XSD.datetime("2002-04-02T23:00:00+00:00")},
|
||||
{XSD.datetime("2002-04-02T23:00:00+00:00"), DateTimeWithTz.new("2002-04-02T23:00:00-00:00")},
|
||||
]
|
||||
@unequal_datetimes [
|
||||
{XSD.datetime("2002-04-02T12:00:00"), XSD.datetime("2002-04-02T17:00:00")},
|
||||
{XSD.datetime("2005-04-04T24:00:00"), XSD.datetime("2005-04-04T00:00:00")}
|
||||
{XSD.datetime("2005-04-04T24:00:00"), XSD.datetime("2005-04-04T00:00:00")},
|
||||
{DateTimeWithTz.new("2005-04-04T24:00:00"), DateTimeWithTz.new("2005-04-04T00:00:00")}
|
||||
]
|
||||
@equal_datetimes_by_coercion [
|
||||
{XSD.datetime("2002-04-02T12:00:00-01:00"),
|
||||
|
@ -285,17 +306,21 @@ defmodule RDF.EqualityTest do
|
|||
elem(DateTime.from_iso8601("2002-04-02T12:00:00+00:00"), 1)}
|
||||
]
|
||||
@equal_invalid_datetimes [
|
||||
{XSD.datetime("foo"), XSD.datetime("foo")}
|
||||
{XSD.datetime("foo"), XSD.datetime("foo")},
|
||||
{DateTimeWithTz.new("foo"), DateTimeWithTz.new("foo")}
|
||||
]
|
||||
@unequal_invalid_datetimes [
|
||||
{XSD.datetime("foo"), XSD.datetime("bar")}
|
||||
{XSD.datetime("foo"), XSD.datetime("bar")},
|
||||
{DateTimeWithTz.new("foo"), DateTimeWithTz.new("bar")},
|
||||
{XSD.datetime("foo"), DateTimeWithTz.new("bar")}
|
||||
]
|
||||
@incomparable_datetimes [
|
||||
{XSD.datetime("2002-04-02T12:00:00"), XSD.datetime("2002-04-02T12:00:00Z")},
|
||||
{XSD.datetime("2010-01-01T00:00:00Z"), XSD.datetime("2010-01-01T00:00:00")},
|
||||
{XSD.string("2002-04-02T12:00:00-01:00"), XSD.datetime("2002-04-02T12:00:00-01:00")},
|
||||
# These are incomparable because of indeterminacy due to missing timezone
|
||||
{XSD.datetime("2002-04-02T12:00:00"), XSD.datetime("2002-04-02T23:00:00+00:00")}
|
||||
{XSD.datetime("2002-04-02T12:00:00"), XSD.datetime("2002-04-02T23:00:00+00:00")},
|
||||
{XSD.datetime("2002-04-02T12:00:00"), DateTimeWithTz.new("2002-04-02T12:00:00Z")},
|
||||
]
|
||||
|
||||
test "term equality", do: assert_term_equal(@term_equal_datetimes)
|
||||
|
@ -311,15 +336,18 @@ defmodule RDF.EqualityTest do
|
|||
describe "XSD.Date" do
|
||||
@term_equal_dates [
|
||||
{XSD.date("2002-04-02-01:00"), XSD.date("2002-04-02-01:00")},
|
||||
{XSD.date("2002-04-02"), XSD.date("2002-04-02")}
|
||||
{XSD.date("2002-04-02"), XSD.date("2002-04-02")},
|
||||
{DateWithoutTz.new("2002-04-02"), DateWithoutTz.new("2002-04-02")},
|
||||
]
|
||||
@value_equal_dates [
|
||||
{XSD.date("2002-04-02-00:00"), XSD.date("2002-04-02+00:00")},
|
||||
{XSD.date("2002-04-02Z"), XSD.date("2002-04-02+00:00")},
|
||||
{XSD.date("2002-04-02Z"), XSD.date("2002-04-02-00:00")}
|
||||
{XSD.date("2002-04-02Z"), XSD.date("2002-04-02-00:00")},
|
||||
{XSD.date("2002-04-02"), DateWithoutTz.new("2002-04-02")},
|
||||
]
|
||||
@unequal_dates [
|
||||
{XSD.date("2002-04-01"), XSD.date("2002-04-02")}
|
||||
{XSD.date("2002-04-01"), XSD.date("2002-04-02")},
|
||||
{DateWithoutTz.new("2002-04-02"), DateWithoutTz.new("2002-04-01")},
|
||||
]
|
||||
@equal_dates_by_coercion [
|
||||
{XSD.date("2002-04-02"), Date.from_iso8601!("2002-04-02")}
|
||||
|
@ -328,11 +356,14 @@ defmodule RDF.EqualityTest do
|
|||
{XSD.date("2002-04-02"), Date.from_iso8601!("2002-04-03")}
|
||||
]
|
||||
@equal_invalid_dates [
|
||||
{XSD.date("foo"), XSD.date("foo")}
|
||||
{XSD.date("foo"), XSD.date("foo")},
|
||||
{DateWithoutTz.new("foo"), DateWithoutTz.new("foo")},
|
||||
]
|
||||
@unequal_invalid_dates [
|
||||
{XSD.date("2002.04.02"), XSD.date("2002-04-02")},
|
||||
{XSD.date("foo"), XSD.date("bar")}
|
||||
{XSD.date("foo"), XSD.date("bar")},
|
||||
{DateWithoutTz.new("foo"), DateWithoutTz.new("bar")},
|
||||
{XSD.date("foo"), DateWithoutTz.new("bar")},
|
||||
]
|
||||
@incomparable_dates [
|
||||
{XSD.date("2002-04-02"), XSD.string("2002-04-02")},
|
||||
|
@ -402,14 +433,18 @@ defmodule RDF.EqualityTest do
|
|||
describe "XSD.Time" do
|
||||
@term_equal_times [
|
||||
{XSD.time("12:00:00+01:00"), XSD.time("12:00:00+01:00")},
|
||||
{XSD.time("12:00:00"), XSD.time("12:00:00")}
|
||||
{XSD.time("12:00:00"), XSD.time("12:00:00")},
|
||||
{CustomTime.new("00:00:00Z"), CustomTime.new("00:00:00Z")},
|
||||
]
|
||||
@value_equal_times [
|
||||
{XSD.time("00:00:00+00:00"), XSD.time("00:00:00Z")}
|
||||
{XSD.time("00:00:00+00:00"), XSD.time("00:00:00Z")},
|
||||
{XSD.time("00:00:00+00:00"), CustomTime.new("00:00:00Z")},
|
||||
{CustomTime.new("00:00:00+00:00"), CustomTime.new("00:00:00Z")},
|
||||
]
|
||||
@unequal_times [
|
||||
{XSD.time("12:00:00"), XSD.time("13:00:00")},
|
||||
{XSD.time("00:00:00.0000Z"), XSD.time("00:00:00Z")}
|
||||
{XSD.time("00:00:00.0000Z"), XSD.time("00:00:00Z")},
|
||||
{XSD.time("00:00:00.0000Z"), CustomTime.new("00:00:00Z")},
|
||||
]
|
||||
@equal_times_by_coercion [
|
||||
{XSD.time("12:00:00"), Time.from_iso8601!("12:00:00")}
|
||||
|
@ -418,14 +453,17 @@ defmodule RDF.EqualityTest do
|
|||
{XSD.time("12:00:00"), Time.from_iso8601!("13:00:00")}
|
||||
]
|
||||
@equal_invalid_times [
|
||||
{XSD.time("foo"), XSD.time("foo")}
|
||||
{XSD.time("foo"), XSD.time("foo")},
|
||||
{CustomTime.new("foo"), CustomTime.new("foo")},
|
||||
]
|
||||
@unequal_invalid_times [
|
||||
{XSD.time("foo"), XSD.time("bar")}
|
||||
{XSD.time("foo"), XSD.time("bar")},
|
||||
{XSD.time("foo"), CustomTime.new("bar")},
|
||||
]
|
||||
@incomparable_times [
|
||||
{XSD.time("12:00:00"), XSD.string("12:00:00")},
|
||||
{XSD.time("00:00:00"), XSD.time("00:00:00Z")},
|
||||
{CustomTime.new("00:00:00"), CustomTime.new("00:00:00Z")},
|
||||
{XSD.time("00:00:00.0000"), XSD.time("00:00:00Z")}
|
||||
]
|
||||
|
||||
|
|
38
test/unit/xsd/datatype_test.exs
Normal file
38
test/unit/xsd/datatype_test.exs
Normal file
|
@ -0,0 +1,38 @@
|
|||
defmodule RDF.XSD.DatatypeTest do
|
||||
use RDF.Test.Case
|
||||
|
||||
alias RDF.TestDatatypes.{CustomTime, Age}
|
||||
|
||||
describe "most_specific/2" do
|
||||
test "when equal" do
|
||||
assert XSD.Datatype.most_specific(XSD.Integer, XSD.Integer) == XSD.Integer
|
||||
assert XSD.Datatype.most_specific(XSD.Byte, XSD.Byte) == XSD.Byte
|
||||
assert XSD.Datatype.most_specific(CustomTime, CustomTime) == CustomTime
|
||||
end
|
||||
|
||||
test "when one is derived from the other datatype" do
|
||||
%{
|
||||
XSD.Byte => {XSD.Integer, XSD.Byte},
|
||||
XSD.UnsignedShort => {XSD.UnsignedInt, XSD.UnsignedShort},
|
||||
Age => {XSD.Integer, Age},
|
||||
CustomTime => {XSD.Time, CustomTime}
|
||||
}
|
||||
|> Enum.each(fn {most_specific, {left, right}} ->
|
||||
assert XSD.Datatype.most_specific(left, right) == most_specific
|
||||
assert XSD.Datatype.most_specific(right, left) == most_specific
|
||||
end)
|
||||
end
|
||||
|
||||
test "when independent" do
|
||||
[
|
||||
{XSD.Double, XSD.Byte},
|
||||
{XSD.NegativeInteger, XSD.UnsignedShort},
|
||||
{XSD.Date, CustomTime}
|
||||
]
|
||||
|> Enum.each(fn {left, right} ->
|
||||
refute XSD.Datatype.most_specific(left, right)
|
||||
refute XSD.Datatype.most_specific(right, left)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue