65bb0831b8
This also fixes the undefined-function-warnings raised in the previous version when using terms from non-strict vocabulary namespaces (incl. the auto-generated ad-hoc vocabulary namespaces).
347 lines
11 KiB
Elixir
347 lines
11 KiB
Elixir
defmodule RDF do
|
|
@moduledoc """
|
|
The top-level module of RDF.ex.
|
|
|
|
RDF.ex consists of:
|
|
|
|
- modules for the nodes of an RDF graph
|
|
- `RDF.Term`
|
|
- `RDF.IRI`
|
|
- `RDF.BlankNode`
|
|
- `RDF.Literal`
|
|
- the `RDF.Literal.Datatype` system
|
|
- a facility for the mapping of URIs of a vocabulary to Elixir modules and
|
|
functions: `RDF.Vocabulary.Namespace`
|
|
- a facility for the automatic generation of resource identifiers: `RDF.Resource.Generator`
|
|
- modules for the construction of statements
|
|
- `RDF.Triple`
|
|
- `RDF.Quad`
|
|
- `RDF.Statement`
|
|
- modules for collections of statements
|
|
- `RDF.Description`
|
|
- `RDF.Graph`
|
|
- `RDF.Dataset`
|
|
- `RDF.Data`
|
|
- `RDF.List`
|
|
- `RDF.Diff`
|
|
- functions to construct and execute basic graph pattern queries: `RDF.Query`
|
|
- functions for working with RDF serializations: `RDF.Serialization`
|
|
- behaviours for the definition of RDF serialization formats
|
|
- `RDF.Serialization.Format`
|
|
- `RDF.Serialization.Decoder`
|
|
- `RDF.Serialization.Encoder`
|
|
- and the implementation of various RDF serialization formats
|
|
- `RDF.NTriples`
|
|
- `RDF.NQuads`
|
|
- `RDF.Turtle`
|
|
|
|
This top-level module provides shortcut functions for the construction of the
|
|
basic elements and structures of RDF and some general helper functions.
|
|
|
|
For a general introduction you may refer to the guides on the [homepage](https://rdf-elixir.dev).
|
|
"""
|
|
|
|
alias RDF.{
|
|
IRI,
|
|
BlankNode,
|
|
Literal,
|
|
Namespace,
|
|
Description,
|
|
Graph,
|
|
Dataset,
|
|
Serialization,
|
|
PrefixMap
|
|
}
|
|
|
|
import RDF.Guards
|
|
import RDF.Utils.Bootstrapping
|
|
|
|
@star? Application.get_env(:rdf, :star, true)
|
|
@doc """
|
|
Returns whether RDF-star support is enabled.
|
|
"""
|
|
def star?(), do: @star?
|
|
|
|
defdelegate default_base_iri(), to: IRI, as: :default_base
|
|
|
|
@standard_prefixes PrefixMap.new(
|
|
xsd: xsd_iri_base(),
|
|
rdf: rdf_iri_base(),
|
|
rdfs: rdfs_iri_base()
|
|
)
|
|
|
|
@doc """
|
|
A fixed set prefixes that will always be part of the `default_prefixes/0`.
|
|
|
|
```elixir
|
|
#{inspect(@standard_prefixes, pretty: true)}
|
|
```
|
|
|
|
See `default_prefixes/0`, if you don't want these standard prefixes to be part
|
|
of the default prefixes.
|
|
"""
|
|
def standard_prefixes(), do: @standard_prefixes
|
|
|
|
@doc """
|
|
A user-defined `RDF.PrefixMap` of prefixes to IRI namespaces.
|
|
|
|
This prefix map will be used implicitly wherever a prefix map is expected, but
|
|
not provided. For example, when you don't pass a prefix map to the Turtle serializer,
|
|
this prefix map will be used.
|
|
|
|
By default the `standard_prefixes/0` are part of this prefix map, but you can
|
|
define additional default prefixes via the `default_prefixes` compile-time
|
|
configuration.
|
|
|
|
For example:
|
|
|
|
config :rdf,
|
|
default_prefixes: %{
|
|
ex: "http://example.com/"
|
|
}
|
|
|
|
You can also set `:default_prefixes` to a module-function tuple `{mod, fun}`
|
|
with a function which should be called to determine the default prefixes.
|
|
|
|
If you don't want the `standard_prefixes/0` to be part of the default prefixes,
|
|
or you want to map the standard prefixes to different namespaces (strongly discouraged!),
|
|
you can set the `use_standard_prefixes` compile-time configuration flag to `false`.
|
|
|
|
config :rdf,
|
|
use_standard_prefixes: false
|
|
|
|
"""
|
|
case Application.get_env(:rdf, :default_prefixes, %{}) do
|
|
{mod, fun} ->
|
|
if Application.get_env(:rdf, :use_standard_prefixes, true) do
|
|
def default_prefixes() do
|
|
PrefixMap.merge!(@standard_prefixes, apply(unquote(mod), unquote(fun), []))
|
|
end
|
|
else
|
|
def default_prefixes(), do: apply(unquote(mod), unquote(fun), [])
|
|
end
|
|
|
|
default_prefixes ->
|
|
@default_prefixes PrefixMap.new(default_prefixes)
|
|
if Application.get_env(:rdf, :use_standard_prefixes, true) do
|
|
def default_prefixes() do
|
|
PrefixMap.merge!(@standard_prefixes, @default_prefixes)
|
|
end
|
|
else
|
|
def default_prefixes(), do: @default_prefixes
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Returns the `default_prefixes/0` with additional prefix mappings.
|
|
|
|
The `prefix_mappings` can be given in any format accepted by `RDF.PrefixMap.new/1`.
|
|
"""
|
|
def default_prefixes(prefix_mappings) do
|
|
default_prefixes() |> PrefixMap.merge!(prefix_mappings)
|
|
end
|
|
|
|
defdelegate read_string(string, opts), to: Serialization
|
|
defdelegate read_string!(string, opts), to: Serialization
|
|
defdelegate read_stream(stream, opts \\ []), to: Serialization
|
|
defdelegate read_stream!(stream, opts \\ []), to: Serialization
|
|
defdelegate read_file(filename, opts \\ []), to: Serialization
|
|
defdelegate read_file!(filename, opts \\ []), to: Serialization
|
|
defdelegate write_string(data, opts), to: Serialization
|
|
defdelegate write_string!(data, opts), to: Serialization
|
|
defdelegate write_stream(data, opts), to: Serialization
|
|
defdelegate write_file(data, filename, opts \\ []), to: Serialization
|
|
defdelegate write_file!(data, filename, opts \\ []), to: Serialization
|
|
|
|
@doc """
|
|
Checks if the given value is a RDF resource.
|
|
|
|
## Examples
|
|
|
|
Supposed `EX` is a `RDF.Vocabulary.Namespace` and `Foo` is not.
|
|
|
|
iex> RDF.resource?(RDF.iri("http://example.com/resource"))
|
|
true
|
|
iex> RDF.resource?(EX.resource)
|
|
true
|
|
iex> RDF.resource?(EX.Resource)
|
|
true
|
|
iex> RDF.resource?(Foo.Resource)
|
|
false
|
|
iex> RDF.resource?(RDF.bnode)
|
|
true
|
|
iex> RDF.resource?(RDF.XSD.integer(42))
|
|
false
|
|
iex> RDF.resource?(42)
|
|
false
|
|
"""
|
|
def resource?(value)
|
|
def resource?(%IRI{}), do: true
|
|
def resource?(%BlankNode{}), do: true
|
|
|
|
def resource?(qname) when maybe_ns_term(qname) do
|
|
case Namespace.resolve_term(qname) do
|
|
{:ok, iri} -> resource?(iri)
|
|
_ -> false
|
|
end
|
|
end
|
|
|
|
if @star? do
|
|
def resource?({_, _, _} = triple), do: RDF.Triple.valid?(triple)
|
|
end
|
|
|
|
def resource?(_), do: false
|
|
|
|
@doc """
|
|
Checks if the given value is a RDF term.
|
|
|
|
## Examples
|
|
|
|
Supposed `EX` is a `RDF.Vocabulary.Namespace` and `Foo` is not.
|
|
|
|
iex> RDF.term?(RDF.iri("http://example.com/resource"))
|
|
true
|
|
iex> RDF.term?(EX.resource)
|
|
true
|
|
iex> RDF.term?(EX.Resource)
|
|
true
|
|
iex> RDF.term?(Foo.Resource)
|
|
false
|
|
iex> RDF.term?(RDF.bnode)
|
|
true
|
|
iex> RDF.term?(RDF.XSD.integer(42))
|
|
true
|
|
iex> RDF.term?(42)
|
|
false
|
|
"""
|
|
def term?(value)
|
|
def term?(%Literal{}), do: true
|
|
def term?(value), do: resource?(value)
|
|
|
|
defdelegate uri?(value), to: IRI, as: :valid?
|
|
defdelegate iri?(value), to: IRI, as: :valid?
|
|
defdelegate uri(value), to: IRI, as: :new
|
|
defdelegate iri(value), to: IRI, as: :new
|
|
defdelegate uri!(value), to: IRI, as: :new!
|
|
defdelegate iri!(value), to: IRI, as: :new!
|
|
|
|
@doc """
|
|
Checks if the given value is a blank node.
|
|
|
|
## Examples
|
|
|
|
iex> RDF.bnode?(RDF.bnode)
|
|
true
|
|
iex> RDF.bnode?(RDF.iri("http://example.com/resource"))
|
|
false
|
|
iex> RDF.bnode?(42)
|
|
false
|
|
"""
|
|
def bnode?(%BlankNode{}), do: true
|
|
def bnode?(_), do: false
|
|
|
|
defdelegate bnode(), 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, opts), to: Literal, as: :new
|
|
|
|
if @star? do
|
|
alias RDF.Star.{Triple, Quad, Statement}
|
|
|
|
defdelegate triple(s, p, o, property_map \\ nil), to: Triple, as: :new
|
|
defdelegate triple(tuple, property_map \\ nil), to: Triple, as: :new
|
|
|
|
defdelegate quad(s, p, o, g, property_map \\ nil), to: Quad, as: :new
|
|
defdelegate quad(tuple, property_map \\ nil), to: Quad, as: :new
|
|
|
|
defdelegate statement(s, p, o), to: Statement, as: :new
|
|
defdelegate statement(s, p, o, g), to: Statement, as: :new
|
|
defdelegate statement(tuple, property_map \\ nil), to: Statement, as: :new
|
|
|
|
defdelegate coerce_subject(subject, property_map \\ nil), to: Statement
|
|
defdelegate coerce_predicate(predicate), to: Statement
|
|
defdelegate coerce_predicate(predicate, property_map), to: Statement
|
|
defdelegate coerce_object(object, property_map \\ nil), to: Statement
|
|
defdelegate coerce_graph_name(graph_name), to: Statement
|
|
else
|
|
alias RDF.{Triple, Quad, Statement}
|
|
|
|
defdelegate triple(s, p, o, property_map \\ nil), to: Triple, as: :new
|
|
defdelegate triple(tuple, property_map \\ nil), to: Triple, as: :new
|
|
|
|
defdelegate quad(s, p, o, g, property_map \\ nil), to: Quad, as: :new
|
|
defdelegate quad(tuple, property_map \\ nil), to: Quad, as: :new
|
|
|
|
defdelegate statement(s, p, o), to: Statement, as: :new
|
|
defdelegate statement(s, p, o, g), to: Statement, as: :new
|
|
defdelegate statement(tuple, property_map \\ nil), to: Statement, as: :new
|
|
|
|
defdelegate coerce_subject(subject), to: Statement
|
|
defdelegate coerce_predicate(predicate), to: Statement
|
|
defdelegate coerce_predicate(predicate, property_map), to: Statement
|
|
defdelegate coerce_object(object), to: Statement
|
|
defdelegate coerce_graph_name(graph_name), to: Statement
|
|
end
|
|
|
|
defdelegate description(subject, opts \\ []), to: Description, as: :new
|
|
|
|
defdelegate graph(), to: Graph, as: :new
|
|
defdelegate graph(arg), to: Graph, as: :new
|
|
defdelegate graph(arg1, arg2), to: Graph, as: :new
|
|
|
|
defdelegate dataset(), to: Dataset, as: :new
|
|
defdelegate dataset(arg), to: Dataset, as: :new
|
|
defdelegate dataset(arg1, arg2), to: Dataset, as: :new
|
|
|
|
defdelegate diff(arg1, arg2), to: RDF.Diff
|
|
|
|
defdelegate list?(resource, graph), to: RDF.List, as: :node?
|
|
defdelegate list?(description), to: RDF.List, as: :node?
|
|
|
|
def list(native_list), do: RDF.List.from(native_list)
|
|
def list(head, %Graph{} = graph), do: RDF.List.new(head, graph)
|
|
def list(native_list, opts), do: RDF.List.from(native_list, opts)
|
|
|
|
defdelegate prefix_map(prefixes), to: RDF.PrefixMap, as: :new
|
|
defdelegate property_map(property_map), to: RDF.PropertyMap, as: :new
|
|
|
|
############################################################################
|
|
# These alias functions for the RDF.NS.RDF namespace are mandatory.
|
|
# Without them the property functions are inaccessible, since the namespace
|
|
# can't be aliased, because it gets in conflict with the root namespace
|
|
# of the project.
|
|
|
|
defdelegate langString(value, opts), to: RDF.LangString, as: :new
|
|
defdelegate lang_string(value, opts), to: RDF.LangString, as: :new
|
|
|
|
for term <- ~w[type subject predicate object first rest value]a do
|
|
defdelegate unquote(term)(), to: RDF.NS.RDF
|
|
@doc false
|
|
defdelegate unquote(term)(s, o), to: RDF.NS.RDF
|
|
@doc false
|
|
defdelegate unquote(term)(s, o1, o2), to: RDF.NS.RDF
|
|
@doc false
|
|
defdelegate unquote(term)(s, o1, o2, o3), to: RDF.NS.RDF
|
|
@doc false
|
|
defdelegate unquote(term)(s, o1, o2, o3, o4), to: RDF.NS.RDF
|
|
@doc false
|
|
defdelegate unquote(term)(s, o1, o2, o3, o4, o5), to: RDF.NS.RDF
|
|
end
|
|
|
|
defdelegate langString(), to: RDF.NS.RDF
|
|
defdelegate lang_string(), to: RDF.NS.RDF, as: :langString
|
|
defdelegate unquote(nil)(), to: RDF.NS.RDF
|
|
|
|
defdelegate __base_iri__(), to: RDF.NS.RDF
|
|
defdelegate __terms__(), to: RDF.NS.RDF
|
|
defdelegate __iris__(), to: RDF.NS.RDF
|
|
defdelegate __strict__(), to: RDF.NS.RDF
|
|
defdelegate __resolve_term__(term), to: RDF.NS.RDF
|
|
end
|