rdf-ex/lib/rdf/vocabulary/namespace/term_mapping.ex

177 lines
5.9 KiB
Elixir

defmodule RDF.Vocabulary.Namespace.TermMapping do
@moduledoc false
import RDF.Namespace.Builder, only: [valid_term?: 1, valid_characters?: 1, reserved_term?: 1]
import RDF.Vocabulary, only: [term_to_iri: 2]
def normalize_terms(module, terms, ignored_terms, strict, opts) do
aliases =
opts
|> Keyword.get(:alias, [])
|> Keyword.new(fn {alias, original_term} ->
{normalize_term(alias), normalize_term(original_term)}
end)
terms = Map.new(terms, &{normalize_term(&1), true})
normalized_terms =
Enum.reduce(aliases, terms, fn {alias, original_term}, terms ->
cond do
reserved_term?(alias) ->
raise RDF.Namespace.InvalidAliasError,
"alias '#{alias}' in vocabulary namespace #{module} is a reserved term and can't be used as an alias"
not valid_characters?(alias) ->
raise RDF.Namespace.InvalidAliasError,
"alias '#{alias}' in vocabulary namespace #{module} contains invalid characters"
Map.get(terms, alias) == true ->
raise RDF.Namespace.InvalidAliasError,
"alias '#{alias}' in vocabulary namespace #{module} already defined"
strict and not Map.has_key?(terms, original_term) ->
raise RDF.Namespace.InvalidAliasError,
"term '#{original_term}' is not a term in vocabulary namespace #{module}"
Map.get(terms, original_term, true) != true ->
raise RDF.Namespace.InvalidAliasError,
"'#{original_term}' is already an alias in vocabulary namespace #{module}"
true ->
if alias in ignored_terms do
IO.warn("ignoring alias '#{alias}' in vocabulary namespace #{module}")
end
if valid_term?(original_term) and original_term not in ignored_terms do
terms
else
Map.delete(terms, original_term)
end
|> Map.put(alias, normalize_aliased_term(original_term))
end
end)
|> Map.drop(MapSet.to_list(ignored_terms))
{normalized_terms, aliases}
end
def term_mapping(base_uri, terms, ignored_terms) do
Enum.flat_map(terms, fn
{term, true} ->
[{term, term_to_iri(base_uri, term)}]
{term, original} ->
iri = term_to_iri(base_uri, original)
original = normalize_term(original)
if valid_term?(original) and original not in ignored_terms do
[{term, iri}, {original, iri}]
else
[{term, iri}]
end
end)
end
def normalize_term(term) when is_atom(term), do: term
def normalize_term(term) when is_binary(term), do: String.to_atom(term)
def normalize_term(term), do: raise(RDF.Namespace.InvalidTermError, inspect(term))
def normalize_aliased_term(term) when is_binary(term), do: term
def normalize_aliased_term(term) when is_atom(term), do: Atom.to_string(term)
def normalize_ignored_terms(terms), do: MapSet.new(terms, &normalize_term/1)
def extract_aliases(terms, opts) do
aliases = opts |> Keyword.get(:alias, []) |> Keyword.new()
{terms, aliases} =
Enum.reduce(terms, {[], aliases}, fn
{_, term} = alias, {terms, aliases} -> {[term | terms], [alias | aliases]}
term, {terms, aliases} -> {[term | terms], aliases}
end)
{terms, Keyword.put(opts, :alias, aliases)}
end
def aliases(terms) do
for {alias, term} <- terms, term != true, do: alias
end
def validate!({terms, ignored_terms}, opts) do
{invalid_terms, invalid_characters} =
Enum.reduce(terms, {[], []}, fn
{term, _}, {invalid_terms, invalid_character_terms} ->
cond do
reserved_term?(term) ->
{[term | invalid_terms], invalid_character_terms}
not valid_characters?(term) ->
{invalid_terms, [term | invalid_character_terms]}
true ->
{
invalid_terms,
invalid_character_terms
}
end
end)
{terms, ignored_terms}
|> handle_invalid_terms!(
invalid_terms,
Keyword.get(opts, :invalid_terms, :fail)
)
|> handle_invalid_characters!(
invalid_characters,
Keyword.get(opts, :invalid_characters, :fail)
)
end
defp handle_invalid_terms!(terms_and_ignored, [], _), do: terms_and_ignored
defp handle_invalid_terms!({terms, aliases}, invalid_terms, :ignore) do
{Map.drop(terms, invalid_terms), MapSet.union(aliases, MapSet.new(invalid_terms))}
end
defp handle_invalid_terms!(_, invalid_terms, :fail) do
raise RDF.Namespace.InvalidTermError, """
The following terms can not be used, because they conflict with the Elixir semantics:
- #{Enum.join(invalid_terms, "\n- ")}
You have the following options:
- define an alias with the :alias option on defvocab
- ignore the resource with the :ignore option on defvocab
"""
end
defp handle_invalid_characters!(terms_and_ignored, [], _), do: terms_and_ignored
defp handle_invalid_characters!({terms, ignored_terms}, invalid_terms, :ignore) do
{Map.drop(terms, invalid_terms), MapSet.union(ignored_terms, MapSet.new(invalid_terms))}
end
defp handle_invalid_characters!(_, invalid_terms, :fail) do
raise RDF.Namespace.InvalidTermError, """
The following terms contain invalid characters:
- #{Enum.join(invalid_terms, "\n- ")}
You have the following options:
- if you are in control of the vocabulary, consider renaming the resource
- define an alias with the :alias option on defvocab
- change the handling of invalid characters with the :invalid_characters option on defvocab
- ignore the resource with the :ignore option on defvocab
"""
end
defp handle_invalid_characters!(terms_and_ignored, invalid_terms, :warn) do
Enum.each(invalid_terms, fn term ->
IO.warn("'#{term}' is not valid term, since it contains invalid characters")
end)
terms_and_ignored
end
end