wOTTR: Web Reasonable Ontology Templates
top
examples:
/
SHACL:
/
Table of Contents
Web Reasonable Ontology Templates (wOTTR)
![wOTTR](https://www.ottr.xyz/logo/wOTTR.jpg)
RDF serialisation format for OTTR templates and instances
- Version
- 0.4.0
- Published
- 2019-03-29
- Authors
- Martin G. Skjæveland
- Issues
- https://gitlab.com/ottr/language/wOTTR/issues
1 Introduction
This specification defines the RDF serialisation of Reasonable Ontology Templates (OTTR), called Web Reasonable Ontology Templates (wOTTR).
With wOTTR it is possible to express templates and template instances in an RDF format that is designed to be compact and readable. Given that wOTTR is defined for RDF it is possible to leverage the existing W3C stack of languages and tools to work with OTTR templates.
The vocabulary of the wOTTR language is specified by an OWL ontology presented in section 2.1 and the grammar is defined by a SHACL shape specification presented in section 2.2. The appendix (section 3) contains unit tests.
This example of a template serialised in wOTTR gives a reasonable
complete introduction to the syntax without further explanation. The
example shows the NamedPizza template which specifies three parameters
(on lines 4, 7 and 11) typed as respectively
owl:Class
, owl:NamedIndividual
and a list of owl:Class
-es. The
second parameter is set as optional.
The template's pattern contains 5 template instances (lines
15, 18, 26, 30 and 34). The
pattern instance of the template ax:SubObjectSomeValuesFrom
(line
18) takes three arguments, where the third argument is "list
expanded" (line 23) using the cross modifier (line
24).
The template is annotated by an annotation instance (line 40).
This template is explained in more detail in section 3.1.
1: pizza:NamedPizza a ottr:Template ; 2: ottr:parameters ( 3: 4: [ ottr:type owl:Class; 5: ottr:variable _:pizza ] 6: 7: [ ottr:type owl:NamedIndividual; 8: ottr:variable _:country; 9: ottr:modifier ottr:optional ] 10: 11: [ ottr:type ( rdf:List owl:Class ); 12: ottr:variable _:toppings; ] ) ; 13: 14: ottr:pattern 15: [ ottr:of ax:SubClassOf ; 16: ottr:values ( _:pizza p:NamedPizza ) ] , 17: 18: [ ottr:of ax:SubObjectSomeValuesFrom ; 19: ottr:arguments ( 20: [ ottr:value _:pizza ] 21: [ ottr:value p:hasTopping ] 22: [ ottr:value _:toppings; 23: ottr:modifier ottr:listExpand ] ) ; 24: ottr:modifier ottr:cross ] , 25: 26: [ ottr:of ax:SubObjectHasValue ; 27: ottr:values 28: ( _:pizza p:hasCountryOfOrigin _:country ) ] , 29: 30: [ ottr:of ax:SubObjectAllValuesFrom ; 31: ottr:values 32: ( :pizza p:hasTopping _:alltoppings ) ] , 33: 34: [ ottr:of rstr:ObjectUnionOf ; 35: ottr:values 36: ( _:alltoppings _:toppings ) ] ; # end pattern 37: 38: ## new feature 39: ottr:annotation 40: [ ottr:of <http://example.net/ns#TemplateAnnotation> ; 41: ottr:values ( pizza:NamedPizza "A pizza template" ) ] .
1.1 Documents
This specification consists of the following files:
- ./core-vocabulary.owl.ttl
- Ontology declaring the core vocabulary of wOTTR.
- ./core-grammar.shacl.ttl
- Shape definitions of wOTTR grammar rules.
- ./all.owl.ttl
- Ontology that imports all OTTR specifications, including the above.
- ./index.html
- This file.
Other files:
- ./examples.zip
- all the examples in this document
- ./tests.zip
- all the unit tests in this document
- ./testbed.zip
- Java 8 Maven project used to test this specification
- ./std-tokens.shacl.ttl
- Shape definitions that restrict
the
rdf:
,rdfs:
,owl:
andsh:
namespace to the standard respective vocabularies. - ./ottr-tokens.shacl.ttl
- Shape definitions that restrict the
ottr:
namespace to the vocabulary defined in this specification.
1.2 Document conventions
1.2.1 Notation
This is a defined term.
This is a mention of a defined term.
This is an example box. Examples are informative.
Examples are standalone and makes no reference to external resources unless explicitly stated. The examples are meant to illustrate use of the syntax, and not necessarily to display interesting or useful templates.
This is a box containing a normative SHACL shape snippet.
1.2.2 Prefixes
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . @prefix owl: <http://www.w3.org/2002/07/owl#> . @prefix skos: <http://www.w3.org/2004/02/skos/core#> . @prefix vann: <http://purl.org/vocab/vann/> . @prefix dc: <http://purl.org/dc/elements/1.1/> . @prefix foaf: <http://xmlns.com/foaf/0.1/> . @prefix dcterms: <http://purl.org/dc/terms/> . @prefix cc: <http://creativecommons.org/ns#> . @prefix sh: <http://www.w3.org/ns/shacl#> . @prefix dash: <http://datashapes.org/dash#> . @prefix ex: <http://example.net/ns#> . @prefix ottr: <http://ns.ottr.xyz/0.4/> . @prefix o-wottr: <http://spec.ottr.xyz/wOTTR/0.4/tpl/> .
1.2.3 Diagrams
The diagrams in this document are informal, but should be understood in the following way:
- Arrows with open heads indicate subclass relationships—pointing to the superclass
- Dotted arrows indicate type relationships—pointing to the class
- Other arrows indicate domain and possibly range restrictions to
properties—pointing from the domain (arrow source) to the
range (arrow target):
- A regular arrowhead indicates a regular domain and range restriction
- A diamond arrowhead indicates that the range is a list of the target class
- An empty blue target indicates that the range is unspecified
2 wOTTR language
First we give an overview of the wOTTR vocabulary, then present each shape in detail.
In order make the presentation more readable, we will frequently
write, "a Class
", e.g., "a ottr:Template
", and take this to mean
a resource of type ottr:Template
. Also, we will write, e.g., "a
ClassX
has a property
which is a ClassY
" and take this to mean
that there must be a triple (x,* property
*, y), where x has
the type ClassX
and y has the type ClassY
, e.g., "an
ottr:Argument
has a ottr:value
which is an RDF resource". The
type information need not be explicitly stated, but can be inferred.
2.1 Vocabulary
The wOTTR vocabulary lies close to the formal vocabulary established in mOTTR
[1] and rOTTR [2]. We will therefore not explicitly
establish the (reasonably obvious) connection between these vocabularies, e.g.,
that the wOTTR vocabulary element ottr:Template
represents a
template as defined in mOTTR [1].
The wOTTR language makes frequent use of RDF lists as a means to represent an ordering of resources. We do this since RDF lists have a succinct serialisation in RDF Turtle, as seen in the above example, which makes template instances syntactically similar to ordinary function calls and predicates. A consequence of using RDF lists is that wOTTR goes beyond the OWL 2 DL language fragment, since RDF lists are used for the serialisation of OWL.
A schematic overview of the ontology is given in Figure 2.
The classes, properties and individuals of the wOTTR vocabulary are presented in the next sections. Each section contains a table. The wOTTR ontology is produced from these tables by converting the tables into OTTR template instances whose expansion, plus some additional ontology metadata, gives the ontology. This process is shown in the appendix.
The tables use the following formatting rules:
- Column headers with a trailing question mark indicate that a value in this column is optional.
- Table cells with a trailing
*
are not translated into the ontology. This is done in the following cases:- Cells that contain lists.
- Cells that contain the value
rdfs:Resource
.
2.1.1 Classes
The classes of the vocabulary are listed in Table 1.
Each row represents a owl:Class
with optional superclass
relationship (using rdfs:subClassOf
), textual definition
(skos:definition
) and optional note (skos:note
).
ottr:Signature |
'A signature specifies the permissible input for instances. It does this through its list of parameters. The IRI of the signature is a unique name that its instances must reference.' | ||
ottr:Template |
ottr:Signature |
'A template is a signature that additionally specifies a pattern. The pattern, which is a set of instances, determines the result of the direct expansion (1-step expansion) of an instance of the template.' | |
ottr:BaseTemplate |
ottr:Signature |
'A base template is a signature with no pattern. The expansion of an instance of a base template is the instance itself.' | |
ottr:Parameter |
'A parameter specifies the variable terms or resources of a pattern and the permissible values for the corresponding instance arguments.' | ||
ottr:ParameterModifier |
'A parameter modifier is a flag or marker that is set on a parameter to alter the permissible corresponding argument values and/or the behaviour of expanding instances.' | 'See also the instances of the class.' | |
ottr:Instance |
'An instance is an instantiation of a signature, template or base template. The instance must refer to a signature and provide arguments that match the corresponding parameters of the signature.' | ||
ottr:ExpansionModifier |
'An expansion modifier is a flag or marker that is used to alter the behaviour of expanding the marked instance' | 'See also the instances of the class. Rename to InstanceModifier?' | |
ottr:Argument |
'An argument specifies an input value for a given instance.' | ||
ottr:ArgumentModifier |
'An argument modifier is a flag or marker that is used to identify the argument that play a special role in modified expansions. See also ExpansionModifier.' | 'See also the instances of the class.' |
2.1.2 Properties
The properties of the vocabulary are listed in Table 2.
Each row represents an
owl:AnnotationProperty
with optional domain (rdfs:domain
) and
optional range (rdfs:range
), textual definition (skos:definition
) and
optional node (skos:note
).
The reasons for using owl:AnnotationProperty
to represent the
properties (and not owl:ObjectProperty
and
owl:DatatypeProperty
) is that
- many of the properties take RDF lists as values, which places the vocabulary outside the OWL 2 DL fragment anyway, and
- some of the properties,
ottr:value
,ottr:default
andottr:parameter
, need to take both literals and IRIs as values, which disqualifies the use ofowl:ObjectProperty
orowl:DatatypeProperty
.
The domains and ranges for ottr:modifier
are not translated into
the ontology. This is because these should be represented as a
"locally scoped" domain and range, but complex axioms on annotation
properties are not supported in OWL.
ottr:parameters |
ottr:Signature |
List of ottr:Parameter * |
'Associates a signature with one required list of parameters.' | |
ottr:annotation |
ottr:Signature |
ottr:Instance |
'Associates a signature with an optional set of annotation instances.' | |
ottr:variable |
ottr:Parameter |
(rdfs:Resource ) * |
'Sets the required variable of a parameter.' | |
ottr:type |
ottr:Parameter |
(rdfs:Resource ) * |
'Sets an optional type of a parameter. A missing type implicitly sets the type to the most general type.' | |
ottr:default |
ottr:Parameter |
(rdfs:Resource ) * |
'Sets an optional default value of a parameter. The default value is used in case an argument value is unspecified or ottr:none.' | |
ottr:pattern |
ottr:Template |
ottr:Instance |
'Associates a template with an optional set of pattern instances.' | |
ottr:modifier |
ottr:Parameter * |
ottr:ParameterModifier * |
||
ottr:of |
ottr:Instance |
ottr:Signature |
'Associates an instance with its required signature.' | |
ottr:arguments |
ottr:Instance |
List of ottr:Argument * |
'Associates an instance with a list of arguments.' | |
ottr:values |
ottr:Instance |
List of (rdfs:Resource ) * |
'Associates an instance with a list of argument values' | |
ottr:value |
ottr:Argument |
(rdfs:Resource ) * |
'Associates an argument with its argument value.' | |
ottr:modifier |
ottr:Argument * |
ottr:ArgumentModifier * |
||
ottr:modifier |
ottr:Instance * |
ottr:ExpansionModifier * |
2.1.3 Individuals
The individuals of the vocabulary are listed in Table 3.
Each row represents
a owl:Thing
with type relationship to a class (rdf:type
),
textual definition (skos:definition
) and optional note
(skos:note
).
ottr:optional |
ottr:ParameterModifier |
'optional is a parameter modifier which makes the value none a permissible instance argument value for this parameter.' | |
ottr:nonBlank |
ottr:ParameterModifier |
'nonBlank is a parameter modifier which makes blank nodes illegal instance argument values for this parameter.' | |
ottr:cross |
ottr:ExpansionModifier |
'cross is an expansion modifier which sets the list expansion operation to cross product.' | |
ottr:zipMax |
ottr:ExpansionModifier |
'zipMax is an expansion modifier which sets the list expansion operation to zip, extending smaller list to the lenght of the longest list by appending empty values.' | |
ottr:zipMin |
ottr:ExpansionModifier |
'zipMin is an expansion modifier which sets the list expansion operation to zip with the shortest list as length.' | |
ottr:listExpand |
ottr:ArgumentModifier |
'listExpand is an argument modifier that selects arguments for list expansion.' | |
ottr:none |
(rdfs:Resource ) * |
'none is an individual which is used to designate a missing argument value.' |
2.2 Grammar
2.2.1 Fundamentals
ottr:RDFListShape a sh:NodeShape ; sh:targetSubjectsOf rdf:first , rdf:rest ; sh:node dash:ListShape ; sh:name "RDF lists" ; sh:message "RDF lists must be regular linked lists." .
2.2.2 Instances
It is optional to explicitly type the resources used to specify an
instance, e.g., it is not required to explicitly add the statement that an
instance has the type ottr:Instance
or ottr:Argument
.
An ottr:Instance
must be associated with the (single)
ottr:Signature
, ottr:Template
and ottr:BaseTemplate
it is an
instance of using the ottr:of
property. (Note that
ottr:Template
or ottr:BaseTemplate
are disjoint subclasses of
ottr:Signature
.)
An ottr:Instance
may have one ottr:modifier
which is an
ottr:ExpansionModifier
. An ottr:ExpansionModifier
is either
ottr:cross
, ottr:zipMax
or ottr:zipMin
.
An ottr:Instance
must specify a list of arguments.
This can be done in two
ways: the canonical or the compact way. The canonical way is to state that an ottr:Instance
has
ottr:arguments
which is an RDF list of
ottr:Argument
-s. The compact way is to directly state that an
ottr:Instance
has (attribute-) ottr:values
which is an RDF list
of RDF resources. However, as explained in the next paragraph, the
reduced expressivity of the
compact way does not cover all cases.
The canonical way covers all cases.
This is the shape expression for ottr:Instance
-s.
ottr:InstanceShape a sh:NodeShape ; sh:targetSubjectsOf ottr:of , ottr:arguments , ottr:values ; sh:targetObjectsOf ottr:pattern, ottr:annotation ; sh:property [ sh:path ottr:of ; sh:minCount 1 ; sh:maxCount 1 ; sh:nodeKind sh:IRI ; sh:name "Instance's reference to signature" ; sh:message "An instance must have exactly one reference to a signature, which must be an IRI." ] , [ sh:path ottr:modifier ; sh:maxCount 1 ; sh:node _:instanceModifierInList ; sh:name "Instance modifier" ; sh:message """An instance can only have one modifier, which must be either ottr:cross, ottr:zipMin or ottr:zipMax.""" ]; sh:xone ( [ sh:property [ sh:path ottr:arguments ; sh:minCount 1 ; sh:maxCount 1 ; sh:node dash:ListShape ; sh:property [ sh:path ( [ sh:zeroOrMorePath rdf:rest ] rdf:first ) ; sh:node ottr:ArgumentShape ; ] ; sh:name "Instance argument list" ; sh:message """An instance must have exactly (possibly empty) one argument list or argument value list. The argument list must contain arguments---and not argument values.""" ] ] [ sh:property [ sh:path ottr:values ; sh:minCount 1; sh:maxCount 1 ; sh:node dash:ListShape ] ; sh:name "Instance argument value list" ; sh:message """An instance must have exactly one (possibly empty) argument list or argument value list.""" ] ) ; ## instance has listExpander if and only if exists argument with modifier. ## modelled as (( not A or B ) and ( not B or A)) sh:and ( [ sh:or ( [ sh:not _:hasPropertyInstanceModifier ] _:hasPropertyArgumentWithListExpander ) ] [ sh:or ( [ sh:not _:hasPropertyArgumentWithListExpander ] _:hasPropertyInstanceModifier ) ] ) . ### Some shapes represented by named blank nodes to avoid repetition: # legal instance modifiers: _:instanceModifierInList sh:in ( ottr:cross ottr:zipMin ottr:zipMax ) . # instance has a modifier: _:hasPropertyInstanceModifier sh:property [ sh:path ottr:modifier ; sh:minCount 1 ; sh:node _:instanceModifierInList ] . # instance has an argument with a modifier: _:hasPropertyArgumentWithListExpander sh:property [ sh:path ( ottr:arguments [ sh:zeroOrMorePath rdf:rest ] rdf:first ottr:modifier ) ; sh:minCount 1 ; sh:hasValue ottr:listExpand ] .
An ottr:Argument
must have one ottr:value
which is any RDF
resource. The resource ottr:none
can be used to explicitly specify
that an ottr:Argument
has no value.
An ottr:Argument
may have a ottr:modifier
which is an
ottr:ArgumentModifier
. The only ottr:ArgumentModifier
is
ottr:listExpand
. An ottr:Argument
which has a
ottr:listExpand
modifier, must have a ottr:value
which is compatible with a
list type.
Canonical vs. Compact arguments
Since the compact way of specifying an instance's arguments does not
include any ottr:Argument
-s, it cannot be used if it is necessary
to associate information on any of the ottr:Argument
-s, for
instance marking an ottr:Argument
with an
ottr:ArgumentModifier
.
The use of ottr:none
is required when stating "no value" in the
compact way. This is due to how lists are represented in RDF Turtle.
This is the shape expression for an ottr:Argument
.
ottr:ArgumentShape a sh:NodeShape ; sh:targetSubjectsOf ottr:value ; ## sh:nodeKind sh:BlankNode ; ## only in strict mode? sh:property [ sh:path ottr:modifier ; sh:maxCount 1; sh:node [ sh:in ( ottr:listExpand ) ] ; sh:name "Argument modifier" ; sh:message "An argument can only have the modifier ottr:listExpand." ] , [ sh:path ottr:value ; sh:minCount 1; sh:maxCount 1; sh:name "Argument value" ; sh:message "An argument can only have one value." ] ; ## if listExpander, then must be blanknode/list ## modelled as not A or B. sh:or ( [ sh:not [ sh:path ottr:modifier ; sh:hasValue ottr:listExpand ] ] [ sh:path ottr:value ; sh:nodeKind sh:BlankNode ; sh:message """ An argument with a ottr:listExpand modifier must have a blanknode (possibly representing a list) as value.""" ; ] ) .
The two instances below are equivalent. Each specifies an instance of
the signature ex:template
with four arguments that have
respectively the values _:A
, (no value), _:C
and _:D
.
# 1. instance, canonical arguments [] ottr:of ex:template ; ottr:arguments ( [ ottr:value _:A ] [ ottr:value ottr:none ] [ ottr:value _:C ] [ ottr:value _:D ] ) . # 2. instance, compact argument values [] ottr:of ex:template ; ottr:values ( _:A ottr:none _:C _:D ) .
The first instance uses the canonical way of specifying arguments,
while the second uses the compact form.
ottr:none
is used to specify the missing value for the second argument.
Note that none of the ottr:Instance
-s or ottr:Argument
-s are typed as
that.
This example shows the use of instance modifiers and argument
modifiers. The instance is marked with the instance modifier
ottr:cross
and the second argument is marked with the argument
modifier ottr:listExpand
.
[] ottr:of ex:template ; ottr:modifier ottr:cross ; ottr:arguments ( [ ottr:value ( 1 2 3 4 ) ; ottr:modifier ottr:listExpand ] [ ottr:value ( ex:a ex:b ex:c ) ] [ ottr:value "string" ] [ ottr:value ottr:none ] [ ottr:value rdfs:subClassOf ] ) .
This instance cannot be specified the compact way, using argument value
syntax and ottr:values
, since it is necessary to mark one of the
arguments with an argument modifier.
2.2.3 Signatures
The ottr:Signature
class has two subclasses, ottr:Template
and
ottr:BaseTemplate
, which are disjoint. We require that instances of
these classes are explicitly typed with at least the most specific
class.
A ottr:Signature
must be identifiable by a concrete IRI
(and
not a blank node).
A ottr:Signature
must have ottr:parameters
which is a
(possibly empty) RDF list of ottr:Parameter
-s.
A ottr:Signature
may have any number of ottr:annotation
-s
which are ottr:Instance
-s.
This is the shape definition of a ottr:Signature
.
ottr:SignatureShape a sh:NodeShape ; sh:targetClass ottr:Signature ; sh:targetSubjectsOf ottr:parameters, ottr:annotation ; sh:class ottr:Signature ; sh:nodeKind sh:IRI ; sh:message "A signature must have the type ottr:Signature or one of its subclasses, and be identified by an IRI." ; sh:property [ sh:path ottr:parameters ; sh:minCount 1 ; sh:maxCount 1 ; sh:node dash:ListShape ; sh:property [ sh:path ( [ sh:zeroOrMorePath rdf:rest ] rdf:first ) ; sh:node ottr:ParameterShape ; ] ; sh:name "Signature parameter list" ; sh:message "A signature must declare exactly one parameter list, which must contain parameters." ] .
This is a signature with no parameters, as seen by the empty RDF list
of ottr:parameters
. The signature is annotated by two instances of the templates ex:templateA
and ex:templateB
.
ex:signature a ottr:Signature ; ottr:parameters ( ); ottr:annotation [ ottr:of ex:templateA ; ottr:values ( ex:signature "this an annotation" ) ] , [ ottr:of ex:templateB ; ottr:values ( ex:signature "this is also an annotation" ) ] .
A ottr:Parameter
must have a ottr:variable
which is a RDF resource.
The RDF resource representing the
variable can be any resource type, i.e., IRI, blank node or literal,
but we recommend using a blank node as their semantics lie close to
the intention of a variable.
A ottr:Parameter
can have a ottr:type
. How types are specified is
described below.
A ottr:Parameter
can have a ottr:default
value which is an RDF resource.
A ottr:Parameter
can have several ottr:modifier
-s which are
ottr:ParameterModifier
-s. A ottr:ParameterModifier
is either
ottr:optional
or ottr:nonBlank
.
This is the shape definition of a ottr:Parameter
.
ottr:ParameterShape a sh:NodeShape ; sh:targetSubjectsOf ottr:variable, ottr:type, ottr:default ; ## sh:nodeKind sh:BlankNode ; ## only in strict mode? sh:property [ sh:path ottr:variable ; ##REMOVED sh:nodeKind sh:BlankNode ; ## only in strict mode? sh:minCount 1 ; sh:maxCount 1 ; sh:name "Parameter variable" ; sh:message "A parameter must have exactly one variable."; ] , [ sh:path ottr:type ; sh:node ottr:TypeListShape ; sh:maxCount 1 ; sh:name "Parameter type" ; sh:message "A parameter can only have one type, which must be a permissible type value." ] , [ sh:path ottr:default ; sh:maxCount 1 ; sh:name "Parameter default value" ; sh:message "A parameter can only have one default value." ] , [ sh:path ottr:modifier ; sh:node [ sh:in ( ottr:optional ottr:nonBlank ) ] ; sh:name "Parameter modifier" ; sh:message "A parameter can only have the modifiers ottr:optional or ottr:nonBlank." ] .
This is the shape expression for ottr:modifier
which restricts its
use to only instances, arguments and parameters.
ottr:ModifierShape a sh:NodeShape ; sh:targetObjectsOf ottr:modifier ; sh:xone ( [ sh:path [ sh:inversePath ottr:modifier ] ; sh:node ottr:InstanceShape ] [ sh:path [ sh:inversePath ottr:modifier ] ; sh:node ottr:ArgumentShape ] [ sh:path [ sh:inversePath ottr:modifier ] ; sh:node ottr:ParameterShape ] ) .
This is a signature that declares three parameters with respectively the variables
_:class
, _:property
and _:string
.
The second parameter is marked by the modifier ottr:nonBlank
,
and the third parameter is marked as optional.
ex:signature a ottr:Signature ; ottr:parameters ( [ ottr:variable _:class ] [ ottr:variable _:property; ottr:modifier ottr:nonBlank ] [ ottr:variable _:string; ottr:modifier ottr:optional ] ) .
2.2.4 Types
Parameter types are specified using an RDF list, where the last
element in the list must be a basic type, the least upper bound type
ottr:LUB
can only occur as the next to last element (which may also
be the first), and the list types
identifiers rdf:List
or ottr:NEList
can not be the last element.
This RDF list (written in RDF Turtle syntax): ( rdf:List ottr:NEList ottt:LUB
xsd:string )
represents the type List<NEList<LUB<xsd:string>>>. If
the type of a ottr:Parameter
is a basic type, it can be specified
using (just) the type IRI (and not a singleton RDF list containing
the type).
This is the shape definition of parameter types.
ottr:TypeListShape a sh:NodeShape ; sh:targetObjectsOf ottr:type ; sh:xone ( [ sh:node dash:ListShape ; sh:property [ sh:path ( [ sh:zeroOrMorePath rdf:rest ] rdf:first ) ; sh:node ottr:TypeShape ; sh:minCount 1; ] ; sh:name "Complex parameter type" ; sh:message """Unrecognised complex type. A type is specified either as a list of types, where the last item in the list must be a basic type, the second last can be a 'least upper bound' type, and the types preceeding it can be list types.""" ] [ sh:node ottr:BasicTypeShape ; sh:name "Basic parameter type" ; sh:message "Unrecognised basic type. A type is specified either as an RDF list of types or a single basic type." ] ) .
These are examples of complex types:
# A list of xsd:int-s -- List<xsd:int> (rdf:List xsd:int) # A non-empty list of xsd:int-s -- NEList<xsd:int>: (ottr:NEList xsd:int) # A list of list of owl:Class-es -- List<List<owl:Class>>: (rdf:List rdf:List owl:Class) # The least upper bound of owl:ObjectProperty -- LUB<owl:ObjectProperty>: (ottr:LUB owl:ObjectProperty) # A list of least upper bounds of owl:ObjectProperty -- List<LUB<owl:ObjectProperty>>: (rdf:List ottr:LUB owl:ObjectProperty)
2.2.5 Template
The class ottr:Template
is a subclass of ottr:Signature
. A ottr:Template
must therefore also follow
the shape specification for ottr:Signature
-s.
A ottr:Template
may have a ottr:patten
which is composed by any
number of ottr:Instance
-s.
ottr:TemplateShape a sh:NodeShape ; sh:targetClass ottr:Template ; sh:targetSubjectsOf ottr:pattern ; sh:class ottr:Template .
This is a template with three parameters and a pattern consisting of two template instances.
ex:template a ottr:Template ; ottr:parameters ( [ ottr:variable _:thing ] [ ottr:variable _:label; ottr:type xsd:string; ottr:modifier ottr:optional ] [ ottr:variable _:comment; ottr:type xsd:string; ottr:modifier ottr:optional ] ) ; ottr:pattern [ ottr:of ottr:Triple; ottr:values ( _:thing rdfs:label _:label ) ] , [ ottr:of ottr:Triple; ottr:values ( _:thing rdfs:comment _:comment ) ] .
2.2.6 Base template
The class ottr:BaseTemplate
is a subclass of ottr:Signature
. A ottr:BaseTemplate
must therefore also follow
the shape specification for ottr:Signature
-s.
A ottr:BaseTemplate
cannot have a ottr:pattern
.
ottr:BaseTemplateShape a sh:NodeShape ; sh:targetClass ottr:BaseTemplate ; sh:class ottr:BaseTemplate ; sh:property [ sh:path ottr:pattern ; sh:maxCount 0 ; sh:message "A base template cannot have a pattern." ] .
This is a base template which represents a regular RDF triple pattern.
ottr:Triple a ottr:BaseTemplate ; ottr:parameters ( [ ottr:type ottr:IRI; ottr:variable _:subject ] [ ottr:type ottr:IRI; ottr:variable _:predicate; ottr:modifier ottr:nonBlank ] [ ottr:type rdfs:Resource; ottr:variable _:object ] ) .
2.3 Base Templates
The base template ottr:Triple
is serialised in wOTTR as follows:
ottr:Triple a ottr:BaseTemplate ; ottr:parameters ( [ ottr:type ottr:IRI; ottr:variable _:subject ] [ ottr:type ottr:IRI; ottr:variable _:predicate; ottr:modifier ottr:nonBlank ] [ ottr:type rdfs:Resource; ottr:variable _:object ] ) .
3 Appendix
3.1 NamedPizza example
We now walk through a complete template and explain each piece before it is listed. We point to some of the changes from the previous version of the vocabulary; these statements are put in parenthesis.
A template is identified by its IRI, which must be a concrete IRI, and must be typed as a ottr:Template
.
1: pizza:NamedPizza a ottr:Template ;
Parameters are given as an RDF list. (This removes the need for
using the old ottr:index
for each of the parameters to ensure an
order of the parameters.)
2: ottr:parameters (
This is the first parameter, represented as a blank node. The type
of the parameter, and thus the type of the variable, is given with
the ottr:type
property. The variable is given with the
ottr:variable
property. We recommend to use a blank node for
representing the variable to avoid clashes with other variables.
3: [ ottr:type owl:Class; 4: ottr:variable _:pizza ]
Optional parameters are marked as such by giving the value
ottr:optional
for the ottr:modifier
predicate.
5: [ ottr:type owl:NamedIndividual; 6: ottr:variable _:country; 7: ottr:modifier ottr:optional ]
List type parameters are given using RDF lists. The type given here
( rdf:List owl:Class )
is a list of classes: List<Class>. (The
types for the first and second parameter could have been given as a
singular list, i.e,. ( owl:Class )
and ( owl:NamedIndividual )
,
however, we allow non-list types like these to be given just using
the IRI of the type alone. (Variables that represent lists are no
longer given as lists, but as a single resource, again, preferably
as a blank node.)
8: [ ottr:type ( rdf:List owl:Class ); 9: ottr:variable _:toppings; ] ) ;
The ottr:pattern
property explicitly points to the set of the
instances of the pattern. (Ordinary triples must be given as
instances of the Triple template.)
10: ottr:pattern
(A template instance is specified as before, but we have introduced
new property IRIs; we now use ottr:of
instead of
ottr:templateRef
, and ottr:values
instead of ottr:withValues
.)
11: [ ottr:of ax:SubClassOf ; 12: ottr:values ( _:pizza p:NamedPizza ) ] ,
If an instance requires additional information about the arguments,
the arguments need to be explicitly represented (not just the
values). In the template instance above, a list expander is specified
(with ottr:modifier ottr:cross
) and the arguments that are selected
for list expansion must be indicated with the use of ottr:modifier
ottr:listExpand
. This modifier can only be applied to arguments which
are of "list type".
13: [ ottr:of ax:SubObjectSomeValuesFrom ; 14: ottr:arguments ( 15: [ ottr:value _:pizza ] 16: [ ottr:value p:hasTopping ] 17: [ ottr:value _:toppings; 18: ottr:modifier ottr:listExpand ] ) ; 19: ottr:modifier ottr:cross ] ,
20: [ ottr:of ax:SubObjectHasValue ; 21: ottr:values 22: ( _:pizza p:hasCountryOfOrigin _:country ) ] , 23: 24: [ ottr:of ax:SubObjectAllValuesFrom ; 25: ottr:values 26: ( :pizza p:hasTopping _:alltoppings ) ] , 27: 28: [ ottr:of rstr:ObjectUnionOf ; 29: ottr:values 30: ( _:alltoppings _:toppings ) ] ; # end pattern
Annotations are a new feature of templates. Technically an annotation is a ground template instance, ground meaning variables are not allowed as arguments. Annotation instances are typically used to associate information with the template itself, such as a description. A template may have any number of annotation instances.
31: ## new feature 32: ottr:annotation
33: [ ottr:of <http://example.net/ns#TemplateAnnotation> ; 34: ottr:values ( pizza:NamedPizza "A pizza template" ) ] .
We show the differences between the old and new wOTTR vocabulary with an example where the matching parts are aligned side by side.
Previous version
pizza:NamedPizza a ottr:Template ; ottr:hasParameter [ ottr:index 1 ; ottr:classVariable :pizza ] , [ ottr:index 2 ; ottr:individualVariable :country; ottr:optional true ] , [ ottr:index 3 ; ottr:listVariable (:toppings) ] . # body: [] ottr:templateRef ax:SubClassOf ; ottr:withValues ( :pizza p:NamedPizza ) . [] ottr:templateRef ax:SubObjectSomeValuesFrom ; ottr:hasArgument [ ottr:index 1; ottr:value :pizza ] , [ ottr:index 2; ottr:value p:hasTopping ] , [ ottr:index 3; ottr:eachValue (:toppings) ] . [] ottr:templateRef ax:SubObjectHasValue ; ottr:withValues ( :pizza p:hasCountryOfOrigin :country ) . [] ottr:templateRef ax:SubObjectAllValuesFrom ; ottr:withValues ( :pizza p:hasTopping _:alltoppings ) . [] ottr:templateRef rstr:ObjectUnionOf ; ottr:withValues ( _:alltoppings (:toppings) ) .
This version
pizza:NamedPizza a ottr:Template ; ottr:parameters ( [ ottr:type owl:Class; ottr:variable _:pizza ] [ ottr:type owl:NamedIndividual; ottr:variable _:country; ottr:modifier ottr:optional ] [ ottr:type ( rdf:List owl:Class ); ottr:variable _:toppings; ] ) ; ottr:pattern [ ottr:of ax:SubClassOf ; ottr:values ( _:pizza p:NamedPizza ) ] , [ ottr:of ax:SubObjectSomeValuesFrom ; ottr:arguments ( [ ottr:value _:pizza ] [ ottr:value p:hasTopping ] [ ottr:value _:toppings; ottr:modifier ottr:listExpand ] ) ; ottr:modifier ottr:cross ] , [ ottr:of ax:SubObjectHasValue ; ottr:values ( _:pizza p:hasCountryOfOrigin _:country ) ] , [ ottr:of ax:SubObjectAllValuesFrom ; ottr:values ( :pizza p:hasTopping _:alltoppings ) ] , [ ottr:of rstr:ObjectUnionOf ; ottr:values ( _:alltoppings _:toppings ) ] ; # end pattern ## new feature ottr:annotation [ ottr:of <http://example.net/ns#TemplateAnnotation> ; ottr:values ( pizza:NamedPizza "A pizza template" ) ] .
3.2 wOTTR Templates
We eat dog food. These are the templates used to construct the wOTTR vocabulary ontology from the tables in this document.
3.2.1 Term template
o-wottr:Term a ottr:Template ; ottr:parameters ( [ ottr:variable _:IRI; ottr:type ottr:IRI ] [ ottr:variable _:definition; ottr:type xsd:string; ottr:modifier ottr:optional ] [ ottr:variable _:note; ottr:type xsd:string; ottr:modifier ottr:optional ] ) ; ottr:pattern [ ottr:of ottr:Triple; ottr:values ( _:IRI skos:definition _:definition ) ] , [ ottr:of ottr:Triple; ottr:values ( _:IRI skos:note _:note ) ] .
3.2.2 Class template
o-wottr:Class a ottr:Template ; ottr:parameters ( [ ottr:variable _:IRI; ottr:type owl:Class ] [ ottr:variable _:super; ottr:type owl:Class; ottr:modifier ottr:optional ] [ ottr:variable _:definition; ottr:type xsd:string; ottr:modifier ottr:optional ] [ ottr:variable _:note; ottr:type xsd:string; ; ottr:modifier ottr:optional ] ) ; ottr:pattern [ ottr:of ottr:Triple; ottr:values (_:IRI rdfs:subClassOf _:super ) ] , [ ottr:of o-wottr:Term ; ottr:values ( _:IRI _:definition _:note ) ] .
3.2.3 Property template
o-wottr:Property a ottr:Template ; ottr:parameters ( [ ottr:variable _:IRI; ottr:type owl:AnnotationProperty ] [ ottr:variable _:domain; ottr:type owl:Class; ottr:modifier ottr:optional ] [ ottr:variable _:range; ottr:type owl:Class; ottr:modifier ottr:optional ] [ ottr:variable _:definition; ottr:type xsd:string; ottr:modifier ottr:optional ] [ ottr:variable _:note; ottr:type xsd:string; ottr:modifier ottr:optional ] ) ; ottr:pattern [ ottr:of ottr:Triple; ottr:values ( _:IRI rdf:type owl:AnnotationProperty ) ] , [ ottr:of ottr:Triple; ottr:values ( _:IRI rdfs:domain _:domain ) ] , [ ottr:of ottr:Triple; ottr:values ( _:IRI rdfs:range _:range ) ] , [ ottr:of o-wottr:Term; ottr:values ( _:IRI _:definition _:note ) ] .
3.2.4 Instance template
o-wottr:Instance a ottr:Template ; ottr:parameters ( [ ottr:variable _:IRI; ottr:type owl:NamedIndividual ] [ ottr:variable _:type; ottr:type owl:Class; ottr:modifier ottr:optional ] [ ottr:variable _:definition; ottr:type xsd:string; ottr:modifier ottr:optional ] [ ottr:variable _:note; ottr:type xsd:string; ottr:modifier ottr:optional ] ) ; ottr:pattern [ ottr:of ottr:Triple ; ottr:values ( _:IRI rdf:type _:type ) ] , [ ottr:of o-wottr:Term ; ottr:values ( _:IRI _:definition _:note ) ] .
3.3 Unit tests
A unit test is either a syntactically correct or an incorrect RDF snippet. Syntactically correct snippets are put in green boxes, while syntactically incorrect snippets are put in red boxes.
# CORRECT
# INCORRECT
Basic
# INCORRECT: rdf:asdf in an incorrect IRI. [] rdf:asdf [] .
# INCORRECT: rdfs:asdf in an incorrect IRI. [] rdfs:asdf [] .
# INCORRECT: owl:asdf in an incorrect IRI. [] owl:asdf [] .
# INCORRECT: sh:asdf in an incorrect IRI. [] sh:asdf [] .
# INCORRECT: ottr:asdf in an incorrect IRI. [] ottr:asdf [] .
# INCORRECT: An RDF list cannot have two firsts. [ rdf:first 1 ; rdf:first 2 ; rdf:rest rdf:nil ]
# INCORRECT: An RDF list cannot have two rests. [ rdf:first 1 ; rdf:rest 2, rdf:nil ]
# INCORRECT: An RDF list cannot be a loop. _:A rdf:first 1 ; rdf:rest [ rdf:first 2 ; rdf:rest _:A ] .
Instance
# CORRECT: This is an instance with no arguments. [] ottr:of ex:template ; ottr:arguments () .
# CORRECT: This is an instance with no argument values. [] ottr:of ex:template ; ottr:values () .
# INCORRECT: an instance's reference to signature, via ottr:of, must be to a concrete IRI. [] ottr:of _:signature ; ottr:arguments () .
# INCORRECT: an instance cannot have both ottr:arguments and ottr:values. [] ottr:of ex:signature ; ottr:arguments () ; ottr:values () .
# INCORRECT: an instance cannot be instance of two different signatures. [] ottr:of ex:signatureA , ex:templateB ; ottr:values () .
# INCORRECT: an instance can only have one modifier. [] ottr:of ex:signature ; ottr:values () ; ottr:modifier ottr:cross, ottr:zipMin .
# INCORRECT: ottr:optional is not a permissible instance modifier. [] ottr:of ex:signature ; ottr:values () ; ottr:modifier ottr:optional .
# CORRECT: ottr:cross is a permissible instance modifier. [] ottr:of ex:signature ; ottr:arguments ( [ ottr:value ( 1 2 ); ottr:modifier ottr:listExpand ] ) ; ottr:modifier ottr:cross .
# INCORRECT: ottr:valus, ottr:modfer and ottr:cros are typos and not correct vocabulary elements. [] ottr:of ex:signature ; ottr:valus () ; ottr:modfer ottr:cros .
# INCORRECT: arguments with listExpander modifier must have list as value. [] ottr:of ex:template ; ottr:modifier ottr:cross ; ottr:arguments ( [ ottr:value ex:not-a-list ; ottr:modifier ottr:listExpand ] ) .
# INCORRECT: An argument has a listExpand modifier iff the instance must has a modifier. [] ottr:of ex:signature ; ottr:arguments ( [ ottr:value ( 1 2 ); ottr:modifier ottr:listExpand ] ) .
# INCORRECT: A argument has a listExpand modifier iff then the instance has a modifier. [] ottr:of ex:signature ; ottr:arguments ( [ ottr:value ( 1 2 ) ] ) ; ottr:modifier ottr:cross .
Argument
# INCORRECT: an argument list must contain arguments (and not values). [] ottr:of ex:template ; ottr:arguments (_:val1 _:val2 ) .
# CORRECT: This is a template that uses the canonical way of representing arguments. [] ottr:of ex:template ; ottr:arguments ( [ ottr:value _:val1 ] [ ottr:value _:val2 ] ) .
# CORRECT: This is a template that uses the compact way of representing argument values. [] ottr:of ex:template ; ottr:values (_:val1 _:val2 ) .
Signature
# INCORRECT: a signature must have parameters. ex:signature a ottr:Signature .
# CORRECT: This is a signaure with an empty parameter list. ex:signature a ottr:Signature ; ottr:parameters () .
# INCORRECT: a signature must be identified by an IRI and not a blank node. [] a ottr:Signature ; ottr:parameters () .
# INCORRECT: A signature cannot have multiple parameter lists ex:signature a ottr:Signature ; ottr:parameters ( [ ottr:variable _:a ] ) ; ottr:parameters ( [ ottr:variable _:b ] ) .
# CORRECT: This is a signature with two parameters. ex:signature a ottr:Signature ; ottr:parameters ( [ ottr:variable _:a ] [ ottr:variable _:b ]) .
# INCORRECT: A signature must be typed as one. ex:signature ottr:parameters ( ) .
# CORRECT: typing with subclasses of ottr:Signature is OK. ex:template a ottr:Template ; ottr:parameters ( ) . ex:baseTemplate a ottr:BaseTemplate ; ottr:parameters ( ) .
# INCORRECT: The parameter list must be a list (and not a parameter). ex:signature a ottr:Signature ; ottr:parameters [ ottr:variable _:a ] .
# INCORRECT: The parameter list must be a list of parameters (and not resources). ex:signature a ottr:Signature ; ottr:parameters ( ex:param ) ;
# CORRECT: This is a signature with an annotation instance. ex:signature a ottr:Signature ; ottr:parameters ( ) ; ottr:annotation [ ottr:of ex:template ; ottr:values () ] .
# CORRECT: This is a signature with multiple annotation instances. ex:signature a ottr:Signature ; ottr:parameters ( ) ; ottr:annotation [ ottr:of ex:tb ; ottr:values (1 2 3) ] , [ ottr:of ex:tc ; ottr:values (ex:a 1 2) ] , [ ottr:of ex:td ; ottr:values (true false) ] , [ ottr:of ex:te ; ottr:values (((0))) ] , [ ottr:of ex:tf ; ottr:arguments ([ ottr:value 2 ] [ ottr:value "asdf"] ) ] , [ ottr:of ex:tg ; ottr:modifier ottr:cross; ottr:arguments ([ ottr:value (1 2 3); ottr:modifier ottr:listExpand ] [ ottr:value "asdf"] ) ] .
# INCORRECT: a signature annotations are not given as a list. ex:signature a ottr:Signature ; ottr:parameters ( ) ; ottr:annotation ( [ ottr:of ex:tb ; ottr:values () ] [ ottr:of ex:tc ; ottr:values () ] )
Parameter
# INCORRECT: a parameter must have a variable . ex:signature a ottr:Signature ; ottr:parameters ([]) .
# CORRECT: This is a signature with a parameter. ex:signature a ottr:Signature ; ottr:parameters ([ ottr:variable _:a ]) .
# INCORRECT: a parameter cannot have two types. ex:signature a ottr:Signature ; ottr:parameters ([ ottr:variable _:a ; ottr:type rdfs:Resource, owl:Class ]) .
# INCORRECT: a parameter cannot have two default values. ex:signature a ottr:Signature ; ottr:parameters ([ ottr:variable _:a ; ottr:default "one", "two" ]) .
# INCORRECT: parameter modifiers can only be ottr:optional or ottr:nonBlank ex:signature a ottr:Signature ; ottr:parameters ([ ottr:variable _:a ; ottr:modifier ottr:cross ]) .
# CORRECT ex:signature a ottr:Signature ; ottr:parameters ( [ ottr:variable _:a ] [ ottr:variable _:b ; ottr:type rdfs:Resource ] [ ottr:variable _:c ; ottr:type owl:Class ; ottr:default "value" ] [ ottr:variable _:d ; ottr:type xsd:int ; ottr:default 2 ; ottr:modifier ottr:nonBlank ] [ ottr:variable _:e ; ottr:type owl:ObjectProperty ; ottr:modifier ottr:optional, ottr:nonBlank ] ) .
Type
# CORRECT: This is a signature with one parameter typed as owl:Class. ex:signature a ottr:Signature ; ottr:parameters ( [ ottr:variable _:a; ottr:type owl:Class ] ) .
# CORRECT: This is a signature with one parameter typed as xsd:string. ex:signature a ottr:Signature ; ottr:parameters ( [ ottr:variable _:a; ottr:type xsd:string ] ) .
# CORRECT: This is a signature with one parameter typed as ottt:LUB<xsd:string>. ex:signature a ottr:Signature ; ottr:parameters ( [ ottr:variable _:a; ottr:type ( ottr:LUB xsd:string ) ] ) .
# INCORRECT: types must be taken from rOTTR, and cannot be blank. ex:signature a ottr:Signature ; ottr:parameters ( [ ottr:variable _:a ; ottr:type _:blank ] ) .
@prefix skos: <http://www.w3.org/2004/02/skos/core#> . # INCORRECT: types must be taken from rOTTR ex:signature a ottr:Signature ; ottr:parameters ( [ ottr:variable _:a; ottr:type skos:Concept ] ) .
# INCORRECT: if a "single resource" type cannot be a complex type ex:signature a ottr:Signature ; ottr:parameters ( [ ottr:variable _:a; ottr:type rdf:List ] ) .
# INCORRECT ex:signature a ottr:Signature ; ottr:parameters ( [ ottr:variable _:a; ottr:type (rdf:List xsd:int) ] [ ottr:variable _:b; ottr:type (ottr:NEList xsd:int) ] [ ottr:variable _:c; ottr:type (rdf:List rdf:List owl:Class) ] [ ottr:variable _:d; ottr:type (ottr:LUB owl:ObjectProperty) ] [ ottr:variable _:e; ottr:type (rdf:List ottr:LUB owl:ObjectProperty) ] ) .
Template
# INCORRECT: a template (being a subclass of Signature) must have parameters. ex:template a ottr:Template .
# CORRECT: This is a template with no pattern. ex:template a ottr:Template ; ottr:parameters () .
# CORRECT ex:template a ottr:Template ; ottr:parameters () ; ottr:pattern [ ottr:of ex:tb ; ottr:values (1 2 3) ] , [ ottr:of ex:tc ; ottr:values (ex:a 1 2) ] , [ ottr:of ex:td ; ottr:values (true false) ] , [ ottr:of ex:te ; ottr:values ( ( ( 0 ) ) ) ] , [ ottr:of ex:tf ; ottr:arguments ([ ottr:value 2 ] [ ottr:value "asdf"] ) ] , [ ottr:of ex:tg ; ottr:modifier ottr:cross; ottr:arguments ([ ottr:value (1 2 3); ottr:modifier ottr:listExpand ] [ ottr:value "asdf"] ) ] .
# INCORRECT: a pattern is not given as a list. ex:template a ottr:Template ; ottr:parameters () ; ottr:pattern ( [ ottr:of ex:tb ; ottr:values (1 2 3) ] [ ottr:of ex:tc ; ottr:values (ex:a 1 2) ] [ ottr:of ex:td ; ottr:values (true false) ] [ ottr:of ex:te ; ottr:values ( ( ( 0 ) ) ) ] [ ottr:of ex:tf ; ottr:arguments ([ ottr:value 2 ] [ ottr:value "asdf"] ) ] [ ottr:of ex:tg ; ottr:modifier ottr:cross; ottr:arguments ([ ottr:values (1 2 3); ottr:modifier ottr:listExpand ] [ ottr:value "asdf"] ) ] ) .
BaseTemplate
# INCORRECT: a base template (being a subclass of Signature) must have parameters. ex:template a ottr:BaseTemplate .
# INCORRECT: a base template cannot have a pattern. ex:template a ottr:BaseTemplate ; ottr:parameters () ; ottr:pattern [ ottr:of ex:b; ottr:values (1 2 3) ] .
# CORRECT: This is a base template with an annotation instance. ex:template a ottr:BaseTemplate ; ottr:parameters () ; ottr:annotation [ ottr:of ex:b; ottr:values ( 1 2 3 ) ] .
3.4 SHACL validation tests
Validation of wOTTR shapes
Validation of wOTTR shapes against the SHACL definition of SHACL at http://www.w3.org/ns/shacl-shacl, and the wOTTR vocabulary tokens. It should be valid.
nil
Validation of examples
Validation of the examples found in the example boxes of this document against the wOTTR SHACL shape definition. They should all be valid.
nil
Validation of correct unit tests
Validation of syntactically correct unit tests against the wOTTR SHACL shape definition. They should all be valid.
nil
Validation of incorrect unit tests
Validation of syntactically incorrect unit tests against the wOTTR SHACL shape definition. They should all raise an error.
nil
4 References
- mOTTR
- https://spec.ottr.xyz/mOTTR/0.1/
- rOTTR
- https://spec.ottr.xyz/rOTTR/0.2/
- RDF 1.1 Turtle
- https://www.w3.org/TR/turtle/