Add proper handling of vocabulary terms at various places

- in the coerce and value functions of the RDF.Term protocol for atoms
- allow them as XSD.AnyURI values
- RDF.Literal.coerce produces XSD.AnyURI literals from them
- allow them in equal_value comparisons with RDF.IRIs and XSD.AnyURIs
This commit is contained in:
Marcel Otto 2020-05-17 00:53:36 +02:00
parent 76b6f69613
commit 47a19c0a67
8 changed files with 87 additions and 5 deletions

View file

@ -238,6 +238,16 @@ defmodule RDF.IRI do
def equal_value?(%__MODULE__{value: left}, %URI{} = right),
do: left == URI.to_string(right)
def equal_value?(left, %__MODULE__{} = right) when maybe_ns_term(left),
do: equal_value?(right, left)
def equal_value?(%__MODULE__{} = left, right) when maybe_ns_term(right) do
case Namespace.resolve_term(right) do
{:ok, iri} -> equal_value?(left, iri)
_ -> nil
end
end
def equal_value?(_, _),
do: nil

View file

@ -8,6 +8,8 @@ defmodule RDF.Literal do
alias RDF.{IRI, LangString}
alias RDF.Literal.{Generic, Datatype}
import RDF.Guards
@type t :: %__MODULE__{:literal => Datatype.literal()}
@rdf_lang_string RDF.Utils.Bootstrapping.rdf_iri("langString")
@ -103,6 +105,13 @@ defmodule RDF.Literal do
def coerce(%NaiveDateTime{} = value), do: RDF.XSD.DateTime.new(value)
def coerce(%URI{} = value), do: RDF.XSD.AnyURI.new(value)
def coerce(value) when maybe_ns_term(value) do
case RDF.Namespace.resolve_term(value) do
{:ok, iri} -> iri |> IRI.parse() |> coerce()
_ -> nil
end
end
# 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
Enum.each(Datatype.Registry.builtin_datatypes(), fn datatype ->

View file

@ -134,11 +134,18 @@ defimpl RDF.Term, for: Atom do
def coerce(true), do: RDF.XSD.true
def coerce(false), do: RDF.XSD.false
def coerce(_), do: nil
def coerce(nil), do: nil
def coerce(term) do
case RDF.Namespace.resolve_term(term) do
{:ok, iri} -> iri
_ -> nil
end
end
def value(true), do: true
def value(false), do: false
def value(_), do: nil
def value(nil), do: nil
def value(term), do: RDF.Term.value(coerce(term))
def term?(_), do: false
end

View file

@ -9,6 +9,8 @@ defmodule RDF.XSD.AnyURI do
alias RDF.IRI
import RDF.Guards
use RDF.XSD.Datatype.Primitive,
name: "anyURI",
id: RDF.Utils.Bootstrapping.xsd_iri("anyURI")
@ -20,6 +22,14 @@ defmodule RDF.XSD.AnyURI do
@impl RDF.XSD.Datatype
@spec elixir_mapping(any, Keyword.t()) :: value
def elixir_mapping(%URI{} = uri, _), do: uri
def elixir_mapping(value, _) when maybe_ns_term(value) do
case RDF.Namespace.resolve_term(value) do
{:ok, iri} -> IRI.parse(iri)
_ -> @invalid_value
end
end
def elixir_mapping(_, _), do: @invalid_value
@impl RDF.Literal.Datatype
@ -35,5 +45,15 @@ defmodule RDF.XSD.AnyURI do
def do_equal_value?(%__MODULE__{} = 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?(%__MODULE__{} = left, right) when maybe_ns_term(right) do
case RDF.Namespace.resolve_term(right) do
{:ok, iri} -> equal_value?(left, iri)
_ -> nil
end
end
def do_equal_value?(literal1, literal2), do: super(literal1, literal2)
end

View file

@ -18,10 +18,14 @@ defmodule RDF.EqualityTest do
@equal_iris_by_coercion [
{RDF.iri("http://example.com/"), URI.parse("http://example.com/")},
{XSD.anyURI("http://example.com/"), URI.parse("http://example.com/")},
{RDF.iri("http://example.com/Foo"), EX.Foo},
{XSD.anyURI("http://example.com/Foo"), EX.Foo},
]
@unequal_iris_by_coercion [
{RDF.iri("http://example.com/foo"), URI.parse("http://example.com/bar")},
{XSD.anyURI("http://example.com/foo"), URI.parse("http://example.com/bar")},
{RDF.iri("http://example.com/Bar"), EX.Foo},
{XSD.anyURI("http://example.com/Bar"), EX.Foo},
]
@incomparable_iris [
{RDF.iri("http://example.com/"), XSD.string("http://example.com/")},

View file

@ -168,6 +168,15 @@ defmodule RDF.LiteralTest do
XSD.any_uri("http://example.com")
end
test "with a resolvable vocabulary namespace term atom" do
assert Literal.coerce(EX.Foo) == EX.Foo |> RDF.iri() |> IRI.parse() |> XSD.any_uri()
end
test "with a non-resolvable atom" do
refute Literal.coerce(Foo)
refute Literal.coerce(:foo)
end
test "with RDF.Literals" do
assert XSD.integer(42) |> Literal.coerce() == XSD.integer(42)
end
@ -177,7 +186,8 @@ defmodule RDF.LiteralTest do
end
test "with inconvertible values" do
assert self() |> Literal.coerce() == nil
refute Literal.coerce(nil)
refute Literal.coerce(self())
end
end

View file

@ -18,6 +18,16 @@ defmodule RDF.TermTest do
assert RDF.Term.coerce(~L"foo") == ~L"foo"
end
test "with a resolvable vocabulary namespace term atom" do
assert RDF.Term.coerce(EX.Foo) == RDF.iri(EX.Foo)
end
test "with a non-resolvable atom" do
refute RDF.Term.coerce(nil)
refute RDF.Term.coerce(Foo)
refute RDF.Term.coerce(:foo)
end
test "with boolean" do
assert RDF.Term.coerce(true) == XSD.true
assert RDF.Term.coerce(false) == XSD.false
@ -83,6 +93,16 @@ defmodule RDF.TermTest do
assert XSD.integer("foo") |> RDF.Term.value() == "foo"
end
test "with a resolvable vocabulary namespace term atom" do
assert RDF.Term.value(EX.Foo) == EX.Foo |> RDF.iri() |> to_string()
end
test "with a non-resolvable atom" do
refute RDF.Term.value(nil)
refute RDF.Term.value(Foo)
refute RDF.Term.value(:foo)
end
test "with boolean" do
assert RDF.Term.value(true) == true
assert RDF.Term.value(false) == false

View file

@ -8,9 +8,11 @@ defmodule RDF.XSD.AnyURITest do
"http://example.com/foo" =>
{URI.parse("http://example.com/foo"), nil, "http://example.com/foo"},
URI.parse("http://example.com/foo") =>
{URI.parse("http://example.com/foo"), nil, "http://example.com/foo"}
{URI.parse("http://example.com/foo"), nil, "http://example.com/foo"},
RDF.List =>
{URI.parse("http://www.w3.org/1999/02/22-rdf-syntax-ns#List"), nil, "http://www.w3.org/1999/02/22-rdf-syntax-ns#List"},
},
invalid: [42, 3.14, true, false]
invalid: [42, 3.14, Foo, :foo, true, false]
describe "cast/1" do