From 15002a0bbb7c01c2d81c9514fa488859dec31af7 Mon Sep 17 00:00:00 2001 From: Marcel Otto Date: Wed, 28 Oct 2020 11:51:00 +0100 Subject: [PATCH] Add RDF.Dataset.prefixes/1 --- CHANGELOG.md | 1 + lib/rdf/blank_node/increment.ex | 6 ++++ lib/rdf/dataset.ex | 18 ++++++++++- lib/rdf/serializations/turtle_encoder.ex | 40 +++++++++++------------- test/support/rdf_case.ex | 3 +- test/unit/dataset_test.exs | 8 +++++ 6 files changed, 52 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a671151..7e514a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ This project adheres to [Semantic Versioning](http://semver.org/) and ### Added +- `RDF.Dataset.prefixes/1` for getting an aggregated `RDF.PrefixMap` over all graphs - `RDF.PrefixMap.put/3` for adding a prefix mapping and overwrite an existing one ### Changed diff --git a/lib/rdf/blank_node/increment.ex b/lib/rdf/blank_node/increment.ex index 196c581..e3fd389 100644 --- a/lib/rdf/blank_node/increment.ex +++ b/lib/rdf/blank_node/increment.ex @@ -14,6 +14,12 @@ defmodule RDF.BlankNode.Increment do alias RDF.BlankNode + @type state :: %{ + optional(:prefix) => String.t(), + map: map, + counter: pos_integer + } + @impl BlankNode.Generator.Algorithm def init(%{prefix: prefix} = opts) do opts diff --git a/lib/rdf/dataset.ex b/lib/rdf/dataset.ex index a9f4a33..afd6b9b 100644 --- a/lib/rdf/dataset.ex +++ b/lib/rdf/dataset.ex @@ -17,7 +17,7 @@ defmodule RDF.Dataset do @behaviour Access - alias RDF.{Graph, Description, IRI, Statement, PropertyMap} + alias RDF.{Graph, Description, IRI, Statement, PrefixMap, PropertyMap} import RDF.Statement, only: [coerce_subject: 1, coerce_graph_name: 1] import RDF.Utils @@ -853,6 +853,22 @@ defmodule RDF.Dataset do def equal?(_, _), do: false + @doc """ + Returns the aggregated prefixes of all graphs of `dataset` as a `RDF.PrefixMap`. + """ + @spec prefixes(t) :: PrefixMap.t() | nil + def prefixes(%__MODULE__{} = dataset) do + dataset + |> RDF.Dataset.graphs() + |> Enum.reduce(RDF.PrefixMap.new(), fn graph, prefixes -> + if graph.prefixes do + RDF.PrefixMap.merge!(prefixes, graph.prefixes, :ignore) + else + prefixes + end + end) + end + defp clear_metadata(%__MODULE__{} = dataset) do %__MODULE__{ dataset diff --git a/lib/rdf/serializations/turtle_encoder.ex b/lib/rdf/serializations/turtle_encoder.ex index f8e45fd..df92d73 100644 --- a/lib/rdf/serializations/turtle_encoder.ex +++ b/lib/rdf/serializations/turtle_encoder.ex @@ -4,7 +4,7 @@ defmodule RDF.Turtle.Encoder do use RDF.Serialization.Encoder alias RDF.Turtle.Encoder.State - alias RDF.{BlankNode, Dataset, Description, Graph, IRI, XSD, Literal, LangString} + alias RDF.{BlankNode, Dataset, Description, Graph, IRI, XSD, Literal, LangString, PrefixMap} @document_structure [ :base, @@ -36,14 +36,19 @@ defmodule RDF.Turtle.Encoder do @ordered_properties MapSet.new(@predicate_order) @impl RDF.Serialization.Encoder - @callback encode(Graph.t() | Dataset.t(), keyword | map) :: {:ok, String.t()} | {:error, any} + @spec encode(Graph.t() | Dataset.t(), keyword) :: {:ok, String.t()} | {:error, any} def encode(data, opts \\ []) do - with base = - Keyword.get(opts, :base, Keyword.get(opts, :base_iri)) - |> base_iri(data) - |> init_base_iri(), - prefixes = Keyword.get(opts, :prefixes) |> prefixes(data) |> init_prefixes(), - {:ok, state} = State.start_link(data, base, prefixes) do + base = + Keyword.get(opts, :base, Keyword.get(opts, :base_iri)) + |> base_iri(data) + |> init_base_iri() + + prefixes = + Keyword.get(opts, :prefixes) + |> prefixes(data) + |> init_prefixes() + + with {:ok, state} = State.start_link(data, base, prefixes) do try do State.preprocess(state) @@ -74,7 +79,7 @@ defmodule RDF.Turtle.Encoder do raise "unknown Turtle document element: #{inspect(element)}" end - defp base_iri(nil, %RDF.Graph{base_iri: base_iri}) when not is_nil(base_iri), do: base_iri + defp base_iri(nil, %Graph{base_iri: base_iri}) when not is_nil(base_iri), do: base_iri defp base_iri(nil, _), do: RDF.default_base_iri() defp base_iri(base_iri, _), do: IRI.coerce_base(base_iri) @@ -91,19 +96,10 @@ defmodule RDF.Turtle.Encoder do end end - defp prefixes(nil, %RDF.Graph{prefixes: prefixes}) when not is_nil(prefixes), do: prefixes + defp prefixes(nil, %Graph{prefixes: prefixes}) when not is_nil(prefixes), do: prefixes - defp prefixes(nil, %RDF.Dataset{} = dataset) do - prefixes = - dataset - |> RDF.Dataset.graphs() - |> Enum.reduce(RDF.PrefixMap.new(), fn graph, prefixes -> - if graph.prefixes do - RDF.PrefixMap.merge!(prefixes, graph.prefixes, :ignore) - else - prefixes - end - end) + defp prefixes(nil, %Dataset{} = dataset) do + prefixes = Dataset.prefixes(dataset) if Enum.empty?(prefixes) do RDF.default_prefixes() @@ -113,7 +109,7 @@ defmodule RDF.Turtle.Encoder do end defp prefixes(nil, _), do: RDF.default_prefixes() - defp prefixes(prefixes, _), do: RDF.PrefixMap.new(prefixes) + defp prefixes(prefixes, _), do: PrefixMap.new(prefixes) defp init_prefixes(prefixes) do Enum.reduce(prefixes, %{}, fn {prefix, iri}, reverse -> diff --git a/test/support/rdf_case.ex b/test/support/rdf_case.ex index e1727ae..67dddff 100644 --- a/test/support/rdf_case.ex +++ b/test/support/rdf_case.ex @@ -11,7 +11,8 @@ defmodule RDF.Test.Case do using do quote do - alias RDF.{Dataset, Graph, Description, IRI, XSD, PropertyMap} + alias RDF.{Dataset, Graph, Description, IRI, XSD, PrefixMap, PropertyMap} + alias RDF.NS.{RDFS, OWL} alias unquote(__MODULE__).{EX, FOAF} import RDF, only: [iri: 1, literal: 1, bnode: 1] diff --git a/test/unit/dataset_test.exs b/test/unit/dataset_test.exs index 5125961..59f247b 100644 --- a/test/unit/dataset_test.exs +++ b/test/unit/dataset_test.exs @@ -1757,6 +1757,14 @@ defmodule RDF.DatasetTest do ) end + test "prefixes/1" do + assert Dataset.new() + |> Dataset.add(Graph.new(prefixes: [ex: EX, foo: RDFS])) + |> Dataset.add(Graph.new(name: EX.Graph, prefixes: [ex: EX, foo: OWL])) + |> Dataset.prefixes() == + PrefixMap.new(ex: EX, foo: RDFS) + end + describe "Enumerable protocol" do test "Enum.count" do assert Enum.count(Dataset.new(name: EX.foo())) == 0