aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2018-05-27 15:48:25 +0200
committerDavid Robillard <d@drobilla.net>2022-01-28 21:57:29 -0500
commitf93a441065a611cc32874dde67e53a8295c87baf (patch)
tree3793e2df1c365bf93fbe700c9428b54668f08c53
parent2c5bee49b4494e172c4fa147af91bad199ed9362 (diff)
downloadserd-f93a441065a611cc32874dde67e53a8295c87baf.tar.gz
serd-f93a441065a611cc32874dde67e53a8295c87baf.tar.bz2
serd-f93a441065a611cc32874dde67e53a8295c87baf.zip
[WIP] Add validation
-rw-r--r--.clang-format3
-rw-r--r--.gitlab-ci.yml1
-rw-r--r--.includes.imp18
-rw-r--r--NEWS1
-rw-r--r--doc/conf.py.in1
-rw-r--r--doc/serd-filter.12
-rw-r--r--doc/serd-pipe.12
-rw-r--r--doc/serd-sort.12
-rw-r--r--doc/serd-validate.1247
-rw-r--r--include/serd/serd.h279
-rw-r--r--meson.build10
-rw-r--r--schemas/dc.ttl101
-rw-r--r--schemas/dcam.ttl42
-rw-r--r--schemas/dcterms.ttl906
-rw-r--r--schemas/doap.ttl868
-rw-r--r--schemas/foaf.ttl810
-rw-r--r--schemas/manifest.ttl50
-rw-r--r--schemas/meson.build12
-rw-r--r--schemas/mf.ttl176
-rw-r--r--schemas/owl.ttl628
-rw-r--r--schemas/rdf.ttl177
-rw-r--r--schemas/rdfs.ttl124
-rw-r--r--schemas/xsd.ttl363
-rwxr-xr-xscripts/fetch_schemas.sh45
-rw-r--r--serd.ttl157
-rw-r--r--src/log.c8
-rw-r--r--src/string.c2
-rw-r--r--src/validate.c2042
-rw-r--r--test/meson.build22
-rw-r--r--test/pretty/meson.build16
-rwxr-xr-xtest/run_filter_suite.py2
-rwxr-xr-xtest/run_validation_test_suite.py278
-rw-r--r--test/test_string.c2
-rw-r--r--test/validate/bad-all-values-from.ttl24
-rw-r--r--test/validate/bad-anyuri.ttl13
-rw-r--r--test/validate/bad-cardinality-high.ttl19
-rw-r--r--test/validate/bad-cardinality-low.ttl17
-rw-r--r--test/validate/bad-cardinality.ttl19
-rw-r--r--test/validate/bad-class-type-undefined.ttl5
-rw-r--r--test/validate/bad-class-type.ttl9
-rw-r--r--test/validate/bad-datatype-cycle.ttl18
-rw-r--r--test/validate/bad-datatype-property.ttl19
-rw-r--r--test/validate/bad-deprecated-class.ttl12
-rw-r--r--test/validate/bad-deprecated-property.ttl13
-rw-r--r--test/validate/bad-disjoint-with.ttl18
-rw-r--r--test/validate/bad-domain.ttl27
-rw-r--r--test/validate/bad-functional-property.ttl13
-rw-r--r--test/validate/bad-inverse-functional-property.ttl15
-rw-r--r--test/validate/bad-literal-pattern.ttl22
-rw-r--r--test/validate/bad-literal-value-high-exclusive.ttl20
-rw-r--r--test/validate/bad-literal-value-high-inclusive.ttl20
-rw-r--r--test/validate/bad-literal-value-low-exclusive.ttl20
-rw-r--r--test/validate/bad-literal-value-low-inclusive.ttl21
-rw-r--r--test/validate/bad-literal-value.ttl7
-rw-r--r--test/validate/bad-object-property.ttl12
-rw-r--r--test/validate/bad-pattern.ttl23
-rw-r--r--test/validate/bad-plain-literal.ttl12
-rw-r--r--test/validate/bad-predicate-type-undefined.ttl5
-rw-r--r--test/validate/bad-predicate-type.ttl9
-rw-r--r--test/validate/bad-range-instance-not-literal.ttl19
-rw-r--r--test/validate/bad-range-instance.ttl24
-rw-r--r--test/validate/bad-range-literal-not-instance.ttl16
-rw-r--r--test/validate/bad-range-literal.ttl27
-rw-r--r--test/validate/bad-some-values-from.ttl19
-rw-r--r--test/validate/bad-string-literal-value-high.ttl21
-rw-r--r--test/validate/bad-string-literal-value-low.ttl21
-rw-r--r--test/validate/bad-subclass-cycle.ttl13
-rw-r--r--test/validate/bad-subproperty-cycle.ttl14
-rw-r--r--test/validate/bad-superclass-restriction.ttl22
-rw-r--r--test/validate/bad-union-of.ttl46
-rw-r--r--test/validate/bad-unknown-datatype.ttl7
-rw-r--r--test/validate/good-anyuri.ttl13
-rw-r--r--test/validate/good-cardinality.ttl18
-rw-r--r--test/validate/good-literal-value-high-inclusive.ttl21
-rw-r--r--test/validate/good-literal-value-low-inclusive.ttl21
-rw-r--r--test/validate/good-owl-thing.ttl16
-rw-r--r--test/validate/good-pattern.ttl22
-rw-r--r--test/validate/good-rdfs-resource.ttl12
-rw-r--r--test/validate/good-some-values-from.ttl18
-rw-r--r--test/validate/good-string-literal-value-low.ttl21
-rw-r--r--test/validate/good-union-of.ttl51
-rw-r--r--test/validate/manifest.ttl362
-rw-r--r--tools/meson.build13
-rw-r--r--tools/serd-validate.c479
84 files changed, 9103 insertions, 22 deletions
diff --git a/.clang-format b/.clang-format
index 08b326c8..ccd9a659 100644
--- a/.clang-format
+++ b/.clang-format
@@ -18,6 +18,9 @@ IndentCaseLabels: false
IndentPPDirectives: AfterHash
KeepEmptyLinesAtTheStartOfBlocks: false
SpacesInContainerLiterals: false
+ForEachMacros:
+ - SERD_FOREACH
+ - SERD_FOREACH_NODE
StatementMacros:
- SERD_API
- SERD_CONST_API
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index d5c5aca8..bc6d7706 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -51,6 +51,7 @@ x64_dbg:
- mandoc -Thtml -Wwarning -O style=mandoc.css,man=%N.html doc/man/serd-filter.1 > build/doc/serd-filter.html
- mandoc -Thtml -Wwarning -O style=mandoc.css,man=%N.html doc/man/serd-pipe.1 > build/doc/serd-pipe.html
- mandoc -Thtml -Wwarning -O style=mandoc.css,man=%N.html doc/man/serd-sort.1 > build/doc/serd-sort.html
+ - mandoc -Thtml -Wwarning -O style=mandoc.css,man=%N.html doc/man/serd-validate.1 > build/doc/serd-validate.html
artifacts:
paths:
- build/doc
diff --git a/.includes.imp b/.includes.imp
index 3829d78c..ec06835e 100644
--- a/.includes.imp
+++ b/.includes.imp
@@ -1,12 +1,14 @@
[
- { "symbol": [ "int16_t", "private", "<stdint.h>", "public" ] },
- { "symbol": [ "int32_t", "private", "<stdint.h>", "public" ] },
- { "symbol": [ "int64_t", "private", "<stdint.h>", "public" ] },
- { "symbol": [ "int8_t", "private", "<stdint.h>", "public" ] },
- { "symbol": [ "uint16_t", "private", "<stdint.h>", "public" ] },
- { "symbol": [ "uint32_t", "private", "<stdint.h>", "public" ] },
- { "symbol": [ "uint64_t", "private", "<stdint.h>", "public" ] },
- { "symbol": [ "uint8_t", "private", "<stdint.h>", "public" ] },
+ { "symbol": [ "int16_t", "private", "<stdint.h>", "public" ] },
+ { "symbol": [ "int32_t", "private", "<stdint.h>", "public" ] },
+ { "symbol": [ "int64_t", "private", "<stdint.h>", "public" ] },
+ { "symbol": [ "int8_t", "private", "<stdint.h>", "public" ] },
+ { "symbol": [ "intptr_t", "private", "<stdint.h>", "public" ] },
+ { "symbol": [ "uint16_t", "private", "<stdint.h>", "public" ] },
+ { "symbol": [ "uint32_t", "private", "<stdint.h>", "public" ] },
+ { "symbol": [ "uint64_t", "private", "<stdint.h>", "public" ] },
+ { "symbol": [ "uint8_t", "private", "<stdint.h>", "public" ] },
+ { "symbol": [ "uintptr_t", "private", "<stdint.h>", "public" ] },
{ "include": [ "<bits/struct_stat.h>", "private", "<sys/stat.h>", "public", ] },
]
diff --git a/NEWS b/NEWS
index 43beefdd..3d37e7bb 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,7 @@ serd (1.0.1) unstable;
* Add option for writing terse output without newlines
* Add support for converting literals to canonical form
* Add support for parsing variables
+ * Add support for validation
* Add support for writing terse collections
* Add support for xsd:float and xsd:double literals
* Bring read/write interface closer to C standard
diff --git a/doc/conf.py.in b/doc/conf.py.in
index 8760bbd9..bc64c9f3 100644
--- a/doc/conf.py.in
+++ b/doc/conf.py.in
@@ -33,6 +33,7 @@ _opaque = [
"SerdReaderImpl",
"SerdSinkImpl",
"SerdStatementImpl",
+ "SerdValidatorImpl",
"SerdWorldImpl",
"SerdWriterImpl",
"int16_t",
diff --git a/doc/serd-filter.1 b/doc/serd-filter.1
index 0f09d1d4..e05559e2 100644
--- a/doc/serd-filter.1
+++ b/doc/serd-filter.1
@@ -146,6 +146,8 @@ To print every statement about http://example.org/subject:
.It
.Xr serd-sort 1
.It
+.Xr serd-validate 1
+.It
.Lk http://drobilla.net/software/serd/
.El
.Sh STANDARDS
diff --git a/doc/serd-pipe.1 b/doc/serd-pipe.1
index 28f1373d..d94d4445 100644
--- a/doc/serd-pipe.1
+++ b/doc/serd-pipe.1
@@ -317,6 +317,8 @@ To merge two files:
.It
.Xr serd-sort 1
.It
+.Xr serd-validate 1
+.It
.Lk http://drobilla.net/software/serd/
.El
.Sh STANDARDS
diff --git a/doc/serd-sort.1 b/doc/serd-sort.1
index 3249fbbc..7fb02978 100644
--- a/doc/serd-sort.1
+++ b/doc/serd-sort.1
@@ -153,6 +153,8 @@ To print statements ordered by predicate, subject, then object:
.It
.Xr serd-filter 1
.It
+.Xr serd-validate 1
+.It
.Lk http://drobilla.net/software/serd/
.El
.Sh STANDARDS
diff --git a/doc/serd-validate.1 b/doc/serd-validate.1
new file mode 100644
index 00000000..3d8c0035
--- /dev/null
+++ b/doc/serd-validate.1
@@ -0,0 +1,247 @@
+.Dd October 21, 2021
+.Dt SERD-VALIDATE 1
+.Os Serd
+.Sh NAME
+.Nm serd-validate
+.Nd validate RDF data
+.Sh SYNOPSIS
+.Nm serd-validate
+.Op Fl hVv
+.Op Fl B Ar base
+.Op Fl I Ar syntax
+.Op Fl W Ar checks
+.Op Fl X Ar checks
+.Op Fl k Ar bytes
+.Op Fl s Ar schema
+.Ar input ...
+.Sh DESCRIPTION
+.Nm
+validates RDF data against one or more RDFS or OWL schemas.
+Unlike other serd tools,
+.Nm
+does not write any data,
+it only prints messages and returns a status that indicates whether the data is valid.
+.Pp
+All inputs to check must be given as an
+.Ar input .
+Schemas can also be given as an
+.Ar input ,
+in which case they will also be checked for issues.
+Alternatively, schemas can be given using the
+.Fl s
+option, which will load the schema to check against,
+but not initiate checks for schema statements themselves.
+This roughly means that error messages will not be printed for the schema files,
+although some may still appear if they are related to issues in the input files.
+When using schemas in this way, it is highly recommended to ensure that the schemas are valid first,
+otherwise the validator may not work correctly.
+.Pp
+The options are as follows:
+.Pp
+.Bl -tag -compact -width 3n
+.It Fl B Ar base
+Base URI or path.
+See
+.Xr serd-pipe 1
+for details.
+.Pp
+.It Fl I Ar syntax
+Input syntax or option:
+.Cm NQuads ,
+.Cm NTriples ,
+.Cm TriG ,
+.Cm Turtle ,
+.Cm lax ,
+.Cm variables ,
+.Cm relative ,
+or
+.Cm labels .
+See
+.Xr serd-pipe 1
+for details.
+.Pp
+.It Fl W Ar checks
+Warn about any issues found by the given
+.Ar checks ,
+which is a regular expression that matches a set of check names to enable,
+or a special name for standard sets of checks:
+.Bl -tag -width 3n
+.It Cm all
+Enables all checks that should pass on typical well-formed data.
+.It Cm everything
+Enables all the checks of
+.Cm all ,
+along with additional strict checks that are useful for making data simpler and more explicit.
+.El
+.Pp
+See
+.Sx CHECKS
+below for a detailed list of all checks.
+.Pp
+.It Fl X Ar checks
+Exclude
+.Ar checks
+from the set of checks enabled by a previous
+.Fl W
+option.
+This is typically used after
+.Fl W Cm all
+or
+.Fl W Cm everything
+to suppress a few specific checks.
+.Pp
+.It Fl V
+Display version information and exit.
+.Pp
+.It Fl h
+Print the command line options.
+.Pp
+.It Fl k Ar bytes
+Parser stack size.
+See
+.Xr serd-pipe 1
+for details.
+.Pp
+.It Fl s Ar schema
+Schema input file.
+The schema will be loaded like an input,
+but into a separate graph that is not checked for issues.
+.Pp
+.It Fl v
+Print verbose messages about loaded resources.
+.El
+.Sh CHECKS
+.Bl -tag -compact -width 3n
+.It Cm allValuesFrom
+Checks that all properties with owl:allValuesFrom restrictions have valid value types.
+.It Cm anyUri
+Checks that the value of any property with range xsd:anyURI is a URI.
+.It Cm cardinalityEqual
+Checks that any instance of a class with a owl:cardinality property restriction has exactly that many values of that property.
+.It Cm cardinalityMax
+Checks that any instance of a class with a owl:maxCardinality property restriction has no more than that many values of that property.
+.It Cm cardinalityMin
+Checks that any instance of a class with a owl:minCardinality property restriction has at least that many values of that property.
+.It Cm classCycle
+Checks that no class is a sub-class of itself, recursively.
+This ensures that the graph is acyclic with respect to rdfs:subClassOf.
+If this check fails, all further checks are aborted.
+.It Cm classLabel
+Checks that every rdfs:Class has an rdfs:label.
+.It Cm datatypeCycle
+Checks that no datatype is a sub-datatype of itself, recursively.
+This ensures that the graph is acyclic with respect to owl:onDatatype.
+If this check fails, all further checks are aborted.
+.It Cm datatypeProperty
+Checks that datatype properties have literal (not instance) values.
+.It Cm datatypeType
+Checks that every datatype is defined as a rdfs:Datatype.
+.It Cm deprecatedClass
+Checks that there are no instances of deprecated classes.
+.It Cm deprecatedProperty
+Checks that there are no uses of deprecated properties.
+.It Cm explicitInstanceType
+Checks that every instance explicitly has every type required of it.
+This is a (often overly) strict check that assumes a closed world and requires every instance to explicitly have the type(s) required of it.
+.It Cm functionalProperty
+Checks that no instance has several values of a functional property.
+.It Cm instanceLiteral
+Checks that there are no instances where a literal is expected.
+.It Cm instanceType
+Checks that every instance with an explicit type matches that type.
+This is a broad check that triggers other type-related checks, but mainly it will check that every instance of a class conforms to any restrictions on that class.
+.It Cm inverseFunctionalProperty
+Checks that at most one instance has a given value of an inverse functional property.
+.It Cm literalInstance
+Checks that there are no literals where an instance is expected.
+.It Cm literalMaxExclusive
+Checks that literal values are not greater than or equal to any applicable xsd:maxExclusive datatype restrictions.
+.It Cm literalMaxInclusive
+Checks that literal values are not greater than any applicable xsd:maxInclusive datatype restrictions.
+.It Cm literalMinExclusive
+Checks that literal values are not less than or equal to any applicable xsd:minExclusive datatype restrictions.
+.It Cm literalMinInclusive
+Checks that literal values are not less than any applicable xsd:minInclusive datatype restrictions.
+.It Cm literalPattern
+Checks that literals with xsd:pattern restrictions match the regular expression pattern for their datatype.
+.It Cm literalRestriction
+Checks that literals with supported restrictions conform to those restrictions.
+This is a high-level check that triggers the more specific individual literal restriction checks.
+.It Cm literalValue
+Checks that literals with supported XSD datatypes are valid.
+The set of supported types is the same as when writing canonical forms.
+.It Cm objectProperty
+Checks that object properties have instance (not literal) values.
+.It Cm plainLiteralDatatype
+Checks that there are no typed literals where a plain literal is expected.
+A plain literal may have an optional language tag, but not a datatype.
+.It Cm predicateType
+Checks that every predicate is defined as an rdf:Property.
+.It Cm propertyCycle
+Checks that no property is a sub-property of itself, recursively.
+This ensures that the graph is acyclic with respect to rdfs:subPropertyOf.
+If this check fails, all further checks are aborted.
+.It Cm propertyDomain
+Checks that any instance with a property with an rdfs:domain is in that domain.
+.It Cm propertyLabel
+Checks that every rdf:Property has an rdfs:label.
+.It Cm propertyRange
+Checks that the value for any property with an rdfs:range is in that range.
+.It Cm someValuesFrom
+Checks that instances of classes with owl:someValuesFrom property restrictions have at least one matching property value.
+.El
+.Sh EXIT STATUS
+.Nm
+exits with a status of 0 on success,
+17 if the data is invalid,
+or another non-zero status if an error occured.
+.Sh EXAMPLES
+To run all checks on a file:
+.Pp
+.Dl $ serd-validate -W all input.ttl
+.Pp
+To run only the
+.Li classLabel
+check on a file:
+.Pp
+.Dl $ serd-validate -W classLabel input.ttl
+.Pp
+To run all checks
+.Em except
+.Li classLabel
+on a file:
+.Pp
+.Dl $ serd-validate -W all -X classLabel input.ttl
+.Sh SEE ALSO
+.Bl -item -compact
+.It
+.Xr serd-pipe 1
+.It
+.Xr serd-filter 1
+.It
+.Xr serd-sort 1
+.It
+.Lk http://drobilla.net/software/serd/
+.El
+.Sh STANDARDS
+.Bl -item -compact
+.It
+.Rs
+.%A W3C
+.%T OWL 2 Web Ontology Language
+.%D December 2012
+.Re
+.Lk https://www.w3.org/TR/owl2-syntax/
+.It
+.Rs
+.%A W3C
+.%D February 2014
+.%T RDF Schema 1.1
+.Re
+.Lk https://www.w3.org/TR/rdf-schema/
+.El
+.Sh AUTHORS
+.Nm
+is a part of serd, by
+.An David Robillard
+.Mt d@drobilla.net .
diff --git a/include/serd/serd.h b/include/serd/serd.h
index e450f853..6a67c322 100644
--- a/include/serd/serd.h
+++ b/include/serd/serd.h
@@ -337,6 +337,7 @@ typedef enum {
SERD_BAD_TEXT, ///< Invalid text encoding
SERD_BAD_URI, ///< Invalid or unresolved URI
SERD_BAD_WRITE, ///< Error writing to file
+ SERD_BAD_DATA, ///< Invalid data
} SerdStatus;
/**
@@ -3298,7 +3299,19 @@ serd_model_clear(SerdModel* SERD_NONNULL model);
@{
*/
-/// Create an inserter for writing statements to a model
+/**
+ Create an inserter for writing statements to a model.
+
+ Once created, an inserter is just a sink with no additional interface.
+
+ @param model The model to insert received statements into.
+
+ @param default_graph Optional default graph, which will be set on received
+ statements that have no graph. This allows, for example, loading a Turtle
+ document into an isolated graph in the model.
+
+ @return A newly allocated sink which must be freed with serd_sink_free().
+*/
SERD_API
SerdSink* SERD_ALLOCATED
serd_inserter_new(SerdModel* SERD_NONNULL model,
@@ -3306,6 +3319,270 @@ serd_inserter_new(SerdModel* SERD_NONNULL model,
/**
@}
+ @defgroup serd_validator Validator
+ @{
+*/
+
+/// Model validator
+typedef struct SerdValidatorImpl SerdValidator;
+
+/// A check that a validator can perform against a model
+typedef enum {
+ /// Checks nothing and always succeeds (for use as a sentinel)
+ SERD_CHECK_NOTHING,
+
+ /**
+ Checks that all properties with owl:allValuesFrom restrictions have
+ valid value types.
+ */
+ SERD_CHECK_ALL_VALUES_FROM,
+
+ /// Checks that the value of any property with range xsd:anyURI is a URI
+ SERD_CHECK_ANY_URI,
+
+ /**
+ Checks that any instance of a class with a owl:cardinality property
+ restriction has exactly that many values of that property.
+ */
+ SERD_CHECK_CARDINALITY_EQUAL,
+
+ /**
+ Checks that any instance of a class with a owl:maxCardinality
+ property restriction has no more than that many values of that
+ property.
+ */
+ SERD_CHECK_CARDINALITY_MAX,
+
+ /**
+ Checks that any instance of a class with a owl:minCardinality
+ property restriction has at least that many values of that property.
+ */
+ SERD_CHECK_CARDINALITY_MIN,
+
+ /**
+ Checks that no class is a sub-class of itself, recursively.
+
+ This ensures that the graph is acyclic with respect to rdfs:subClassOf.
+ If this check fails, all further checks are aborted.
+ */
+ SERD_CHECK_CLASS_CYCLE,
+
+ /// Checks that every rdfs:Class has an rdfs:label
+ SERD_CHECK_CLASS_LABEL,
+
+ /**
+ Checks that no datatype is a sub-datatype of itself, recursively.
+
+ This ensures that the graph is acyclic with respect to owl:onDatatype. If
+ this check fails, all further checks are aborted.
+ */
+ SERD_CHECK_DATATYPE_CYCLE,
+
+ /// Checks that datatype properties have literal (not instance) values
+ SERD_CHECK_DATATYPE_PROPERTY,
+
+ /// Checks that every datatype is defined as a rdfs:Datatype
+ SERD_CHECK_DATATYPE_TYPE,
+
+ /// Checks that there are no instances of deprecated classes
+ SERD_CHECK_DEPRECATED_CLASS,
+
+ /// Checks that there are no uses of deprecated properties
+ SERD_CHECK_DEPRECATED_PROPERTY,
+
+ /**
+ Checks that every instance explicitly has every type required of it.
+
+ This is a (often overly) strict check that assumes a closed world and
+ requires every instance to explicitly have the type(s) required of
+ it.
+ */
+ SERD_CHECK_EXPLICIT_INSTANCE_TYPE,
+
+ /// Checks that no instance has several values of a functional property
+ SERD_CHECK_FUNCTIONAL_PROPERTY,
+
+ /// Checks that there are no instances where a literal is expected
+ SERD_CHECK_INSTANCE_LITERAL,
+
+ /**
+ Checks that every instance with an explicit type matches that type.
+
+ This is a broad check that triggers other type-related checks, but mainly
+ it will check that every instance of a class conforms to any
+ restrictions on that class.
+ */
+ SERD_CHECK_INSTANCE_TYPE,
+
+ /**
+ Checks that at most one instance has a given value of an inverse
+ functional property.
+ */
+ SERD_CHECK_INVERSE_FUNCTIONAL_PROPERTY,
+
+ /// Checks that there are no literals where an instance is expected
+ SERD_CHECK_LITERAL_INSTANCE,
+
+ /**
+ Checks that literal values are not greater than or equal to any
+ applicable xsd:maxExclusive datatype restrictions.
+ */
+ SERD_CHECK_LITERAL_MAX_EXCLUSIVE,
+
+ /**
+ Checks that literal values are not greater than any applicable
+ xsd:maxInclusive datatype restrictions.
+ */
+ SERD_CHECK_LITERAL_MAX_INCLUSIVE,
+
+ /**
+ Checks that literal values are not less than or equal to any
+ applicable xsd:minExclusive datatype restrictions.
+ */
+ SERD_CHECK_LITERAL_MIN_EXCLUSIVE,
+
+ /**
+ Checks that literal values are not less than any applicable
+ xsd:minInclusive datatype restrictions.
+ */
+ SERD_CHECK_LITERAL_MIN_INCLUSIVE,
+
+ /**
+ Checks that literals with xsd:pattern restrictions match the regular
+ expression pattern for their datatype.
+ */
+ SERD_CHECK_LITERAL_PATTERN,
+
+ /**
+ Checks that literals with supported restrictions conform to those
+ restrictions.
+
+ This is a high-level check that triggers the more specific individual
+ literal restriction checks.
+ */
+ SERD_CHECK_LITERAL_RESTRICTION,
+
+ /**
+ Checks that literals with supported XSD datatypes are valid.
+
+ The set of supported types is the same as when writing canonical forms.
+ */
+ SERD_CHECK_LITERAL_VALUE,
+
+ /// Checks that object properties have instance (not literal) values
+ SERD_CHECK_OBJECT_PROPERTY,
+
+ /**
+ Checks that there are no typed literals where a plain literal is expected.
+
+ A plain literal may have an optional language tag, but not a datatype.
+ */
+ SERD_CHECK_PLAIN_LITERAL_DATATYPE,
+
+ /// Checks that every predicate is defined as an rdf:Property
+ SERD_CHECK_PREDICATE_TYPE,
+
+ /**
+ Checks that no property is a sub-property of itself, recursively.
+
+ This ensures that the graph is acyclic with respect to rdfs:subPropertyOf.
+ If this check fails, all further checks are aborted.
+ */
+ SERD_CHECK_PROPERTY_CYCLE,
+
+ /**
+ Checks that any instance with a property with an rdfs:domain is in
+ that domain.
+ */
+ SERD_CHECK_PROPERTY_DOMAIN,
+
+ /// Checks that every rdf:Property has an rdfs:label
+ SERD_CHECK_PROPERTY_LABEL,
+
+ /**
+ Checks that the value for any property with an rdfs:range is in that
+ range.
+ */
+ SERD_CHECK_PROPERTY_RANGE,
+
+ /**
+ Checks that instances of classes with owl:someValuesFrom property
+ restrictions have at least one matching property value.
+ */
+ SERD_CHECK_SOME_VALUES_FROM,
+} SerdValidatorCheck;
+
+/**
+ Create a new validator.
+
+ @return A newly-allocated validator with no checks enabled which must be
+ freed with serd_validator_free().
+*/
+SERD_MALLOC_API
+SerdValidator* SERD_ALLOCATED
+serd_validator_new(SerdWorld* SERD_NONNULL world);
+
+/// Free `validator`
+SERD_API
+void
+serd_validator_free(SerdValidator* SERD_NULLABLE validator);
+
+/// Enable a validator check
+SERD_API
+SerdStatus
+serd_validator_enable_check(SerdValidator* SERD_NONNULL validator,
+ SerdValidatorCheck check);
+
+/// Disable a validator check
+SERD_API
+SerdStatus
+serd_validator_disable_check(SerdValidator* SERD_NONNULL validator,
+ SerdValidatorCheck check);
+
+/// Enable all validator checks with names that match the given pattern
+SERD_API
+SerdStatus
+serd_validator_enable_checks(SerdValidator* SERD_NONNULL validator,
+ const char* SERD_NONNULL regex);
+
+/// Disable all validator checks with names that match the given pattern
+SERD_API
+SerdStatus
+serd_validator_disable_checks(SerdValidator* SERD_NONNULL validator,
+ const char* SERD_NONNULL regex);
+
+/**
+ Validate a model.
+
+ This performs validation based on the XSD, RDF, RDFS, and OWL vocabularies.
+ All necessary data, including those vocabularies and any property/class
+ definitions that use them, are assumed to be in the model.
+
+ Validation errors are reported to the world's error sink.
+
+ @param validator Validator configured to run the desired checks.
+
+ @param model The model to validate.
+
+ @param graph Optional graph to check. Is this is given, then top-level
+ checks will be initiated only for statements in the given graph. The entire
+ model is still searched while running a check so that, for example, schemas
+ that define classes and properties can be stored in separate graphs.
+
+ @param env Environment used to shorten URIs in log messages.
+
+ @return #SERD_SUCCESS if no errors are found, or #SERD_BAD_DATA if
+ validation checks failed.
+*/
+SERD_API
+SerdStatus
+serd_validate(SerdValidator* SERD_NONNULL const validator,
+ const SerdModel* SERD_NONNULL model,
+ const SerdNode* SERD_NULLABLE graph,
+ const SerdEnv* SERD_NULLABLE env);
+
+/**
+ @}
@}
*/
diff --git a/meson.build b/meson.build
index 326e5fbf..0620db02 100644
--- a/meson.build
+++ b/meson.build
@@ -58,6 +58,7 @@ if get_option('strict')
'/wd4710', # function not inlined
'/wd4711', # function selected for automatic inline expansion
'/wd4774', # format string is not a string literal
+ '/wd4777', # format string '%lld' requires a '__int64_ argument
'/wd4800', # implicit conversion from int to bool
'/wd4820', # padding added after construct
'/wd4996', # POSIX name for this item is deprecated
@@ -118,6 +119,7 @@ sources = [
'src/syntax.c',
'src/system.c',
'src/uri.c',
+ 'src/validate.c',
'src/value.c',
'src/world.c',
'src/writer.c',
@@ -179,6 +181,10 @@ exess_dep = dependency('exess-0',
version: '>= 0.0.1',
fallback: ['exess', 'exess_dep'])
+rerex_dep = dependency('rerex-0',
+ version: '>= 0.0.1',
+ fallback: ['rerex', 'rerex_dep'])
+
# Build shared and/or static library/libraries
libserd = build_target(
library_name,
@@ -186,7 +192,7 @@ libserd = build_target(
version: meson.project_version(),
include_directories: include_directories('include', 'src'),
c_args: c_warnings + platform_args + library_args,
- dependencies: [exess_dep, m_dep, zix_dep],
+ dependencies: [exess_dep, m_dep, rerex_dep, zix_dep],
gnu_symbol_visibility: 'hidden',
install: true,
target_type: library_type)
@@ -225,6 +231,8 @@ if get_option('tests')
subdir('test')
endif
+subdir('schemas')
+
if not meson.is_subproject() and meson.version().version_compare('>=0.53.0')
summary('Tests', get_option('tests'), bool_yn: true)
summary('Tools', get_option('tools'), bool_yn: true)
diff --git a/schemas/dc.ttl b/schemas/dc.ttl
new file mode 100644
index 00000000..3cf79c28
--- /dev/null
+++ b/schemas/dc.ttl
@@ -0,0 +1,101 @@
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
+@prefix dcam: <http://purl.org/dc/dcam/> .
+@prefix dcterms: <http://purl.org/dc/terms/> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://purl.org/dc/elements/1.1/>
+ dcterms:modified "2012-06-14"^^<http://www.w3.org/2001/XMLSchema#date> ;
+ dcterms:publisher <http://purl.org/dc/aboutdcmi#DCMI> ;
+ dcterms:title "Dublin Core Metadata Element Set, Version 1.1"@en .
+
+<http://purl.org/dc/elements/1.1/contributor>
+ a rdf:Property ;
+ rdfs:comment "An entity responsible for making contributions to the resource."@en ;
+ rdfs:isDefinedBy <http://purl.org/dc/elements/1.1/> ;
+ rdfs:label "Contributor"@en .
+
+<http://purl.org/dc/elements/1.1/coverage>
+ a rdf:Property ;
+ rdfs:comment "The spatial or temporal topic of the resource, spatial applicability of the resource, or jurisdiction under which the resource is relevant."@en ;
+ rdfs:isDefinedBy <http://purl.org/dc/elements/1.1/> ;
+ rdfs:label "Coverage"@en .
+
+<http://purl.org/dc/elements/1.1/creator>
+ a rdf:Property ;
+ rdfs:comment "An entity primarily responsible for making the resource."@en ;
+ rdfs:isDefinedBy <http://purl.org/dc/elements/1.1/> ;
+ rdfs:label "Creator"@en .
+
+<http://purl.org/dc/elements/1.1/date>
+ a rdf:Property ;
+ rdfs:comment "A point or period of time associated with an event in the lifecycle of the resource."@en ;
+ rdfs:isDefinedBy <http://purl.org/dc/elements/1.1/> ;
+ rdfs:label "Date"@en .
+
+<http://purl.org/dc/elements/1.1/description>
+ a rdf:Property ;
+ rdfs:comment "An account of the resource."@en ;
+ rdfs:isDefinedBy <http://purl.org/dc/elements/1.1/> ;
+ rdfs:label "Description"@en .
+
+<http://purl.org/dc/elements/1.1/format>
+ a rdf:Property ;
+ rdfs:comment "The file format, physical medium, or dimensions of the resource."@en ;
+ rdfs:isDefinedBy <http://purl.org/dc/elements/1.1/> ;
+ rdfs:label "Format"@en .
+
+<http://purl.org/dc/elements/1.1/identifier>
+ a rdf:Property ;
+ rdfs:comment "An unambiguous reference to the resource within a given context."@en ;
+ rdfs:isDefinedBy <http://purl.org/dc/elements/1.1/> ;
+ rdfs:label "Identifier"@en .
+
+<http://purl.org/dc/elements/1.1/language>
+ a rdf:Property ;
+ rdfs:comment "A language of the resource."@en ;
+ rdfs:isDefinedBy <http://purl.org/dc/elements/1.1/> ;
+ rdfs:label "Language"@en .
+
+<http://purl.org/dc/elements/1.1/publisher>
+ a rdf:Property ;
+ rdfs:comment "An entity responsible for making the resource available."@en ;
+ rdfs:isDefinedBy <http://purl.org/dc/elements/1.1/> ;
+ rdfs:label "Publisher"@en .
+
+<http://purl.org/dc/elements/1.1/relation>
+ a rdf:Property ;
+ rdfs:comment "A related resource."@en ;
+ rdfs:isDefinedBy <http://purl.org/dc/elements/1.1/> ;
+ rdfs:label "Relation"@en .
+
+<http://purl.org/dc/elements/1.1/rights>
+ a rdf:Property ;
+ rdfs:comment "Information about rights held in and over the resource."@en ;
+ rdfs:isDefinedBy <http://purl.org/dc/elements/1.1/> ;
+ rdfs:label "Rights"@en .
+
+<http://purl.org/dc/elements/1.1/source>
+ a rdf:Property ;
+ rdfs:comment "A related resource from which the described resource is derived."@en ;
+ rdfs:isDefinedBy <http://purl.org/dc/elements/1.1/> ;
+ rdfs:label "Source"@en .
+
+<http://purl.org/dc/elements/1.1/subject>
+ a rdf:Property ;
+ rdfs:comment "The topic of the resource."@en ;
+ rdfs:isDefinedBy <http://purl.org/dc/elements/1.1/> ;
+ rdfs:label "Subject"@en .
+
+<http://purl.org/dc/elements/1.1/title>
+ a rdf:Property ;
+ rdfs:comment "A name given to the resource."@en ;
+ rdfs:isDefinedBy <http://purl.org/dc/elements/1.1/> ;
+ rdfs:label "Title"@en .
+
+<http://purl.org/dc/elements/1.1/type>
+ a rdf:Property ;
+ rdfs:comment "The nature or genre of the resource."@en ;
+ rdfs:isDefinedBy <http://purl.org/dc/elements/1.1/> ;
+ rdfs:label "Type"@en .
diff --git a/schemas/dcam.ttl b/schemas/dcam.ttl
new file mode 100644
index 00000000..a559f5c6
--- /dev/null
+++ b/schemas/dcam.ttl
@@ -0,0 +1,42 @@
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
+@prefix dcam: <http://purl.org/dc/dcam/> .
+@prefix dcterms: <http://purl.org/dc/terms/> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+dcam:
+ dcterms:modified "2012-06-14"^^<http://www.w3.org/2001/XMLSchema#date> ;
+ dcterms:publisher <http://purl.org/dc/aboutdcmi#DCMI> ;
+ dcterms:title "Metadata terms for vocabulary description"@en .
+
+dcam:VocabularyEncodingScheme
+ a rdfs:Class ;
+ dcterms:issued "2008-01-14"^^<http://www.w3.org/2001/XMLSchema#date> ;
+ rdfs:comment "An enumerated set of resources."@en ;
+ rdfs:isDefinedBy dcam: ;
+ rdfs:label "Vocabulary Encoding Scheme"@en ;
+ rdfs:seeAlso <https://www.dublincore.org/specifications/dublin-core/2007/06/04/abstract-model/> .
+
+dcam:domainIncludes
+ a rdf:Property ;
+ dcterms:issued "2020-01-20"^^<http://www.w3.org/2001/XMLSchema#date> ;
+ rdfs:comment "A suggested class for subjects of this property."@en ;
+ rdfs:isDefinedBy dcam: ;
+ rdfs:label "Domain Includes"@en .
+
+dcam:memberOf
+ a rdf:Property ;
+ dcterms:issued "2008-01-14"^^<http://www.w3.org/2001/XMLSchema#date> ;
+ rdfs:comment "A relationship between a resource and a vocabulary encoding scheme which indicates that the resource is a member of a set."@en ;
+ rdfs:isDefinedBy dcam: ;
+ rdfs:label "Member Of"@en ;
+ rdfs:range dcam:VocabularyEncodingScheme ;
+ rdfs:seeAlso <https://www.dublincore.org/specifications/dublin-core/2007/06/04/abstract-model/> .
+
+dcam:rangeIncludes
+ a rdf:Property ;
+ dcterms:issued "2020-01-20"^^<http://www.w3.org/2001/XMLSchema#date> ;
+ rdfs:comment "A suggested class for values of this property."@en ;
+ rdfs:isDefinedBy dcam: ;
+ rdfs:label "Range Includes"@en .
diff --git a/schemas/dcterms.ttl b/schemas/dcterms.ttl
new file mode 100644
index 00000000..f93bf50d
--- /dev/null
+++ b/schemas/dcterms.ttl
@@ -0,0 +1,906 @@
+@prefix dc: <http://purl.org/dc/elements/1.1/> .
+@prefix dcam: <http://purl.org/dc/dcam/> .
+@prefix dcterms: <http://purl.org/dc/terms/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+dcterms:
+ dcterms:modified "2012-06-14"^^xsd:date ;
+ dcterms:publisher <http://purl.org/dc/aboutdcmi#DCMI> ;
+ dcterms:title "DCMI Metadata Terms - other"@en .
+
+dcterms:Agent
+ a dcterms:AgentClass ,
+ rdfs:Class ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A resource that acts or has the power to act."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Agent"@en .
+
+dcterms:AgentClass
+ a rdfs:Class ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A group of agents."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Agent Class"@en ;
+ rdfs:subClassOf rdfs:Class .
+
+dcterms:BibliographicResource
+ a rdfs:Class ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A book, article, or other documentary resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Bibliographic Resource"@en .
+
+dcterms:Box
+ a rdfs:Datatype ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "The set of regions in space defined by their geographic coordinates according to the DCMI Box Encoding Scheme."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "DCMI Box"@en ;
+ rdfs:seeAlso <https://www.dublincore.org/specifications/dublin-core/dcmi-box/> .
+
+dcterms:DCMIType
+ a dcam:VocabularyEncodingScheme ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "The set of classes specified by the DCMI Type Vocabulary, used to categorize the nature or genre of the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "DCMI Type Vocabulary"@en ;
+ rdfs:seeAlso <http://purl.org/dc/dcmitype/> .
+
+dcterms:DDC
+ a dcam:VocabularyEncodingScheme ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "The set of conceptual resources specified by the Dewey Decimal Classification."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "DDC"@en ;
+ rdfs:seeAlso <http://www.oclc.org/dewey/> .
+
+dcterms:FileFormat
+ a rdfs:Class ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A digital resource format."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "File Format"@en ;
+ rdfs:subClassOf dcterms:MediaType .
+
+dcterms:Frequency
+ a rdfs:Class ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A rate at which something recurs."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Frequency"@en .
+
+dcterms:IMT
+ a dcam:VocabularyEncodingScheme ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "The set of media types specified by the Internet Assigned Numbers Authority."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "IMT"@en ;
+ rdfs:seeAlso <http://www.iana.org/assignments/media-types/> .
+
+dcterms:ISO3166
+ a rdfs:Datatype ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "The set of codes listed in ISO 3166-1 for the representation of names of countries."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "ISO 3166"@en ;
+ rdfs:seeAlso <https://www.iso.org/obp/ui/#search> .
+
+dcterms:ISO639-2
+ a rdfs:Datatype ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "The three-letter alphabetic codes listed in ISO639-2 for the representation of names of languages."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "ISO 639-2"@en ;
+ rdfs:seeAlso <http://lcweb.loc.gov/standards/iso639-2/langhome.html> .
+
+dcterms:ISO639-3
+ a rdfs:Datatype ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "The set of three-letter codes listed in ISO 639-3 for the representation of names of languages."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "ISO 639-3"@en ;
+ rdfs:seeAlso <http://www.sil.org/iso639-3/> .
+
+dcterms:Jurisdiction
+ a rdfs:Class ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "The extent or range of judicial, law enforcement, or other authority."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Jurisdiction"@en ;
+ rdfs:subClassOf dcterms:LocationPeriodOrJurisdiction .
+
+dcterms:LCC
+ a dcam:VocabularyEncodingScheme ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "The set of conceptual resources specified by the Library of Congress Classification."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "LCC"@en ;
+ rdfs:seeAlso <http://lcweb.loc.gov/catdir/cpso/lcco/lcco.html> .
+
+dcterms:LCSH
+ a dcam:VocabularyEncodingScheme ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "The set of labeled concepts specified by the Library of Congress Subject Headings."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "LCSH"@en .
+
+dcterms:LicenseDocument
+ a rdfs:Class ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A legal document giving official permission to do something with a resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "License Document"@en ;
+ rdfs:subClassOf dcterms:RightsStatement .
+
+dcterms:LinguisticSystem
+ a rdfs:Class ;
+ dcterms:description "Written, spoken, sign, and computer languages are linguistic systems."@en ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A system of signs, symbols, sounds, gestures, or rules used in communication."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Linguistic System"@en .
+
+dcterms:Location
+ a rdfs:Class ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A spatial region or named place."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Location"@en ;
+ rdfs:subClassOf dcterms:LocationPeriodOrJurisdiction .
+
+dcterms:LocationPeriodOrJurisdiction
+ a rdfs:Class ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A location, period of time, or jurisdiction."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Location, Period, or Jurisdiction"@en .
+
+dcterms:MESH
+ a dcam:VocabularyEncodingScheme ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "The set of labeled concepts specified by the Medical Subject Headings."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "MeSH"@en ;
+ rdfs:seeAlso <http://www.nlm.nih.gov/mesh/meshhome.html> .
+
+dcterms:MediaType
+ a rdfs:Class ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A file format or physical medium."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Media Type"@en ;
+ rdfs:subClassOf dcterms:MediaTypeOrExtent .
+
+dcterms:MediaTypeOrExtent
+ a rdfs:Class ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A media type or extent."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Media Type or Extent"@en .
+
+dcterms:MethodOfAccrual
+ a rdfs:Class ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A method by which resources are added to a collection."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Method of Accrual"@en .
+
+dcterms:MethodOfInstruction
+ a rdfs:Class ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A process that is used to engender knowledge, attitudes, and skills."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Method of Instruction"@en .
+
+dcterms:NLM
+ a dcam:VocabularyEncodingScheme ;
+ dcterms:issued "2005-06-13"^^xsd:date ;
+ rdfs:comment "The set of conceptual resources specified by the National Library of Medicine Classification."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "NLM"@en ;
+ rdfs:seeAlso <http://wwwcf.nlm.nih.gov/class/> .
+
+dcterms:Period
+ a rdfs:Datatype ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "The set of time intervals defined by their limits according to the DCMI Period Encoding Scheme."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "DCMI Period"@en ;
+ rdfs:seeAlso <https://www.dublincore.org/specifications/dublin-core/dcmi-period/> .
+
+dcterms:PeriodOfTime
+ a rdfs:Class ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "An interval of time that is named or defined by its start and end dates."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Period of Time"@en ;
+ rdfs:subClassOf dcterms:LocationPeriodOrJurisdiction .
+
+dcterms:PhysicalMedium
+ a rdfs:Class ;
+ dcterms:description "Examples include paper, canvas, or DVD."@en ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A physical material or carrier."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Physical Medium"@en ;
+ rdfs:subClassOf dcterms:MediaType .
+
+dcterms:PhysicalResource
+ a rdfs:Class ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A material thing."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Physical Resource"@en .
+
+dcterms:Point
+ a rdfs:Datatype ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "The set of points in space defined by their geographic coordinates according to the DCMI Point Encoding Scheme."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "DCMI Point"@en ;
+ rdfs:seeAlso <https://www.dublincore.org/specifications/dublin-core/dcmi-point/> .
+
+dcterms:Policy
+ a rdfs:Class ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A plan or course of action by an authority, intended to influence and determine decisions, actions, and other matters."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Policy"@en .
+
+dcterms:ProvenanceStatement
+ a rdfs:Class ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "Any changes in ownership and custody of a resource since its creation that are significant for its authenticity, integrity, and interpretation."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Provenance Statement"@en .
+
+dcterms:RFC1766
+ a rdfs:Datatype ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "The set of tags, constructed according to RFC 1766, for the identification of languages."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "RFC 1766"@en ;
+ rdfs:seeAlso <http://www.ietf.org/rfc/rfc1766.txt> .
+
+dcterms:RFC3066
+ a rdfs:Datatype ;
+ dcterms:description "RFC 3066 has been obsoleted by RFC 4646."@en ;
+ dcterms:issued "2002-07-13"^^xsd:date ;
+ rdfs:comment "The set of tags constructed according to RFC 3066 for the identification of languages."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "RFC 3066"@en ;
+ rdfs:seeAlso <http://www.ietf.org/rfc/rfc3066.txt> .
+
+dcterms:RFC4646
+ a rdfs:Datatype ;
+ dcterms:description "RFC 4646 obsoletes RFC 3066."@en ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "The set of tags constructed according to RFC 4646 for the identification of languages."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "RFC 4646"@en ;
+ rdfs:seeAlso <http://www.ietf.org/rfc/rfc4646.txt> .
+
+dcterms:RFC5646
+ a rdfs:Datatype ;
+ dcterms:description "RFC 5646 obsoletes RFC 4646."@en ;
+ dcterms:issued "2010-10-11"^^xsd:date ;
+ rdfs:comment "The set of tags constructed according to RFC 5646 for the identification of languages."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "RFC 5646"@en ;
+ rdfs:seeAlso <http://www.ietf.org/rfc/rfc5646.txt> .
+
+dcterms:RightsStatement
+ a rdfs:Class ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A statement about the intellectual property rights (IPR) held in or over a resource, a legal document giving official permission to do something with a resource, or a statement about access rights."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Rights Statement"@en .
+
+dcterms:SizeOrDuration
+ a rdfs:Class ;
+ dcterms:description "Examples include a number of pages, a specification of length, width, and breadth, or a period in hours, minutes, and seconds."@en ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A dimension or extent, or a time taken to play or execute."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Size or Duration"@en ;
+ rdfs:subClassOf dcterms:MediaTypeOrExtent .
+
+dcterms:Standard
+ a rdfs:Class ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A reference point against which other things can be evaluated or compared."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Standard"@en .
+
+dcterms:TGN
+ a dcam:VocabularyEncodingScheme ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "The set of places specified by the Getty Thesaurus of Geographic Names."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "TGN"@en ;
+ rdfs:seeAlso <http://www.getty.edu/research/tools/vocabulary/tgn/index.html> .
+
+dcterms:UDC
+ a dcam:VocabularyEncodingScheme ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "The set of conceptual resources specified by the Universal Decimal Classification."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "UDC"@en ;
+ rdfs:seeAlso <http://www.udcc.org/> .
+
+dcterms:URI
+ a rdfs:Datatype ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "The set of identifiers constructed according to the generic syntax for Uniform Resource Identifiers as specified by the Internet Engineering Task Force."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "URI"@en ;
+ rdfs:seeAlso <http://www.ietf.org/rfc/rfc3986.txt> .
+
+dcterms:W3CDTF
+ a rdfs:Datatype ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "The set of dates and times constructed according to the W3C Date and Time Formats Specification."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "W3C-DTF"@en ;
+ rdfs:seeAlso <http://www.w3.org/TR/NOTE-datetime> .
+
+dcterms:abstract
+ a rdf:Property ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "A summary of the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Abstract"@en ;
+ rdfs:subPropertyOf dc:description ,
+ dcterms:description .
+
+dcterms:accessRights
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:RightsStatement ;
+ dcterms:description "Access Rights may include information regarding access or restrictions based on privacy, security, or other policies."@en ;
+ dcterms:issued "2003-02-15"^^xsd:date ;
+ rdfs:comment "Information about who access the resource or an indication of its security status."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Access Rights"@en ;
+ rdfs:subPropertyOf dc:rights ,
+ dcterms:rights .
+
+dcterms:accrualMethod
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:MethodOfAccrual ;
+ dcterms:description "Recommended practice is to use a value from the Collection Description Accrual Method Vocabulary [[DCMI-ACCRUALMETHOD](https://dublincore.org/groups/collections/accrual-method/)]."@en ;
+ dcterms:issued "2005-06-13"^^xsd:date ;
+ rdfs:comment "The method by which items are added to a collection."@en ;
+ rdfs:domain <http://purl.org/dc/dcmitype/Collection> ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Accrual Method"@en .
+
+dcterms:accrualPeriodicity
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:Frequency ;
+ dcterms:description "Recommended practice is to use a value from the Collection Description Frequency Vocabulary [[DCMI-COLLFREQ](https://dublincore.org/groups/collections/frequency/)]."@en ;
+ dcterms:issued "2005-06-13"^^xsd:date ;
+ rdfs:comment "The frequency with which items are added to a collection."@en ;
+ rdfs:domain <http://purl.org/dc/dcmitype/Collection> ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Accrual Periodicity"@en .
+
+dcterms:accrualPolicy
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:Policy ;
+ dcterms:description "Recommended practice is to use a value from the Collection Description Accrual Policy Vocabulary [[DCMI-ACCRUALPOLICY](https://dublincore.org/groups/collections/accrual-policy/)]."@en ;
+ dcterms:issued "2005-06-13"^^xsd:date ;
+ rdfs:comment "The policy governing the addition of items to a collection."@en ;
+ rdfs:domain <http://purl.org/dc/dcmitype/Collection> ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Accrual Policy"@en .
+
+dcterms:alternative
+ a rdf:Property ;
+ dcterms:description "The distinction between titles and alternative titles is application-specific."@en ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "An alternative name for the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Alternative Title"@en ;
+ rdfs:range rdfs:Literal ;
+ rdfs:subPropertyOf dc:title ,
+ dcterms:title .
+
+dcterms:audience
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:AgentClass ;
+ dcterms:description "Recommended practice is to use this property with non-literal values from a vocabulary of audience types."@en ;
+ dcterms:issued "2001-05-21"^^xsd:date ;
+ rdfs:comment "A class of agents for whom the resource is intended or useful."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Audience"@en .
+
+dcterms:available
+ a rdf:Property ;
+ dcterms:description "Recommended practice is to describe the date, date/time, or period of time as recommended for the property Date, of which this is a subproperty."@en ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "Date that the resource became or will become available."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Date Available"@en ;
+ rdfs:range rdfs:Literal ;
+ rdfs:subPropertyOf dc:date ,
+ dcterms:date .
+
+dcterms:bibliographicCitation
+ a rdf:Property ;
+ dcterms:description "Recommended practice is to include sufficient bibliographic detail to identify the resource as unambiguously as possible."@en ;
+ dcterms:issued "2003-02-15"^^xsd:date ;
+ rdfs:comment "A bibliographic reference for the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Bibliographic Citation"@en ;
+ rdfs:range rdfs:Literal ;
+ rdfs:subPropertyOf dc:identifier ,
+ dcterms:identifier .
+
+dcterms:conformsTo
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:Standard ;
+ dcterms:issued "2001-05-21"^^xsd:date ;
+ rdfs:comment "An established standard to which the described resource conforms."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Conforms To"@en ;
+ rdfs:subPropertyOf dc:relation ,
+ dcterms:relation .
+
+dcterms:contributor
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:Agent ;
+ dcterms:description "The guidelines for using names of persons or organizations as creators apply to contributors."@en ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "An entity responsible for making contributions to the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Contributor"@en ;
+ rdfs:subPropertyOf dc:contributor .
+
+dcterms:coverage
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:Jurisdiction ,
+ dcterms:Location ,
+ dcterms:Period ;
+ dcterms:description "Spatial topic and spatial applicability may be a named place or a location specified by its geographic coordinates. Temporal topic may be a named period, date, or date range. A jurisdiction may be a named administrative entity or a geographic place to which the resource applies. Recommended practice is to use a controlled vocabulary such as the Getty Thesaurus of Geographic Names [[TGN](https://www.getty.edu/research/tools/vocabulary/tgn/index.html)]. Where appropriate, named places or time periods may be used in preference to numeric identifiers such as sets of coordinates or date ranges. Because coverage is so broadly defined, it is preferable to use the more specific subproperties Temporal Coverage and Spatial Coverage."@en ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "The spatial or temporal topic of the resource, spatial applicability of the resource, or jurisdiction under which the resource is relevant."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Coverage"@en ;
+ rdfs:subPropertyOf dc:coverage .
+
+dcterms:created
+ a rdf:Property ;
+ dcterms:description "Recommended practice is to describe the date, date/time, or period of time as recommended for the property Date, of which this is a subproperty."@en ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "Date of creation of the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Date Created"@en ;
+ rdfs:range rdfs:Literal ;
+ rdfs:subPropertyOf dc:date ,
+ dcterms:date .
+
+dcterms:creator
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:Agent ;
+ dcterms:description "Recommended practice is to identify the creator with a URI. If this is not possible or feasible, a literal value that identifies the creator may be provided."@en ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "An entity responsible for making the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Creator"@en ;
+ rdfs:subPropertyOf dc:creator ,
+ dcterms:contributor ;
+ owl:equivalentProperty <http://xmlns.com/foaf/0.1/maker> .
+
+dcterms:date
+ a rdf:Property ;
+ dcterms:description "Date may be used to express temporal information at any level of granularity. Recommended practice is to express the date, date/time, or period of time according to ISO 8601-1 [[ISO 8601-1](https://www.iso.org/iso-8601-date-and-time-format.html)] or a published profile of the ISO standard, such as the W3C Note on Date and Time Formats [[W3CDTF](https://www.w3.org/TR/NOTE-datetime)] or the Extended Date/Time Format Specification [[EDTF](http://www.loc.gov/standards/datetime/)]. If the full date is unknown, month and year (YYYY-MM) or just year (YYYY) may be used. Date ranges may be specified using ISO 8601 period of time specification in which start and end dates are separated by a '/' (slash) character. Either the start or end date may be missing."@en ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A point or period of time associated with an event in the lifecycle of the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Date"@en ;
+ rdfs:range rdfs:Literal ;
+ rdfs:subPropertyOf dc:date .
+
+dcterms:dateAccepted
+ a rdf:Property ;
+ dcterms:description "Recommended practice is to describe the date, date/time, or period of time as recommended for the property Date, of which this is a subproperty. Examples of resources to which a date of acceptance may be relevant are a thesis (accepted by a university department) or an article (accepted by a journal)."@en ;
+ dcterms:issued "2002-07-13"^^xsd:date ;
+ rdfs:comment "Date of acceptance of the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Date Accepted"@en ;
+ rdfs:range rdfs:Literal ;
+ rdfs:subPropertyOf dc:date ,
+ dcterms:date .
+
+dcterms:dateCopyrighted
+ a rdf:Property ;
+ dcterms:description "Typically a year. Recommended practice is to describe the date, date/time, or period of time as recommended for the property Date, of which this is a subproperty."@en ;
+ dcterms:issued "2002-07-13"^^xsd:date ;
+ rdfs:comment "Date of copyright of the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Date Copyrighted"@en ;
+ rdfs:range rdfs:Literal ;
+ rdfs:subPropertyOf dc:date ,
+ dcterms:date .
+
+dcterms:dateSubmitted
+ a rdf:Property ;
+ dcterms:description "Recommended practice is to describe the date, date/time, or period of time as recommended for the property Date, of which this is a subproperty. Examples of resources to which a 'Date Submitted' may be relevant include a thesis (submitted to a university department) or an article (submitted to a journal)."@en ;
+ dcterms:issued "2002-07-13"^^xsd:date ;
+ rdfs:comment "Date of submission of the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Date Submitted"@en ;
+ rdfs:range rdfs:Literal ;
+ rdfs:subPropertyOf dc:date ,
+ dcterms:date .
+
+dcterms:description
+ a rdf:Property ;
+ dcterms:description "Description may include but is not limited to: an abstract, a table of contents, a graphical representation, or a free-text account of the resource."@en ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "An account of the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Description"@en ;
+ rdfs:subPropertyOf dc:description .
+
+dcterms:educationLevel
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:AgentClass ;
+ dcterms:issued "2002-07-13"^^xsd:date ;
+ rdfs:comment "A class of agents, defined in terms of progression through an educational or training context, for which the described resource is intended."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Audience Education Level"@en ;
+ rdfs:subPropertyOf dcterms:audience .
+
+dcterms:extent
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:SizeOrDuration ;
+ dcterms:description "Recommended practice is to specify the file size in megabytes and duration in ISO 8601 format."@en ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "The size or duration of the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Extent"@en ;
+ rdfs:subPropertyOf dc:format ,
+ dcterms:format .
+
+dcterms:format
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:Extent ,
+ dcterms:MediaType ;
+ dcterms:description "Recommended practice is to use a controlled vocabulary where available. For example, for file formats one could use the list of Internet Media Types [[MIME](https://www.iana.org/assignments/media-types/media-types.xhtml)]. Examples of dimensions include size and duration."@en ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "The file format, physical medium, or dimensions of the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Format"@en ;
+ rdfs:subPropertyOf dc:format .
+
+dcterms:hasFormat
+ a rdf:Property ;
+ dcterms:description "This property is intended to be used with non-literal values. This property is an inverse property of Is Format Of."@en ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "A related resource that is substantially the same as the pre-existing described resource, but in another format."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Has Format"@en ;
+ rdfs:subPropertyOf dc:relation ,
+ dcterms:relation .
+
+dcterms:hasPart
+ a rdf:Property ;
+ dcterms:description "This property is intended to be used with non-literal values. This property is an inverse property of Is Part Of."@en ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "A related resource that is included either physically or logically in the described resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Has Part"@en ;
+ rdfs:subPropertyOf dc:relation ,
+ dcterms:relation .
+
+dcterms:hasVersion
+ a rdf:Property ;
+ dcterms:description "Changes in version imply substantive changes in content rather than differences in format. This property is intended to be used with non-literal values. This property is an inverse property of Is Version Of."@en ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "A related resource that is a version, edition, or adaptation of the described resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Has Version"@en ;
+ rdfs:subPropertyOf dc:relation ,
+ dcterms:relation .
+
+dcterms:identifier
+ a rdf:Property ;
+ dcterms:description "Recommended practice is to identify the resource by means of a string conforming to an identification system. Examples include International Standard Book Number (ISBN), Digital Object Identifier (DOI), and Uniform Resource Name (URN). Persistent identifiers should be provided as HTTP URIs."@en ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "An unambiguous reference to the resource within a given context."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Identifier"@en ;
+ rdfs:range rdfs:Literal ;
+ rdfs:subPropertyOf dc:identifier .
+
+dcterms:instructionalMethod
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:MethodOfInstruction ;
+ dcterms:description "Instructional Method typically includes ways of presenting instructional materials or conducting instructional activities, patterns of learner-to-learner and learner-to-instructor interactions, and mechanisms by which group and individual levels of learning are measured. Instructional methods include all aspects of the instruction and learning processes from planning and implementation through evaluation and feedback."@en ;
+ dcterms:issued "2005-06-13"^^xsd:date ;
+ rdfs:comment "A process, used to engender knowledge, attitudes and skills, that the described resource is designed to support."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Instructional Method"@en .
+
+dcterms:isFormatOf
+ a rdf:Property ;
+ dcterms:description "This property is intended to be used with non-literal values. This property is an inverse property of Has Format."@en ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "A pre-existing related resource that is substantially the same as the described resource, but in another format."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Is Format Of"@en ;
+ rdfs:subPropertyOf dc:relation ,
+ dcterms:relation .
+
+dcterms:isPartOf
+ a rdf:Property ;
+ dcterms:description "This property is intended to be used with non-literal values. This property is an inverse property of Has Part."@en ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "A related resource in which the described resource is physically or logically included."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Is Part Of"@en ;
+ rdfs:subPropertyOf dc:relation ,
+ dcterms:relation .
+
+dcterms:isReferencedBy
+ a rdf:Property ;
+ dcterms:description "This property is intended to be used with non-literal values. This property is an inverse property of References."@en ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "A related resource that references, cites, or otherwise points to the described resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Is Referenced By"@en ;
+ rdfs:subPropertyOf dc:relation ,
+ dcterms:relation .
+
+dcterms:isReplacedBy
+ a rdf:Property ;
+ dcterms:description "This property is intended to be used with non-literal values. This property is an inverse property of Replaces."@en ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "A related resource that supplants, displaces, or supersedes the described resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Is Replaced By"@en ;
+ rdfs:subPropertyOf dc:relation ,
+ dcterms:relation .
+
+dcterms:isRequiredBy
+ a rdf:Property ;
+ dcterms:description "This property is intended to be used with non-literal values. This property is an inverse property of Requires."@en ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "A related resource that requires the described resource to support its function, delivery, or coherence."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Is Required By"@en ;
+ rdfs:subPropertyOf dc:relation ,
+ dcterms:relation .
+
+dcterms:isVersionOf
+ a rdf:Property ;
+ dcterms:description "Changes in version imply substantive changes in content rather than differences in format. This property is intended to be used with non-literal values. This property is an inverse property of Has Version."@en ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "A related resource of which the described resource is a version, edition, or adaptation."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Is Version Of"@en ;
+ rdfs:subPropertyOf dc:relation ,
+ dcterms:relation .
+
+dcterms:issued
+ a rdf:Property ;
+ dcterms:description "Recommended practice is to describe the date, date/time, or period of time as recommended for the property Date, of which this is a subproperty."@en ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "Date of formal issuance of the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Date Issued"@en ;
+ rdfs:range rdfs:Literal ;
+ rdfs:subPropertyOf dc:date ,
+ dcterms:date .
+
+dcterms:language
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:LinguisticSystem ;
+ dcterms:description "Recommended practice is to use either a non-literal value representing a language from a controlled vocabulary such as ISO 639-2 or ISO 639-3, or a literal value consisting of an IETF Best Current Practice 47 [[IETF-BCP47](https://tools.ietf.org/html/bcp47)] language tag."@en ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A language of the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Language"@en ;
+ rdfs:subPropertyOf dc:language .
+
+dcterms:license
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:LicenseDocument ;
+ dcterms:description "Recommended practice is to identify the license document with a URI. If this is not possible or feasible, a literal value that identifies the license may be provided."@en ;
+ dcterms:issued "2004-06-14"^^xsd:date ;
+ rdfs:comment "A legal document giving official permission to do something with the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "License"@en ;
+ rdfs:subPropertyOf dc:rights ,
+ dcterms:rights .
+
+dcterms:mediator
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:AgentClass ;
+ dcterms:description "In an educational context, a mediator might be a parent, teacher, teaching assistant, or care-giver."@en ;
+ dcterms:issued "2001-05-21"^^xsd:date ;
+ rdfs:comment "An entity that mediates access to the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Mediator"@en ;
+ rdfs:subPropertyOf dcterms:audience .
+
+dcterms:medium
+ a rdf:Property ;
+ dcam:domainIncludes dcterms:PhysicalResource ;
+ dcam:rangeIncludes dcterms:PhysicalMedium ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "The material or physical carrier of the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Medium"@en ;
+ rdfs:subPropertyOf dc:format ,
+ dcterms:format .
+
+dcterms:modified
+ a rdf:Property ;
+ dcterms:description "Recommended practice is to describe the date, date/time, or period of time as recommended for the property Date, of which this is a subproperty."@en ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "Date on which the resource was changed."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Date Modified"@en ;
+ rdfs:range rdfs:Literal ;
+ rdfs:subPropertyOf dc:date ,
+ dcterms:date .
+
+dcterms:provenance
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:ProvenanceStatement ;
+ dcterms:description "The statement may include a description of any changes successive custodians made to the resource."@en ;
+ dcterms:issued "2004-09-20"^^xsd:date ;
+ rdfs:comment "A statement of any changes in ownership and custody of the resource since its creation that are significant for its authenticity, integrity, and interpretation."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Provenance"@en .
+
+dcterms:publisher
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:Agent ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "An entity responsible for making the resource available."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Publisher"@en ;
+ rdfs:subPropertyOf dc:publisher .
+
+dcterms:references
+ a rdf:Property ;
+ dcterms:description "This property is intended to be used with non-literal values. This property is an inverse property of Is Referenced By."@en ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "A related resource that is referenced, cited, or otherwise pointed to by the described resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "References"@en ;
+ rdfs:subPropertyOf dc:relation ,
+ dcterms:relation .
+
+dcterms:relation
+ a rdf:Property ;
+ dcterms:description "Recommended practice is to identify the related resource by means of a URI. If this is not possible or feasible, a string conforming to a formal identification system may be provided."@en ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A related resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Relation"@en ;
+ rdfs:subPropertyOf dc:relation .
+
+dcterms:replaces
+ a rdf:Property ;
+ dcterms:description "This property is intended to be used with non-literal values. This property is an inverse property of Is Replaced By."@en ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "A related resource that is supplanted, displaced, or superseded by the described resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Replaces"@en ;
+ rdfs:subPropertyOf dc:relation ,
+ dcterms:relation .
+
+dcterms:requires
+ a rdf:Property ;
+ dcterms:description "This property is intended to be used with non-literal values. This property is an inverse property of Is Required By."@en ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "A related resource that is required by the described resource to support its function, delivery, or coherence."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Requires"@en ;
+ rdfs:subPropertyOf dc:relation ,
+ dcterms:relation .
+
+dcterms:rights
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:RightsStatement ;
+ dcterms:description "Typically, rights information includes a statement about various property rights associated with the resource, including intellectual property rights. Recommended practice is to refer to a rights statement with a URI. If this is not possible or feasible, a literal value (name, label, or short text) may be provided."@en ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "Information about rights held in and over the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Rights"@en ;
+ rdfs:subPropertyOf dc:rights .
+
+dcterms:rightsHolder
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:Agent ;
+ dcterms:description "Recommended practice is to refer to the rights holder with a URI. If this is not possible or feasible, a literal value that identifies the rights holder may be provided."@en ;
+ dcterms:issued "2004-06-14"^^xsd:date ;
+ rdfs:comment "A person or organization owning or managing rights over the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Rights Holder"@en .
+
+dcterms:source
+ a rdf:Property ;
+ dcterms:description "This property is intended to be used with non-literal values. The described resource may be derived from the related resource in whole or in part. Best practice is to identify the related resource by means of a URI or a string conforming to a formal identification system."@en ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A related resource from which the described resource is derived."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Source"@en ;
+ rdfs:subPropertyOf dc:source ,
+ dcterms:relation .
+
+dcterms:spatial
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:Location ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "Spatial characteristics of the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Spatial Coverage"@en ;
+ rdfs:subPropertyOf dc:coverage ,
+ dcterms:coverage .
+
+dcterms:subject
+ a rdf:Property ;
+ dcterms:description "Recommended practice is to refer to the subject with a URI. If this is not possible or feasible, a literal value that identifies the subject may be provided. Both should preferably refer to a subject in a controlled vocabulary."@en ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A topic of the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Subject"@en ;
+ rdfs:subPropertyOf dc:subject .
+
+dcterms:tableOfContents
+ a rdf:Property ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "A list of subunits of the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Table Of Contents"@en ;
+ rdfs:subPropertyOf dc:description ,
+ dcterms:description .
+
+dcterms:temporal
+ a rdf:Property ;
+ dcam:rangeIncludes dcterms:PeriodOfTime ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "Temporal characteristics of the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Temporal Coverage"@en ;
+ rdfs:subPropertyOf dc:coverage ,
+ dcterms:coverage .
+
+dcterms:title
+ a rdf:Property ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "A name given to the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Title"@en ;
+ rdfs:range rdfs:Literal ;
+ rdfs:subPropertyOf dc:title .
+
+dcterms:type
+ a rdf:Property ;
+ dcterms:description "Recommended practice is to use a controlled vocabulary such as the DCMI Type Vocabulary [[DCMI-TYPE](http://dublincore.org/documents/dcmi-type-vocabulary/)]. To describe the file format, physical medium, or dimensions of the resource, use the property Format."@en ;
+ dcterms:issued "2008-01-14"^^xsd:date ;
+ rdfs:comment "The nature or genre of the resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Type"@en ;
+ rdfs:subPropertyOf dc:type .
+
+dcterms:valid
+ a rdf:Property ;
+ dcterms:description "Recommended practice is to describe the date, date/time, or period of time as recommended for the property Date, of which this is a subproperty."@en ;
+ dcterms:issued "2000-07-11"^^xsd:date ;
+ rdfs:comment "Date (often a range) of validity of a resource."@en ;
+ rdfs:isDefinedBy dcterms: ;
+ rdfs:label "Date Valid"@en ;
+ rdfs:range rdfs:Literal ;
+ rdfs:subPropertyOf dc:date ,
+ dcterms:date .
diff --git a/schemas/doap.ttl b/schemas/doap.ttl
new file mode 100644
index 00000000..f0f42620
--- /dev/null
+++ b/schemas/doap.ttl
@@ -0,0 +1,868 @@
+@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 vs: <http://www.w3.org/2003/06/sw-vocab-status/ns#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix dc: <http://purl.org/dc/elements/1.1/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+
+doap:
+ a owl:Ontology ;
+ dc:creator "Edd Wilder-James" ;
+ dc:description "Das Vokabular \"Description of a Project (DOAP)\", beschrieben durch W3C RDF Schema and the Web Ontology Language."@de ,
+ """El vocabulario Description of a Project (DOAP, Descripción de un Proyecto), descrito usando RDF Schema de W3C
+ y Web Ontology Language."""@es ,
+ """Le vocabulaire Description Of A Project (DOAP, Description D'Un Projet),
+ décrit en utilisant RDF Schema du W3C et OWL."""@fr ,
+ "Slovník Description of a Project (DOAP, Popis projektu), popsaný použitím W3C RDF Schema a Web Ontology Language."@cs ,
+ "The Description of a Project (DOAP) vocabulary, described using W3C RDF Schema and the Web Ontology Language." ,
+ "Vocabulário de descrição de um Projeto (DOAP - Description of a Project), descrito no esquema (schema) W3C RDF e na Web Ontology Language."@pt ;
+ dc:format "application/rdf+xml" ;
+ dc:rights "Copyright © The DOAP Authors" ;
+ dc:title "Description of a Project (DOAP) vocabulary" ;
+ foaf:maker [
+ a foaf:Person ;
+ foaf:mbox <mailto:edd@usefulinc.com> ;
+ foaf:name "Edd Wilder-James"
+ ] .
+
+doap:ArchRepository
+ a rdfs:Class ,
+ owl:Class ;
+ rdfs:comment "Dépôt GNU Arch du code source."@fr ,
+ "GNU Arch Quellcode-Versionierungssystem."@de ,
+ "GNU Arch source code repository."@en ,
+ "Repositorio GNU Arch del código fuente."@es ,
+ "Repositório GNU Arch do código fonte."@pt ,
+ "Úložiště zdrojových kódů GNU Arch."@cs ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Dépôt GNU Arch"@fr ,
+ "GNU Arch repository"@de ,
+ "GNU Arch repository"@en ,
+ "Repositorio GNU Arch"@es ,
+ "Repositório GNU Arch"@pt ,
+ "Úložiště GNU Arch"@cs ;
+ rdfs:subClassOf doap:Repository .
+
+doap:BKRepository
+ a rdfs:Class ,
+ owl:Class ;
+ rdfs:comment "BitKeeper Quellcode-Versionierungssystem."@de ,
+ "BitKeeper source code repository."@en ,
+ "Dépôt BitKeeper du code source."@fr ,
+ "Repositorio BitKeeper del código fuente."@es ,
+ "Repositório BitKeeper do código fonte."@pt ,
+ "Úložiště zdrojových kódů BitKeeper."@cs ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "BitKeeper Repository"@de ,
+ "BitKeeper Repository"@en ,
+ "Dépôt BitKeeper"@fr ,
+ "Repositorio BitKeeper"@es ,
+ "Repositório Bitkeeper"@pt ,
+ "Úložiště BitKeeper"@cs ;
+ rdfs:subClassOf doap:Repository .
+
+doap:BazaarBranch
+ a rdfs:Class ;
+ rdfs:comment "Bazaar source code branch."@en ,
+ "Código fonte da ramificação Bazaar."@pt ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Bazaar Branch"@en ,
+ "Ramificação Bazaar"@pt ;
+ rdfs:subClassOf doap:Repository .
+
+doap:CVSRepository
+ a rdfs:Class ,
+ owl:Class ;
+ rdfs:comment "CVS Quellcode-Versionierungssystem."@de ,
+ "CVS source code repository."@en ,
+ "Dépôt CVS du code source."@fr ,
+ "Repositorio CVS del código fuente."@es ,
+ "Repositório CVS do código fonte."@pt ,
+ "Úložiště zdrojových kódů CVS."@cs ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "CVS Repository"@de ,
+ "CVS Repository"@en ,
+ "Dépôt CVS"@fr ,
+ "Repositorio CVS"@es ,
+ "Repositório CVS"@pt ,
+ "Úložiště CVS"@cs ;
+ rdfs:subClassOf doap:Repository .
+
+doap:DarcsRepository
+ a rdfs:Class ;
+ rdfs:comment "Dépôt darcs du code source."@fr ,
+ "Repositorio darcs del código fuente."@es ,
+ "Repositório darcs do código fonte."@pt ,
+ "darcs source code repository."@en ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Dépôt darcs"@fr ,
+ "Repositorio darcs"@es ,
+ "Repositório darcs"@pt ,
+ "darcs Repository"@en ;
+ rdfs:subClassOf doap:Repository .
+
+doap:GitBranch
+ a rdfs:Class ;
+ rdfs:comment "Código fonte da ramificação Git."@pt ,
+ "Git source code branch."@en ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Git Branch"@en ,
+ "Ramificação Git"@pt ;
+ rdfs:subClassOf doap:Repository .
+
+doap:GitRepository
+ a rdfs:Class ;
+ rdfs:comment "Dépôt Git du code source."@fr ,
+ "Git Quellcode-Versionierungssystem."@de ,
+ "Git source code repository."@en ,
+ "Repositorio Git del código fuente."@es ,
+ "Repositório Git do código fonte."@pt ,
+ "Úložiště zdrojových kódů Git."@cs ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Dépôt Git"@fr ,
+ "Git Repository"@de ,
+ "Git Repository"@en ,
+ "Repositorio Git"@es ,
+ "Repositório Git"@pt ,
+ "Úložiště Git"@cs ;
+ rdfs:subClassOf doap:Repository .
+
+doap:HgRepository
+ a rdfs:Class ;
+ rdfs:comment "Mercurial source code repository."@en ,
+ "Repositório Mercurial do código fonte."@pt ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Mercurial Repository"@en ,
+ "Repositório Mercurial"@pt ;
+ rdfs:subClassOf doap:Repository .
+
+doap:Project
+ a rdfs:Class ;
+ rdfs:comment "A project."@en ,
+ "Ein Projekt."@de ,
+ "Projekt."@cs ,
+ "Projeto."@pt ,
+ "Un projet."@fr ,
+ "Un proyecto."@es ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Project"@en ,
+ "Projekt"@cs ,
+ "Projekt"@de ,
+ "Projet"@fr ,
+ "Projeto"@pt ,
+ "Proyecto"@es ;
+ rdfs:subClassOf foaf:Project ,
+ <http://xmlns.com/wordnet/1.6/Project> .
+
+doap:Repository
+ a rdfs:Class ;
+ rdfs:comment "Dépôt du code source."@fr ,
+ "Quellcode-Versionierungssystem."@de ,
+ "Repositorio del código fuente."@es ,
+ "Repositório do código fonte."@pt ,
+ "Source code repository."@en ,
+ "Úložiště zdrojových kódů."@cs ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Dépôt"@fr ,
+ "Repositorio"@es ,
+ "Repository"@de ,
+ "Repository"@en ,
+ "Repositório"@pt ,
+ "Úložiště"@cs .
+
+doap:SVNRepository
+ a rdfs:Class ;
+ rdfs:comment "Dépôt Subversion du code source."@fr ,
+ "Repositorio Subversion del código fuente."@es ,
+ "Repositório Subversion do código fonte."@pt ,
+ "Subversion Quellcode-Versionierungssystem."@de ,
+ "Subversion source code repository."@en ,
+ "Úložiště zdrojových kódů Subversion."@cs ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Dépôt Subversion"@fr ,
+ "Repositorio Subversion"@es ,
+ "Repositório Subversion"@pt ,
+ "Subversion Repository"@de ,
+ "Subversion Repository"@en ,
+ "Úložiště Subversion"@cs ;
+ rdfs:subClassOf doap:Repository .
+
+doap:Specification
+ a rdfs:Class ;
+ rdfs:comment "A especificação de aspetos, técnicas ou outros do sistema."@pt ,
+ "A specification of a system's aspects, technical or otherwise."@en ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Especificação"@pt ,
+ "Specification"@en ;
+ rdfs:subClassOf rdfs:Resource .
+
+doap:Version
+ a rdfs:Class ;
+ rdfs:comment "Détails sur une version d'une release d'un projet."@fr ,
+ "Informace o uvolněné verzi projektu."@cs ,
+ "Información sobre la versión de un release del proyecto."@es ,
+ "Informação sobre a versão do projeto lançado."@pt ,
+ "Version information of a project release."@en ,
+ "Versionsinformation eines Projekt Releases."@de ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Version"@de ,
+ "Version"@en ,
+ "Version"@fr ,
+ "Versión"@es ,
+ "Versão"@pt ,
+ "Verze"@cs .
+
+doap:anon-root
+ a rdf:Property ;
+ rdfs:comment "Dépôt pour accès anonyme."@fr ,
+ "Repositorio para acceso anónimo."@es ,
+ "Repository for anonymous access."@en ,
+ "Repository für anonymen Zugriff"@de ,
+ "Repositório para acesso anónimo."@pt ,
+ "Úložiště pro anonymní přístup."@cs ;
+ rdfs:domain doap:Repository ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Anonymes Root"@de ,
+ "anonymní kořen"@cs ,
+ "anonymous root"@en ,
+ "racine anonyme"@fr ,
+ "raíz anónima"@es ,
+ "raíz anónima"@pt ;
+ rdfs:range rdfs:Literal .
+
+doap:audience
+ a rdf:Property ;
+ rdfs:comment "Description of target user base"@en ,
+ "Descrição do utilizador base alvo"@pt ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "audience"@en ,
+ "audiência"@pt ;
+ rdfs:range rdfs:Literal .
+
+doap:blog
+ a rdf:Property ;
+ rdfs:comment "URI de um blog relacionado com um projeto"@pt ,
+ "URI of a blog related to a project"@en ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "blog"@en ,
+ "blog"@pt ;
+ rdfs:range <http://rdfs.org/sioc/types#Weblog> ,
+ rdfs:Resource .
+
+doap:browse
+ a rdf:Property ;
+ rdfs:comment "Interface web au dépôt."@fr ,
+ "Interface web del repositorio."@es ,
+ "Interface web do repositório."@pt ,
+ "Web browser interface to repository."@en ,
+ "Web-Browser Interface für das Repository."@de ,
+ "Webové rozhraní pro prohlížení úložiště."@cs ;
+ rdfs:domain doap:Repository ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "browse"@de ,
+ "browse"@en ,
+ "navegar"@es ,
+ "navegar"@pt ,
+ "prohlížeč"@cs ,
+ "visualiser"@fr .
+
+doap:bug-database
+ a rdf:Property ;
+ rdfs:comment "Bug tracker for a project."@en ,
+ "Bug tracker para um projeto."@pt ,
+ "Bug tracker para un proyecto."@es ,
+ "Fehlerdatenbank eines Projektes."@de ,
+ "Správa chyb projektu."@cs ,
+ "Suivi des bugs pour un projet."@fr ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Fehlerdatenbank"@de ,
+ "base de dados de bugs"@pt ,
+ "base de datos de bugs"@es ,
+ "bug database"@en ,
+ "databáze chyb"@cs ,
+ "suivi des bugs"@fr .
+
+doap:category
+ a rdf:Property ;
+ rdfs:comment "A category of project."@en ,
+ "Eine Kategorie eines Projektes."@de ,
+ "Kategorie projektu."@cs ,
+ "Uma categoría de projeto."@pt ,
+ "Una categoría de proyecto."@es ,
+ "Une catégorie de projet."@fr ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Kategorie"@de ,
+ "categoria"@pt ,
+ "category"@en ,
+ "categoría"@es ,
+ "catégorie"@fr ,
+ "kategorie"@cs .
+
+doap:created
+ a rdf:Property ;
+ rdfs:comment "Data em que algo foi criado, no formato AAAA-MM-DD. e.g. 2004-04-05"@pt ,
+ "Date when something was created, in YYYY-MM-DD form. e.g. 2004-04-05"@en ,
+ "Date à laquelle a été créé quelque chose, au format AAAA-MM-JJ (par ex. 2004-04-05)"@fr ,
+ "Datum, kdy bylo něco vytvořeno ve formátu RRRR-MM-DD, např. 2004-04-05"@cs ,
+ "Erstellungsdatum von Irgendwas, angegeben im YYYY-MM-DD Format, z.B. 2004-04-05."@de ,
+ "Fecha en la que algo fue creado, en formato AAAA-MM-DD. e.g. 2004-04-05"@es ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "creado"@es ,
+ "created"@en ,
+ "criado"@pt ,
+ "créé"@fr ,
+ "erstellt"@de ,
+ "vytvořeno"@cs ;
+ rdfs:range rdfs:Literal .
+
+doap:description
+ a rdf:Property ;
+ rdfs:comment "Beschreibung eines Projekts als einfacher Text mit der Länge von 2 bis 4 Sätzen."@de ,
+ "Descripción en texto plano de un proyecto, de 2 a 4 enunciados de longitud."@es ,
+ "Descrição de um projeto em texto apenas, com 2 a 4 frases de comprimento."@pt ,
+ "Plain text description of a project, of 2-4 sentences in length."@en ,
+ "Texte descriptif d'un projet, long de 2 à 4 phrases."@fr ,
+ "Čistě textový, 2 až 4 věty dlouhý popis projektu."@cs ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Beschreibung"@de ,
+ "descripción"@es ,
+ "description"@en ,
+ "description"@fr ,
+ "descrição"@pt ,
+ "popis"@cs ;
+ rdfs:range rdfs:Literal .
+
+doap:developer
+ a rdf:Property ;
+ rdfs:comment "Desarrollador de software para el proyecto."@es ,
+ "Developer of software for the project."@en ,
+ "Développeur pour le projet."@fr ,
+ "Programador de software para o projeto."@pt ,
+ "Software-Entwickler für eine Projekt."@de ,
+ "Vývojář softwaru projektu."@cs ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Entwickler"@de ,
+ "desarrollador"@es ,
+ "developer"@en ,
+ "développeur"@fr ,
+ "programador"@pt ,
+ "vývojář"@cs ;
+ rdfs:range foaf:Person .
+
+doap:developer-forum
+ a rdf:Property ;
+ rdfs:comment "A forum or community for developers of this project."@en ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "developer forum"@en ;
+ rdfs:range <http://rdfs.org/sioc/ns#Container> .
+
+doap:documenter
+ a rdf:Property ;
+ rdfs:comment "Collaborateur à la documentation du projet."@fr ,
+ "Contribuidor para a documentação do projeto."@pt ,
+ "Contributor of documentation to the project."@en ,
+ "Mitarbeiter an der Dokumentation eines Projektes."@de ,
+ "Proveedor de documentación para el proyecto."@es ,
+ "Spoluautor dokumentace projektu."@cs ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Dokumentator"@de ,
+ "documentador"@pt ,
+ "documenter"@en ,
+ "dokumentarista"@cs ,
+ "escritor de ayuda"@es ,
+ "rédacteur de l'aide"@fr ;
+ rdfs:range foaf:Person .
+
+doap:download-mirror
+ a rdf:Property ;
+ rdfs:comment "Miroir de la page de téléchargement du programme."@fr ,
+ "Mirror da página web para fazer download."@pt ,
+ "Mirror de la página web de descarga."@es ,
+ "Mirror of software download web page."@en ,
+ "Spiegel der Seite von die Projekt-Software heruntergeladen werden kann."@de ,
+ "Zrcadlo stránky pro stažení softwaru."@cs ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Spiegel der Seite zum Herunterladen"@de ,
+ "download mirror"@en ,
+ "miroir pour le téléchargement"@fr ,
+ "mirror de descarga"@es ,
+ "mirror para download"@pt ,
+ "zrcadlo stránky pro stažení"@cs .
+
+doap:download-page
+ a rdf:Property ;
+ rdfs:comment "Page web à partir de laquelle on peut télécharger le programme."@fr ,
+ "Página web da qual o projeto de software pode ser descarregado."@pt ,
+ "Página web de la cuál se puede bajar el software."@es ,
+ "Web page from which the project software can be downloaded."@en ,
+ "Web-Seite von der die Projekt-Software heruntergeladen werden kann."@de ,
+ "Webová stránka, na které lze stáhnout projektový software."@cs ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Seite zum Herunterladen"@de ,
+ "download page"@en ,
+ "page de téléchargement"@fr ,
+ "página de descarga"@es ,
+ "página para download"@pt ,
+ "stránka pro stažení"@cs .
+
+doap:file-release
+ a rdf:Property ;
+ rdfs:comment "URI adresa stažení asociované s revizí."@cs ,
+ "URI of download associated with this release."@en ,
+ "URI para download associado com a publicação."@pt ;
+ rdfs:domain doap:Version ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "file-release"@en ,
+ "publicação do ficheiro"@pt ,
+ "soubor revize"@cs .
+
+doap:helper
+ a rdf:Property ;
+ rdfs:comment "Ajudante ou colaborador do projeto."@pt ,
+ "Colaborador del proyecto."@es ,
+ "Collaborateur au projet."@fr ,
+ "Project contributor."@en ,
+ "Projekt-Mitarbeiter."@de ,
+ "Spoluautor projektu."@cs ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Helfer"@de ,
+ "colaborador"@es ,
+ "colaborador"@pt ,
+ "collaborateur"@fr ,
+ "helper"@en ,
+ "spoluautor"@cs ;
+ rdfs:range foaf:Person .
+
+doap:homepage
+ a rdf:Property ,
+ owl:InverseFunctionalProperty ;
+ rdfs:comment """El URL de la página de un proyecto,
+ asociada con exactamente un proyecto."""@es ,
+ """L'URL de la page web d'un projet,
+ associée avec un unique projet."""@fr ,
+ """O URL da página de um projeto,
+ asociada com exactamente um projeto."""@pt ,
+ "URL adresa domovské stránky projektu asociované s právě jedním projektem."@cs ,
+ """URL der Projekt-Homepage,
+ verbunden mit genau einem Projekt."""@de ,
+ """URL of a project's homepage,
+ associated with exactly one project."""@en ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Homepage"@de ,
+ "domovská stránka"@cs ,
+ "homepage"@en ,
+ "page web"@fr ,
+ "página web"@es ,
+ "página web"@pt ;
+ rdfs:subPropertyOf foaf:homepage .
+
+doap:implements
+ a rdf:Property ;
+ rdfs:comment "A specification that a project implements. Could be a standard, API or legally defined level of conformance."@en ,
+ "Uma especificação que um projeto implementa. Pode ser uma padrão, API ou um nível de conformidade definida legalmente."@pt ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Especificações para implementação"@pt ,
+ "Implements specification"@en ;
+ rdfs:range doap:Specification .
+
+doap:language
+ a rdf:Property ;
+ rdfs:comment "BCP47 language code a project has been translated into"@en ,
+ "Código de idioma BCP47 do projeto para o qual foi traduzido"@pt ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "idioma"@pt ,
+ "language"@en ;
+ rdfs:range rdfs:Literal .
+
+doap:license
+ a rdf:Property ;
+ rdfs:comment "Die URI einer RDF-Beschreibung einer Lizenz unter der die Software herausgegeben wird. z.B. eine SPDX Referenz"@de ,
+ "El URI de una descripción RDF de la licencia bajo la cuál se distribuye el software."@es ,
+ "L'URI d'une description RDF de la licence sous laquelle le programme est distribué."@fr ,
+ "O URI de uma descrição RDF da licença do software sob a qual é distribuída. Ex.: referência SPDX"@pt ,
+ "The URI of an RDF description of the license the software is distributed under. E.g. a SPDX reference"@en ,
+ "URI adresa RDF popisu licence, pod kterou je software distribuován."@cs ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Lizenz"@de ,
+ "licence"@cs ,
+ "licence"@fr ,
+ "licencia"@es ,
+ "license"@en ,
+ "licença"@pt .
+
+doap:location
+ a rdf:Property ;
+ rdfs:comment "Emplacement d'un dépôt."@fr ,
+ "Localização de um repositório."@pt ,
+ "Location of a repository."@en ,
+ "Lokation eines Repositorys."@de ,
+ "Umístění úložiště."@cs ,
+ "lugar de un repositorio."@es ;
+ rdfs:domain doap:Repository ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Repository Lokation"@de ,
+ "emplacement du dépôt"@fr ,
+ "localização do respositório"@pt ,
+ "lugar del respositorio"@es ,
+ "repository location"@en ,
+ "umístění úložiště"@cs .
+
+doap:mailing-list
+ a rdf:Property ;
+ rdfs:comment "Domovská stránka nebo e–mailová adresa e–mailové diskuse."@cs ,
+ "Homepage der Mailing Liste oder E-Mail Adresse."@de ,
+ "Mailing list home page or email address."@en ,
+ "Page web de la liste de diffusion, ou adresse de courriel."@fr ,
+ "Página web da lista de distribuição de e-mail ou dos endereços."@pt ,
+ "Página web de la lista de correo o dirección de correo."@es ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Mailing Liste"@de ,
+ "e–mailová diskuse"@cs ,
+ "lista de correo"@es ,
+ "lista de distribuição de e-mail"@pt ,
+ "liste de diffusion"@fr ,
+ "mailing list"@en ;
+ rdfs:range <http://rdfs.org/sioc/types#MailingList> .
+
+doap:maintainer
+ a rdf:Property ;
+ rdfs:comment "Desarrollador principal de un proyecto, un líder de proyecto."@es ,
+ "Développeur principal d'un projet, un meneur du projet."@fr ,
+ "Hauptentwickler eines Projektes, der Projektleiter"@de ,
+ "Maintainer of a project, a project leader."@en ,
+ "Programador principal de um projeto, um líder de projeto."@pt ,
+ "Správce projektu, vedoucí projektu."@cs ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Projektverantwortlicher"@de ,
+ "desarrollador principal"@es ,
+ "développeur principal"@fr ,
+ "maintainer"@en ,
+ "programador principal"@pt ,
+ "správce"@cs ;
+ rdfs:range foaf:Person .
+
+doap:module
+ a rdf:Property ;
+ rdfs:comment "Jméno modulu v CVS, BitKeeper nebo Arch úložišti."@cs ,
+ "Modul-Name eines Subversion, CVS, BitKeeper oder Arch Repositorys."@de ,
+ "Module name of a Subversion, CVS, BitKeeper or Arch repository."@en ,
+ "Nom du module d'un dépôt Subversion, CVS, BitKeeper ou Arch."@fr ,
+ "Nombre del módulo de un repositorio Subversion, CVS, BitKeeper o Arch."@es ,
+ "Nome do módulo de um repositório Subversion, CVS, BitKeeper ou Arch."@pt ;
+ rdfs:domain [
+ a owl:Class ;
+ owl:unionOf (
+ doap:CVSRepository
+ doap:ArchRepository
+ doap:BKRepository
+ )
+ ] ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Modul"@de ,
+ "modul"@cs ,
+ "module"@en ,
+ "module"@fr ,
+ "módulo"@es ,
+ "módulo"@pt .
+
+doap:name
+ a rdf:Property ;
+ rdfs:comment "A name of something."@en ,
+ "Der Name von Irgendwas"@de ,
+ "El nombre de algo."@es ,
+ "Jméno něčeho."@cs ,
+ "Le nom de quelque chose."@fr ,
+ "O nome de alguma coisa."@pt ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Name"@de ,
+ "jméno"@cs ,
+ "name"@en ,
+ "nom"@fr ,
+ "nombre"@es ,
+ "nome"@pt ;
+ rdfs:range rdfs:Literal ;
+ rdfs:subPropertyOf rdfs:label .
+
+doap:old-homepage
+ a rdf:Property ,
+ owl:InverseFunctionalProperty ;
+ rdfs:comment """El URL de la antigua página de un proyecto,
+ asociada con exactamente un proyecto."""@es ,
+ """L'URL d'une ancienne page web d'un
+ projet, associée avec un unique projet."""@fr ,
+ """O URL antigo da página de um projeto,
+ associada com exactamente um projeto."""@pt ,
+ "URL adresa předešlé domovské stránky projektu asociované s právě jedním projektem."@cs ,
+ """URL der letzten Projekt-Homepage,
+ verbunden mit genau einem Projekt."""@de ,
+ """URL of a project's past homepage,
+ associated with exactly one project."""@en ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Alte Homepage"@de ,
+ "ancienne page web"@fr ,
+ "old homepage"@en ,
+ "página web antiga"@pt ,
+ "página web antigua"@es ,
+ "stará domovská stránka"@cs ;
+ rdfs:subPropertyOf foaf:homepage .
+
+doap:os
+ a rdf:Property ;
+ rdfs:comment "Betriebssystem auf dem das Projekt eingesetzt werden kann. Diese Eigenschaft kann ausgelassen werden, wenn das Projekt nicht BS-spezifisch ist."@de ,
+ "Operating system that a project is limited to. Omit this property if the project is not OS-specific."@en ,
+ "Operační systém, na jehož použití je projekt limitován. Vynechejte tuto vlastnost, pokud je projekt nezávislý na operačním systému."@cs ,
+ "Sistema operativo a que o projeto está limitado. Omita esta propriedade se o projeto não é condicionado pelo SO usado."@en ,
+ """Sistema opertivo al cuál está limitado el proyecto. Omita esta propiedad si el proyecto no es específico
+ de un sistema opertaivo en particular."""@es ,
+ """Système d'exploitation auquel est limité le projet. Omettez cette propriété si le
+ projet n'est pas limité à un système d'exploitation."""@fr ;
+ rdfs:domain doap:Project ,
+ doap:Version ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Betriebssystem"@de ,
+ "operating system"@en ,
+ "operační systém"@cs ,
+ "sistema operativo"@es ,
+ "sistema operativo"@pt ,
+ "système d'exploitation"@fr ;
+ rdfs:range rdfs:Literal .
+
+doap:platform
+ a rdf:Property ;
+ rdfs:comment "Indicador da plataforma do software (não específico a nenhum SO), ex.: Java, Firefox, ECMA CLR"@pt ,
+ "Indicator of software platform (non-OS specific), e.g. Java, Firefox, ECMA CLR"@en ;
+ rdfs:domain doap:Project ,
+ doap:Version ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "plataforma"@pt ,
+ "platform"@en ;
+ rdfs:range rdfs:Literal .
+
+doap:programming-language
+ a rdf:Property ;
+ rdfs:comment """Langage de programmation avec lequel un projet est implémenté,
+ ou avec lequel il est prévu de l'utiliser."""@fr ,
+ "Lenguaje de programación en el que un proyecto es implementado o con el cuál pretende usarse."@es ,
+ "Linguagem de programação que o projeto usa ou é para ser utilizada."@pt ,
+ "Programmiersprache in der ein Projekt implementiert ist oder intendiert wird zu benutzen."@de ,
+ "Programming language a project is implemented in or intended for use with."@en ,
+ "Programovací jazyk, ve kterém je projekt implementován nebo pro který je zamýšlen k použití."@cs ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Programmiersprache"@de ,
+ "langage de programmation"@fr ,
+ "lenguaje de programación"@es ,
+ "linguagem de programação"@pt ,
+ "programming language"@en ,
+ "programovací jazyk"@cs ;
+ rdfs:range rdfs:Literal .
+
+doap:release
+ a rdf:Property ;
+ rdfs:comment "A project release."@en ,
+ "A publicação de um projeto."@pt ,
+ "Ein Release (Version) eines Projekts."@de ,
+ "Relase (verze) projektu."@cs ,
+ "Un release (versión) de un proyecto."@es ,
+ "Une release (révision) d'un projet."@fr ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Release"@de ,
+ "publicação"@pt ,
+ "release"@cs ,
+ "release"@en ,
+ "release"@es ,
+ "release"@fr ;
+ rdfs:range doap:Version .
+
+doap:repository
+ a rdf:Property ;
+ rdfs:comment "Dépôt du code source."@fr ,
+ "Quellcode-Versionierungssystem."@de ,
+ "Repositorio del código fuente."@es ,
+ "Repositório do código fonte."@pt ,
+ "Source code repository."@en ,
+ "Úložiště zdrojových kódů."@cs ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Repository"@de ,
+ "dépôt"@fr ,
+ "repositorio"@es ,
+ "repository"@en ,
+ "repositório"@pt ,
+ "úložiště"@cs ;
+ rdfs:range doap:Repository ;
+ owl:inverseOf doap:repositoryOf .
+
+doap:repositoryOf
+ a rdf:Property ;
+ rdfs:comment "The project that uses a repository."@en ;
+ rdfs:domain doap:Repository ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "repository of"@en ;
+ rdfs:range doap:Project ;
+ owl:inverseOf doap:repository .
+
+doap:revision
+ a rdf:Property ;
+ rdfs:comment "Identifiant de révision d'une release du programme."@fr ,
+ "Identificador do lançamento da revisão do software."@pt ,
+ "Identifikátor zpřístupněné revize softwaru."@cs ,
+ "Indentificador de la versión de un release de software."@es ,
+ "Revision identifier of a software release."@en ,
+ "Versionsidentifikator eines Software-Releases."@de ;
+ rdfs:domain doap:Version ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Version"@de ,
+ "revision"@en ,
+ "revisão"@pt ,
+ "révision"@fr ,
+ "versión"@es ,
+ "verze"@cs ;
+ rdfs:range rdfs:Literal .
+
+doap:screenshots
+ a rdf:Property ;
+ rdfs:comment "Page web avec des captures d'écran du projet."@fr ,
+ "Página web com as capturas de ecrãn do projeto."@pt ,
+ "Página web con capturas de pantalla del proyecto."@es ,
+ "Web page with screenshots of project."@en ,
+ "Web-Seite mit Screenshots eines Projektes."@de ,
+ "Webová stránka projektu se snímky obrazovky."@cs ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Screenshots"@de ,
+ "capturas de ecrãs"@pt ,
+ "capturas de pantalla"@es ,
+ "captures d'écran"@fr ,
+ "screenshots"@en ,
+ "snímek obrazovky"@cs .
+
+doap:security-contact
+ a rdf:Property ;
+ rdfs:comment """The Agent that should be contacted
+ if security issues are found with the project."""@en ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "security contact"@en ;
+ rdfs:range foaf:Agent .
+
+doap:security-policy
+ a rdf:Property ;
+ rdfs:comment "URL of the security policy of a project."@en ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "security policy"@en ;
+ rdfs:subPropertyOf foaf:page .
+
+doap:service-endpoint
+ a rdf:Property ;
+ rdfs:comment "The URI of a web service endpoint where software as a service may be accessed"@en ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "service endpoint"@en ;
+ rdfs:range rdfs:Resource .
+
+doap:shortdesc
+ a rdf:Property ;
+ rdfs:comment "Descripción corta (8 o 9 palabras) en texto plano de un proyecto."@es ,
+ "Descrição curta (com 8 ou 9 palavras) de um projeto em texto apenas."@pt ,
+ "Krátký (8 nebo 9 slov) čistě textový popis projektu."@cs ,
+ "Kurzbeschreibung (8 oder 9 Wörter) eines Projects als einfacher Text."@de ,
+ "Short (8 or 9 words) plain text description of a project."@en ,
+ "Texte descriptif concis (8 ou 9 mots) d'un projet."@fr ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Kurzbeschreibung"@de ,
+ "descripción corta"@es ,
+ "description courte"@fr ,
+ "descrição curta"@pt ,
+ "krátký popis"@cs ,
+ "short description"@en ;
+ rdfs:range rdfs:Literal .
+
+doap:support-forum
+ a rdf:Property ;
+ rdfs:comment "A forum or community that supports this project."@en ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "supporting forum"@en ;
+ rdfs:range <http://rdfs.org/sioc/ns#Container> .
+
+doap:tester
+ a rdf:Property ;
+ rdfs:comment "A tester or other quality control contributor."@en ,
+ "Ein Tester oder anderer Mitarbeiter der Qualitätskontrolle."@de ,
+ "Tester nebo jiný spoluautor kontrolující kvalitu."@cs ,
+ "Um controlador ou outro contribuidor para o controlo de qualidade."@pt ,
+ "Un tester u otro proveedor de control de calidad."@es ,
+ "Un testeur ou un collaborateur au contrôle qualité."@fr ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Tester"@de ,
+ "controlador"@pt ,
+ "tester"@cs ,
+ "tester"@en ,
+ "tester"@es ,
+ "testeur"@fr ;
+ rdfs:range foaf:Person .
+
+doap:translator
+ a rdf:Property ;
+ rdfs:comment "Collaborateur à la traduction du projet."@fr ,
+ "Contribuidor das traduções para o projeto."@pt ,
+ "Contributor of translations to the project."@en ,
+ "Mitarbeiter an den Übersetzungen eines Projektes."@de ,
+ "Proveedor de traducciones al proyecto."@es ,
+ "Spoluautor překladu projektu."@cs ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "překladatel"@cs ,
+ "traducteur"@fr ,
+ "traductor"@es ,
+ "tradutor"@pt ,
+ "translator"@en ,
+ "Übersetzer"@de ;
+ rdfs:range foaf:Person .
+
+doap:vendor
+ a rdf:Property ;
+ rdfs:comment "Vendor organization: commercial, free or otherwise"@en ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "vendor"@en ;
+ rdfs:range foaf:Organization .
+
+doap:wiki
+ a rdf:Property ;
+ rdfs:comment "L'URL du Wiki pour la discussion collaborative sur le projet."@fr ,
+ "URL adresa wiki projektu pro společné diskuse."@cs ,
+ "URL da Wiki para discussão em grupo do projeto."@pt ,
+ "URL del Wiki para discusión colaborativa del proyecto."@es ,
+ "URL of Wiki for collaborative discussion of project."@en ,
+ "Wiki-URL für die kollaborative Dikussion eines Projektes."@de ;
+ rdfs:domain doap:Project ;
+ rdfs:isDefinedBy doap: ;
+ rdfs:label "Wiki"@de ,
+ "wiki"@cs ,
+ "wiki"@en ,
+ "wiki"@es ,
+ "wiki"@fr ,
+ "wiki"@pt ;
+ rdfs:range <http://rdfs.org/sioc/types#Wiki> .
diff --git a/schemas/foaf.ttl b/schemas/foaf.ttl
new file mode 100644
index 00000000..44100ac0
--- /dev/null
+++ b/schemas/foaf.ttl
@@ -0,0 +1,810 @@
+@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 vs: <http://www.w3.org/2003/06/sw-vocab-status/ns#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix wot: <http://xmlns.com/wot/0.1/> .
+@prefix dc: <http://purl.org/dc/elements/1.1/> .
+
+dc:date
+ a owl:AnnotationProperty .
+
+dc:description
+ a owl:AnnotationProperty .
+
+dc:title
+ a owl:AnnotationProperty .
+
+rdfs:Class
+ a owl:Class .
+
+owl:Thing
+ rdfs:label "Thing" .
+
+<http://www.w3.org/2003/01/geo/wgs84_pos#SpatialThing>
+ a owl:Class ;
+ rdfs:label "Spatial Thing" .
+
+vs:term_status
+ a owl:AnnotationProperty .
+
+<http://www.w3.org/2004/02/skos/core#Concept>
+ rdfs:label "Concept" .
+
+foaf:
+ a owl:Ontology ;
+ dc:description "The Friend of a Friend (FOAF) RDF vocabulary, described using W3C RDF Schema and the Web Ontology Language." ;
+ dc:title "Friend of a Friend (FOAF) vocabulary" .
+
+foaf:Agent
+ a rdfs:Class ,
+ owl:Class ;
+ rdfs:comment "An agent (eg. person, group, software or physical artifact)." ;
+ rdfs:label "Agent" ;
+ owl:equivalentClass <http://purl.org/dc/terms/Agent> ;
+ vs:term_status "stable" .
+
+foaf:Document
+ a rdfs:Class ,
+ owl:Class ;
+ rdfs:comment "A document." ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "Document" ;
+ owl:disjointWith foaf:Organization ,
+ foaf:Project ;
+ owl:equivalentClass <http://schema.org/CreativeWork> ;
+ vs:term_status "stable" .
+
+foaf:Group
+ a rdfs:Class ,
+ owl:Class ;
+ rdfs:comment "A class of Agents." ;
+ rdfs:label "Group" ;
+ rdfs:subClassOf foaf:Agent ;
+ vs:term_status "stable" .
+
+foaf:Image
+ a rdfs:Class ,
+ owl:Class ;
+ rdfs:comment "An image." ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "Image" ;
+ rdfs:subClassOf foaf:Document ;
+ owl:equivalentClass <http://schema.org/ImageObject> ;
+ vs:term_status "stable" .
+
+foaf:LabelProperty
+ a rdfs:Class ,
+ owl:Class ;
+ rdfs:comment "A foaf:LabelProperty is any RDF property with texual values that serve as labels." ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "Label Property" ;
+ vs:term_status "unstable" .
+
+foaf:OnlineAccount
+ a rdfs:Class ,
+ owl:Class ;
+ rdfs:comment "An online account." ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "Online Account" ;
+ rdfs:subClassOf owl:Thing ;
+ vs:term_status "testing" .
+
+foaf:OnlineChatAccount
+ a rdfs:Class ,
+ owl:Class ;
+ rdfs:comment "An online chat account." ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "Online Chat Account" ;
+ rdfs:subClassOf foaf:OnlineAccount ;
+ vs:term_status "unstable" .
+
+foaf:OnlineEcommerceAccount
+ a rdfs:Class ,
+ owl:Class ;
+ rdfs:comment "An online e-commerce account." ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "Online E-commerce Account" ;
+ rdfs:subClassOf foaf:OnlineAccount ;
+ vs:term_status "unstable" .
+
+foaf:OnlineGamingAccount
+ a rdfs:Class ,
+ owl:Class ;
+ rdfs:comment "An online gaming account." ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "Online Gaming Account" ;
+ rdfs:subClassOf foaf:OnlineAccount ;
+ vs:term_status "unstable" .
+
+foaf:Organization
+ a rdfs:Class ,
+ owl:Class ;
+ rdfs:comment "An organization." ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "Organization" ;
+ rdfs:subClassOf foaf:Agent ;
+ owl:disjointWith foaf:Document ,
+ foaf:Person ;
+ vs:term_status "stable" .
+
+foaf:Person
+ a rdfs:Class ,
+ owl:Class ;
+ rdfs:comment "A person." ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "Person" ;
+ rdfs:subClassOf <http://www.w3.org/2003/01/geo/wgs84_pos#SpatialThing> ,
+ foaf:Agent ;
+ owl:disjointWith foaf:Organization ,
+ foaf:Project ;
+ owl:equivalentClass <http://schema.org/Person> ,
+ <http://www.w3.org/2000/10/swap/pim/contact#Person> ;
+ vs:term_status "stable" .
+
+foaf:PersonalProfileDocument
+ a rdfs:Class ,
+ owl:Class ;
+ rdfs:comment "A personal profile RDF document." ;
+ rdfs:label "PersonalProfileDocument" ;
+ rdfs:subClassOf foaf:Document ;
+ vs:term_status "testing" .
+
+foaf:Project
+ a rdfs:Class ,
+ owl:Class ;
+ rdfs:comment "A project (a collective endeavour of some kind)." ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "Project" ;
+ owl:disjointWith foaf:Document ,
+ foaf:Person ;
+ vs:term_status "testing" .
+
+foaf:account
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "Indicates an account held by this agent." ;
+ rdfs:domain foaf:Agent ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "account" ;
+ rdfs:range foaf:OnlineAccount ;
+ vs:term_status "testing" .
+
+foaf:accountName
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "Indicates the name (identifier) associated with this online account." ;
+ rdfs:domain foaf:OnlineAccount ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "account name" ;
+ rdfs:range rdfs:Literal ;
+ vs:term_status "testing" .
+
+foaf:accountServiceHomepage
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "Indicates a homepage of the service provide for this online account." ;
+ rdfs:domain foaf:OnlineAccount ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "account service homepage" ;
+ rdfs:range foaf:Document ;
+ vs:term_status "testing" .
+
+foaf:age
+ a rdf:Property ,
+ owl:DatatypeProperty ,
+ owl:FunctionalProperty ;
+ rdfs:comment "The age in years of some agent." ;
+ rdfs:domain foaf:Agent ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "age" ;
+ rdfs:range rdfs:Literal ;
+ vs:term_status "unstable" .
+
+foaf:aimChatID
+ a rdf:Property ,
+ owl:DatatypeProperty ,
+ owl:InverseFunctionalProperty ;
+ rdfs:comment "An AIM chat ID" ;
+ rdfs:domain foaf:Agent ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "AIM chat ID" ;
+ rdfs:range rdfs:Literal ;
+ rdfs:subPropertyOf foaf:nick ;
+ vs:term_status "testing" .
+
+foaf:based_near
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "A location that something is based near, for some broadly human notion of near." ;
+ rdfs:domain <http://www.w3.org/2003/01/geo/wgs84_pos#SpatialThing> ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "based near" ;
+ rdfs:range <http://www.w3.org/2003/01/geo/wgs84_pos#SpatialThing> ;
+ vs:term_status "testing" .
+
+foaf:birthday
+ a rdf:Property ,
+ owl:DatatypeProperty ,
+ owl:FunctionalProperty ;
+ rdfs:comment "The birthday of this Agent, represented in mm-dd string form, eg. '12-31'." ;
+ rdfs:domain foaf:Agent ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "birthday" ;
+ rdfs:range rdfs:Literal ;
+ vs:term_status "unstable" .
+
+foaf:currentProject
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "A current project this person works on." ;
+ rdfs:domain foaf:Person ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "current project" ;
+ rdfs:range owl:Thing ;
+ vs:term_status "testing" .
+
+foaf:depiction
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "A depiction of some thing." ;
+ rdfs:domain owl:Thing ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "depiction" ;
+ rdfs:range foaf:Image ;
+ owl:inverseOf foaf:depicts ;
+ vs:term_status "testing" .
+
+foaf:depicts
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "A thing depicted in this representation." ;
+ rdfs:domain foaf:Image ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "depicts" ;
+ rdfs:range owl:Thing ;
+ owl:inverseOf foaf:depiction ;
+ vs:term_status "testing" .
+
+foaf:dnaChecksum
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "A checksum for the DNA of some thing. Joke." ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "DNA checksum" ;
+ rdfs:range rdfs:Literal ;
+ vs:term_status "archaic" .
+
+foaf:familyName
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "The family name of some person." ;
+ rdfs:domain foaf:Person ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "familyName" ;
+ rdfs:range rdfs:Literal ;
+ vs:term_status "testing" .
+
+foaf:family_name
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "The family name of some person." ;
+ rdfs:domain foaf:Person ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "family_name" ;
+ rdfs:range rdfs:Literal ;
+ vs:term_status "archaic" .
+
+foaf:firstName
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "The first name of a person." ;
+ rdfs:domain foaf:Person ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "firstName" ;
+ rdfs:range rdfs:Literal ;
+ vs:term_status "testing" .
+
+foaf:focus
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "The underlying or 'focal' entity associated with some SKOS-described concept." ;
+ rdfs:domain <http://www.w3.org/2004/02/skos/core#Concept> ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "focus" ;
+ rdfs:range owl:Thing ;
+ vs:term_status "testing" .
+
+foaf:fundedBy
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "An organization funding a project or person." ;
+ rdfs:domain owl:Thing ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "funded by" ;
+ rdfs:range owl:Thing ;
+ vs:term_status "archaic" .
+
+foaf:geekcode
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "A textual geekcode for this person, see http://www.geekcode.com/geek.html" ;
+ rdfs:domain foaf:Person ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "geekcode" ;
+ rdfs:range rdfs:Literal ;
+ vs:term_status "archaic" .
+
+foaf:gender
+ a rdf:Property ,
+ owl:DatatypeProperty ,
+ owl:FunctionalProperty ;
+ rdfs:comment "The gender of this Agent (typically but not necessarily 'male' or 'female')." ;
+ rdfs:domain foaf:Agent ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "gender" ;
+ rdfs:range rdfs:Literal ;
+ vs:term_status "testing" .
+
+foaf:givenName
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "The given name of some person." ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "Given name" ;
+ vs:term_status "testing" .
+
+foaf:givenname
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "The given name of some person." ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "Given name" ;
+ vs:term_status "archaic" .
+
+foaf:holdsAccount
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "Indicates an account held by this agent." ;
+ rdfs:domain foaf:Agent ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "account" ;
+ rdfs:range foaf:OnlineAccount ;
+ vs:term_status "archaic" .
+
+foaf:homepage
+ a rdf:Property ,
+ owl:InverseFunctionalProperty ,
+ owl:ObjectProperty ;
+ rdfs:comment "A homepage for some thing." ;
+ rdfs:domain owl:Thing ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "homepage" ;
+ rdfs:range foaf:Document ;
+ rdfs:subPropertyOf foaf:isPrimaryTopicOf ,
+ foaf:page ;
+ vs:term_status "stable" .
+
+foaf:icqChatID
+ a rdf:Property ,
+ owl:DatatypeProperty ,
+ owl:InverseFunctionalProperty ;
+ rdfs:comment "An ICQ chat ID" ;
+ rdfs:domain foaf:Agent ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "ICQ chat ID" ;
+ rdfs:range rdfs:Literal ;
+ rdfs:subPropertyOf foaf:nick ;
+ vs:term_status "testing" .
+
+foaf:img
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "An image that can be used to represent some thing (ie. those depictions which are particularly representative of something, eg. one's photo on a homepage)." ;
+ rdfs:domain foaf:Person ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "image" ;
+ rdfs:range foaf:Image ;
+ rdfs:subPropertyOf foaf:depiction ;
+ vs:term_status "testing" .
+
+foaf:interest
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "A page about a topic of interest to this person." ;
+ rdfs:domain foaf:Agent ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "interest" ;
+ rdfs:range foaf:Document ;
+ vs:term_status "testing" .
+
+foaf:isPrimaryTopicOf
+ a rdf:Property ,
+ owl:InverseFunctionalProperty ;
+ rdfs:comment "A document that this thing is the primary topic of." ;
+ rdfs:domain owl:Thing ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "is primary topic of" ;
+ rdfs:range foaf:Document ;
+ rdfs:subPropertyOf foaf:page ;
+ owl:inverseOf foaf:primaryTopic ;
+ vs:term_status "stable" .
+
+foaf:jabberID
+ a rdf:Property ,
+ owl:DatatypeProperty ,
+ owl:InverseFunctionalProperty ;
+ rdfs:comment "A jabber ID for something." ;
+ rdfs:domain foaf:Agent ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "jabber ID" ;
+ rdfs:range rdfs:Literal ;
+ vs:term_status "testing" .
+
+foaf:knows
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "A person known by this person (indicating some level of reciprocated interaction between the parties)." ;
+ rdfs:domain foaf:Person ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "knows" ;
+ rdfs:range foaf:Person ;
+ vs:term_status "stable" .
+
+foaf:lastName
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "The last name of a person." ;
+ rdfs:domain foaf:Person ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "lastName" ;
+ rdfs:range rdfs:Literal ;
+ vs:term_status "testing" .
+
+foaf:logo
+ a rdf:Property ,
+ owl:InverseFunctionalProperty ,
+ owl:ObjectProperty ;
+ rdfs:comment "A logo representing some thing." ;
+ rdfs:domain owl:Thing ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "logo" ;
+ rdfs:range owl:Thing ;
+ vs:term_status "testing" .
+
+foaf:made
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "Something that was made by this agent." ;
+ rdfs:domain foaf:Agent ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "made" ;
+ rdfs:range owl:Thing ;
+ owl:inverseOf foaf:maker ;
+ vs:term_status "stable" .
+
+foaf:maker
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "An agent that made this thing." ;
+ rdfs:domain owl:Thing ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "maker" ;
+ rdfs:range foaf:Agent ;
+ owl:equivalentProperty <http://purl.org/dc/terms/creator> ;
+ owl:inverseOf foaf:made ;
+ vs:term_status "stable" .
+
+foaf:mbox
+ a rdf:Property ,
+ owl:InverseFunctionalProperty ,
+ owl:ObjectProperty ;
+ rdfs:comment "A personal mailbox, ie. an Internet mailbox associated with exactly one owner, the first owner of this mailbox. This is a 'static inverse functional property', in that there is (across time and change) at most one individual that ever has any particular value for foaf:mbox." ;
+ rdfs:domain foaf:Agent ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "personal mailbox" ;
+ rdfs:range owl:Thing ;
+ vs:term_status "stable" .
+
+foaf:mbox_sha1sum
+ a rdf:Property ,
+ owl:DatatypeProperty ,
+ owl:InverseFunctionalProperty ;
+ rdfs:comment "The sha1sum of the URI of an Internet mailbox associated with exactly one owner, the first owner of the mailbox." ;
+ rdfs:domain foaf:Agent ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "sha1sum of a personal mailbox URI name" ;
+ rdfs:range rdfs:Literal ;
+ vs:term_status "testing" .
+
+foaf:member
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "Indicates a member of a Group" ;
+ rdfs:domain foaf:Group ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "member" ;
+ rdfs:range foaf:Agent ;
+ vs:term_status "stable" .
+
+foaf:membershipClass
+ a rdf:Property ,
+ owl:AnnotationProperty ;
+ rdfs:comment "Indicates the class of individuals that are a member of a Group" ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "membershipClass" ;
+ vs:term_status "unstable" .
+
+foaf:msnChatID
+ a rdf:Property ,
+ owl:DatatypeProperty ,
+ owl:InverseFunctionalProperty ;
+ rdfs:comment "An MSN chat ID" ;
+ rdfs:domain foaf:Agent ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "MSN chat ID" ;
+ rdfs:range rdfs:Literal ;
+ rdfs:subPropertyOf foaf:nick ;
+ vs:term_status "testing" .
+
+foaf:myersBriggs
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "A Myers Briggs (MBTI) personality classification." ;
+ rdfs:domain foaf:Person ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "myersBriggs" ;
+ rdfs:range rdfs:Literal ;
+ vs:term_status "testing" .
+
+foaf:name
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "A name for some thing." ;
+ rdfs:domain owl:Thing ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "name" ;
+ rdfs:range rdfs:Literal ;
+ rdfs:subPropertyOf rdfs:label ;
+ vs:term_status "testing" .
+
+foaf:nick
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "A short informal nickname characterising an agent (includes login identifiers, IRC and other chat nicknames)." ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "nickname" ;
+ vs:term_status "testing" .
+
+foaf:openid
+ a rdf:Property ,
+ owl:InverseFunctionalProperty ,
+ owl:ObjectProperty ;
+ rdfs:comment "An OpenID for an Agent." ;
+ rdfs:domain foaf:Agent ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "openid" ;
+ rdfs:range foaf:Document ;
+ rdfs:subPropertyOf foaf:isPrimaryTopicOf ;
+ vs:term_status "testing" .
+
+foaf:page
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "A page or document about this thing." ;
+ rdfs:domain owl:Thing ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "page" ;
+ rdfs:range foaf:Document ;
+ owl:inverseOf foaf:topic ;
+ vs:term_status "stable" .
+
+foaf:pastProject
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "A project this person has previously worked on." ;
+ rdfs:domain foaf:Person ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "past project" ;
+ rdfs:range owl:Thing ;
+ vs:term_status "testing" .
+
+foaf:phone
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "A phone, specified using fully qualified tel: URI scheme (refs: http://www.w3.org/Addressing/schemes.html#tel)." ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "phone" ;
+ vs:term_status "testing" .
+
+foaf:plan
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "A .plan comment, in the tradition of finger and '.plan' files." ;
+ rdfs:domain foaf:Person ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "plan" ;
+ rdfs:range rdfs:Literal ;
+ vs:term_status "testing" .
+
+foaf:primaryTopic
+ a rdf:Property ,
+ owl:FunctionalProperty ,
+ owl:ObjectProperty ;
+ rdfs:comment "The primary topic of some page or document." ;
+ rdfs:domain foaf:Document ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "primary topic" ;
+ rdfs:range owl:Thing ;
+ owl:inverseOf foaf:isPrimaryTopicOf ;
+ vs:term_status "stable" .
+
+foaf:publications
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "A link to the publications of this person." ;
+ rdfs:domain foaf:Person ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "publications" ;
+ rdfs:range foaf:Document ;
+ vs:term_status "testing" .
+
+foaf:schoolHomepage
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "A homepage of a school attended by the person." ;
+ rdfs:domain foaf:Person ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "schoolHomepage" ;
+ rdfs:range foaf:Document ;
+ vs:term_status "testing" .
+
+foaf:sha1
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "A sha1sum hash, in hex." ;
+ rdfs:domain foaf:Document ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "sha1sum (hex)" ;
+ vs:term_status "unstable" .
+
+foaf:skypeID
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "A Skype ID" ;
+ rdfs:domain foaf:Agent ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "Skype ID" ;
+ rdfs:range rdfs:Literal ;
+ rdfs:subPropertyOf foaf:nick ;
+ vs:term_status "testing" .
+
+foaf:status
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "A string expressing what the user is happy for the general public (normally) to know about their current activity." ;
+ rdfs:domain foaf:Agent ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "status" ;
+ rdfs:range rdfs:Literal ;
+ vs:term_status "unstable" .
+
+foaf:surname
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "The surname of some person." ;
+ rdfs:domain foaf:Person ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "Surname" ;
+ rdfs:range rdfs:Literal ;
+ vs:term_status "archaic" .
+
+foaf:theme
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "A theme." ;
+ rdfs:domain owl:Thing ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "theme" ;
+ rdfs:range owl:Thing ;
+ vs:term_status "archaic" .
+
+foaf:thumbnail
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "A derived thumbnail image." ;
+ rdfs:domain foaf:Image ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "thumbnail" ;
+ rdfs:range foaf:Image ;
+ vs:term_status "testing" .
+
+foaf:tipjar
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "A tipjar document for this agent, describing means for payment and reward." ;
+ rdfs:domain foaf:Agent ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "tipjar" ;
+ rdfs:range foaf:Document ;
+ rdfs:subPropertyOf foaf:page ;
+ vs:term_status "testing" .
+
+foaf:title
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "Title (Mr, Mrs, Ms, Dr. etc)" ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "title" ;
+ vs:term_status "testing" .
+
+foaf:topic
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "A topic of some page or document." ;
+ rdfs:domain foaf:Document ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "topic" ;
+ rdfs:range owl:Thing ;
+ owl:inverseOf foaf:page ;
+ vs:term_status "testing" .
+
+foaf:topic_interest
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "A thing of interest to this person." ;
+ rdfs:domain foaf:Agent ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "topic_interest" ;
+ rdfs:range owl:Thing ;
+ vs:term_status "testing" .
+
+foaf:weblog
+ a rdf:Property ,
+ owl:InverseFunctionalProperty ,
+ owl:ObjectProperty ;
+ rdfs:comment "A weblog of some thing (whether person, group, company etc.)." ;
+ rdfs:domain foaf:Agent ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "weblog" ;
+ rdfs:range foaf:Document ;
+ rdfs:subPropertyOf foaf:page ;
+ vs:term_status "stable" .
+
+foaf:workInfoHomepage
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "A work info homepage of some person; a page about their work for some organization." ;
+ rdfs:domain foaf:Person ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "work info homepage" ;
+ rdfs:range foaf:Document ;
+ vs:term_status "testing" .
+
+foaf:workplaceHomepage
+ a rdf:Property ,
+ owl:ObjectProperty ;
+ rdfs:comment "A workplace homepage of some person; the homepage of an organization they work for." ;
+ rdfs:domain foaf:Person ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "workplace homepage" ;
+ rdfs:range foaf:Document ;
+ vs:term_status "testing" .
+
+foaf:yahooChatID
+ a rdf:Property ,
+ owl:DatatypeProperty ,
+ owl:InverseFunctionalProperty ;
+ rdfs:comment "A Yahoo chat ID" ;
+ rdfs:domain foaf:Agent ;
+ rdfs:isDefinedBy foaf: ;
+ rdfs:label "Yahoo chat ID" ;
+ rdfs:range rdfs:Literal ;
+ rdfs:subPropertyOf foaf:nick ;
+ vs:term_status "testing" .
+
+wot:assurance
+ a owl:AnnotationProperty .
+
+wot:src_assurance
+ a owl:AnnotationProperty .
diff --git a/schemas/manifest.ttl b/schemas/manifest.ttl
new file mode 100644
index 00000000..ae6d8945
--- /dev/null
+++ b/schemas/manifest.ttl
@@ -0,0 +1,50 @@
+@prefix dc: <http://purl.org/dc/elements/1.1/> .
+@prefix dcam: <http://purl.org/dc/dcam/> .
+@prefix dcterms: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+dc:
+ a owl:Ontology ;
+ rdfs:seeAlso <dc.ttl> .
+
+dcam:
+ a owl:Ontology ;
+ rdfs:seeAlso <dcam.ttl> .
+
+dcterms:
+ a owl:Ontology ;
+ rdfs:seeAlso <dcterms.ttl> .
+
+rdf:
+ a owl:Ontology ;
+ rdfs:seeAlso <rdf.ttl> .
+
+rdfs:
+ a owl:Ontology ;
+ rdfs:seeAlso <rdfs.ttl> .
+
+xsd:
+ a owl:Ontology ;
+ rdfs:seeAlso <xsd.ttl> .
+
+doap:
+ a owl:Ontology ;
+ rdfs:seeAlso <doap.ttl> .
+
+foaf:
+ a owl:Ontology ;
+ rdfs:seeAlso <foaf.ttl> .
+
+mf:
+ a owl:Ontology ;
+ rdfs:seeAlso <mf.ttl> .
+
+owl:
+ a owl:Ontology ;
+ rdfs:seeAlso <owl.ttl> .
diff --git a/schemas/meson.build b/schemas/meson.build
new file mode 100644
index 00000000..20eeb07b
--- /dev/null
+++ b/schemas/meson.build
@@ -0,0 +1,12 @@
+install_data(
+ files(
+ 'dc.ttl',
+ 'dcterms.ttl',
+ 'manifest.ttl',
+ 'mf.ttl',
+ 'owl.ttl',
+ 'rdf.ttl',
+ 'rdfs.ttl',
+ 'xsd.ttl',
+ )
+)
diff --git a/schemas/mf.ttl b/schemas/mf.ttl
new file mode 100644
index 00000000..2b5d2890
--- /dev/null
+++ b/schemas/mf.ttl
@@ -0,0 +1,176 @@
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix dc: <http://purl.org/dc/elements/1.1/> .
+@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
+
+mf:
+ dc:creator "Andy Seaborne" ;
+ dc:date "2004-07" ;
+ dc:description "Test case manifest vocabulary" ;
+ dc:format "RDF" ;
+ dc:identifier mf: ;
+ dc:publisher "W3C RDF Data Access Working Group" ;
+ dc:subject "" ;
+ dc:title "Test case manifest vocabulary" ;
+ rdfs:comment "Manifest vocabulary for test cases" .
+
+mf:IllFormedLiterals
+ a mf:Notable ;
+ rdfs:comment "Tests that involve lexical forms which are illegal for the datatype" .
+
+mf:KnownTypesDefault2Neq
+ a mf:Requirement ;
+ rdfs:comment "Values in disjoint value spaces are not equal" .
+
+mf:LangTagAwareness
+ a mf:Requirement ;
+ rdfs:comment "Tests that require langauge tag handling in FILTERs" .
+
+mf:LaxCardinality
+ a mf:ResultCardinality ;
+ rdfs:comment "The given mf:result for a test with an mf:resultCardinality of mf:ReducedCardinalityTest is the results as if the REDUCED keyword were omitted. To pass such a test, an implementation must produce a result set with each solution in the expected results appearing at least once and no more than the number of times it appears in the expected results. Of course, there must also be no results produced that are not in the expected results." .
+
+mf:Manifest
+ a rdfs:Class ;
+ rdfs:comment "The class of manifests" ;
+ rdfs:label "Manifest" .
+
+mf:ManifestEntry
+ a rdfs:Class ;
+ rdfs:comment "One entry in rdf:type list of entries" ;
+ rdfs:label "Manifest Entry" .
+
+mf:NegativeSyntaxTest
+ a rdfs:Class ;
+ rdfs:comment "A type of test specifically for syntax testing. Syntax tests are not required to have an associated result, only an action. Negative syntax tests are tests of which the result should be a parser error." ;
+ rdfs:label "Negative Syntax Test" .
+
+mf:NegativeSyntaxTest11
+ a rdfs:Class ;
+ rdfs:comment "A type of test specifically for syntax testing of new features in the SPARQL1.1 Query Language. Syntax tests are not required to have an associated result, only an action. Negative syntax tests are tests of which the result should be a parser error." ;
+ rdfs:label "Negative Syntax Test for SPARQL1.1 Query" .
+
+mf:NegativeUpdateSyntaxTest11
+ a rdfs:Class ;
+ rdfs:comment "A type of test specifically for syntax testing of SPARQL1.1 Update. Syntax tests are not required to have an associated result, only an action. Negative syntax tests are tests of which the result should be a parser error." ;
+ rdfs:label "Negative Syntax Test for SPARQL1.1 Update" .
+
+mf:Notable
+ a rdfs:Class ;
+ rdfs:comment "Requirements for a particular test" ;
+ rdfs:label "Notable" .
+
+mf:PositiveSyntaxTest
+ a rdfs:Class ;
+ rdfs:comment "A type of test specifically for syntax testing. Syntax tests are not required to have an associated result, only an action." ;
+ rdfs:label "Positive Syntax Test" .
+
+mf:PositiveSyntaxTest11
+ a rdfs:Class ;
+ rdfs:comment "A type of test specifically for syntax testing of new features in the SPARQL1.1 Query Language. Syntax tests are not required to have an associated result, only an action." ;
+ rdfs:label "Positive Syntax Test for SPARQL1.1 Query" .
+
+mf:PositiveUpdateSyntaxTest11
+ a rdfs:Class ;
+ rdfs:comment "A type of test specifically for syntax testing of SPARQL1.1 Update. Syntax tests are not required to have an associated result, only an action." ;
+ rdfs:label "Positive Syntax Test for SPARQL1.1 Update" .
+
+mf:QueryEvaluationTest
+ a rdfs:Class ;
+ rdfs:comment "A type of test specifically for query evaluation testing. Query evaluation tests are required to have an associated input dataset, a query, and an expected output dataset." ;
+ rdfs:label "Query Evaluation Test" .
+
+mf:Requirement
+ a rdfs:Class ;
+ rdfs:comment "Requirements for a particular test" ;
+ rdfs:label "Requirement" .
+
+mf:ResultCardinality
+ a rdfs:Class ;
+ rdfs:comment "Potential modes of evaluating a test's results with respect to solution cardinality" ;
+ rdfs:label "Result Cardinality" .
+
+mf:StringSimpleLiteralCmp
+ a mf:Requirement ;
+ rdfs:comment "Tests that require simple literal is the same value as an xsd:string of the same lexicial form" .
+
+mf:TestStatus
+ a rdfs:Class ;
+ rdfs:comment "Statuses a test can have" ;
+ rdfs:label "Test Status" .
+
+mf:UpdateEvaluationTest
+ a rdfs:Class ;
+ rdfs:comment "The class of all SPARQL 1.1 Update evaluation tests" ;
+ rdfs:label "Update Evaluation Test" .
+
+mf:XsdDateOperations
+ a mf:Requirement ;
+ rdfs:comment "Tests that require xsd:date operations" .
+
+mf:accepted
+ a mf:TestStatus ;
+ rdfs:label "accepted" .
+
+mf:action
+ a rdf:Property ;
+ rdfs:comment "Action to perform" ;
+ rdfs:domain mf:ManifestEntry ;
+ rdfs:label "action" .
+
+mf:entries
+ a rdf:Property ;
+ rdfs:comment "Connects the manifest resource to rdf:type list of entries" ;
+ rdfs:domain mf:Manifest ;
+ rdfs:label "entries" ;
+ rdfs:range rdf:List .
+
+mf:include
+ a rdf:Property ;
+ rdfs:comment "Connects the manifest resource to rdf:type list of manifests" ;
+ rdfs:domain mf:Manifest ;
+ rdfs:label "include" ;
+ rdfs:range rdf:List .
+
+mf:name
+ a rdf:Property ;
+ rdfs:comment "Optional name of this entry" ;
+ rdfs:domain mf:ManifestEntry ;
+ rdfs:label "name" ;
+ rdfs:range rdfs:Literal .
+
+mf:notable
+ a rdf:Property ;
+ rdfs:comment "Notable feature of this test (advisory)" ;
+ rdfs:domain mf:ManifestEntry ;
+ rdfs:label "notable" .
+
+mf:proposed
+ a mf:TestStatus ;
+ rdfs:label "proposed" .
+
+mf:rejected
+ a mf:TestStatus ;
+ rdfs:label "rejected" .
+
+mf:requires
+ a rdf:Property ;
+ rdfs:comment "Required functionality for execution of this test" ;
+ rdfs:domain mf:ManifestEntry ;
+ rdfs:label "requires" ;
+ rdfs:range mf:Requirement .
+
+mf:result
+ a rdf:Property ;
+ rdfs:comment "The expected outcome" ,
+ "The test status" ;
+ rdfs:domain mf:ManifestEntry ;
+ rdfs:label "result" ;
+ rdfs:range mf:TestStatus .
+
+mf:resultCardinality
+ a rdf:Property ;
+ rdfs:comment "Specifies whether passing the test requires strict or lax cardinality adherence" ;
+ rdfs:domain mf:ManifestEntry ;
+ rdfs:label "result cardinality" ;
+ rdfs:range mf:ResultCardinality .
diff --git a/schemas/owl.ttl b/schemas/owl.ttl
new file mode 100644
index 00000000..9aa0b0c7
--- /dev/null
+++ b/schemas/owl.ttl
@@ -0,0 +1,628 @@
+@prefix dc: <http://purl.org/dc/elements/1.1/> .
+@prefix grddl: <http://www.w3.org/2003/g/data-view#> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xml: <http://www.w3.org/XML/1998/namespace> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+<http://www.w3.org/2002/07/owl>
+ a owl:Ontology ;
+ dc:title "The OWL 2 Schema vocabulary (OWL 2)" ;
+ rdfs:comment """
+ This ontology partially describes the built-in classes and
+ properties that together form the basis of the RDF/XML syntax of OWL 2.
+ The content of this ontology is based on Tables 6.1 and 6.2
+ in Section 6.4 of the OWL 2 RDF-Based Semantics specification,
+ available at http://www.w3.org/TR/owl2-rdf-based-semantics/.
+ Please note that those tables do not include the different annotations
+ (labels, comments and rdfs:isDefinedBy links) used in this file.
+ Also note that the descriptions provided in this ontology do not
+ provide a complete and correct formal description of either the syntax
+ or the semantics of the introduced terms (please see the OWL 2
+ recommendations for the complete and normative specifications).
+ Furthermore, the information provided by this ontology may be
+ misleading if not used with care. This ontology SHOULD NOT be imported
+ into OWL ontologies. Importing this file into an OWL 2 DL ontology
+ will cause it to become an OWL 2 Full ontology and may have other,
+ unexpected, consequences.
+ """ ;
+ rdfs:isDefinedBy <http://www.w3.org/TR/owl2-mapping-to-rdf/> ,
+ <http://www.w3.org/TR/owl2-rdf-based-semantics/> ,
+ <http://www.w3.org/TR/owl2-syntax/> ;
+ rdfs:seeAlso <http://www.w3.org/TR/owl2-rdf-based-semantics/#table-axiomatic-classes> ,
+ <http://www.w3.org/TR/owl2-rdf-based-semantics/#table-axiomatic-properties> ;
+ owl:versionIRI <http://www.w3.org/2002/07/owl> ;
+ owl:versionInfo "$Date: 2009/11/15 10:54:12 $" .
+
+owl:AllDifferent
+ a rdfs:Class ;
+ rdfs:comment "The class of collections of pairwise different individuals." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "AllDifferent" ;
+ rdfs:subClassOf rdfs:Resource .
+
+owl:AllDisjointClasses
+ a rdfs:Class ;
+ rdfs:comment "The class of collections of pairwise disjoint classes." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "AllDisjointClasses" ;
+ rdfs:subClassOf rdfs:Resource .
+
+owl:AllDisjointProperties
+ a rdfs:Class ;
+ rdfs:comment "The class of collections of pairwise disjoint properties." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "AllDisjointProperties" ;
+ rdfs:subClassOf rdfs:Resource .
+
+owl:Annotation
+ a rdfs:Class ;
+ rdfs:comment "The class of annotated annotations for which the RDF serialization consists of an annotated subject, predicate and object." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "Annotation" ;
+ rdfs:subClassOf rdfs:Resource .
+
+owl:AnnotationProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of annotation properties." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "AnnotationProperty" ;
+ rdfs:subClassOf rdf:Property .
+
+owl:AsymmetricProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of asymmetric properties." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "AsymmetricProperty" ;
+ rdfs:subClassOf owl:ObjectProperty .
+
+owl:Axiom
+ a rdfs:Class ;
+ rdfs:comment "The class of annotated axioms for which the RDF serialization consists of an annotated subject, predicate and object." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "Axiom" ;
+ rdfs:subClassOf rdfs:Resource .
+
+owl:Class
+ a rdfs:Class ;
+ rdfs:comment "The class of OWL classes." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "Class" ;
+ rdfs:subClassOf rdfs:Class .
+
+owl:DataRange
+ a rdfs:Class ;
+ rdfs:comment "The class of OWL data ranges, which are special kinds of datatypes. Note: The use of the IRI owl:DataRange has been deprecated as of OWL 2. The IRI rdfs:Datatype SHOULD be used instead." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "DataRange" ;
+ rdfs:subClassOf rdfs:Datatype .
+
+owl:DatatypeProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of data properties." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "DatatypeProperty" ;
+ rdfs:subClassOf rdf:Property .
+
+owl:DeprecatedClass
+ a rdfs:Class ;
+ rdfs:comment "The class of deprecated classes." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "DeprecatedClass" ;
+ rdfs:subClassOf rdfs:Class .
+
+owl:DeprecatedProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of deprecated properties." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "DeprecatedProperty" ;
+ rdfs:subClassOf rdf:Property .
+
+owl:FunctionalProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of functional properties." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "FunctionalProperty" ;
+ rdfs:subClassOf rdf:Property .
+
+owl:InverseFunctionalProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of inverse-functional properties." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "InverseFunctionalProperty" ;
+ rdfs:subClassOf owl:ObjectProperty .
+
+owl:IrreflexiveProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of irreflexive properties." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "IrreflexiveProperty" ;
+ rdfs:subClassOf owl:ObjectProperty .
+
+owl:NamedIndividual
+ a rdfs:Class ;
+ rdfs:comment "The class of named individuals." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "NamedIndividual" ;
+ rdfs:subClassOf owl:Thing .
+
+owl:NegativePropertyAssertion
+ a rdfs:Class ;
+ rdfs:comment "The class of negative property assertions." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "NegativePropertyAssertion" ;
+ rdfs:subClassOf rdfs:Resource .
+
+owl:Nothing
+ a owl:Class ;
+ rdfs:comment "This is the empty class." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "Nothing" ;
+ rdfs:subClassOf owl:Thing .
+
+owl:ObjectProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of object properties." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "ObjectProperty" ;
+ rdfs:subClassOf rdf:Property .
+
+owl:Ontology
+ a rdfs:Class ;
+ rdfs:comment "The class of ontologies." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "Ontology" ;
+ rdfs:subClassOf rdfs:Resource .
+
+owl:OntologyProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of ontology properties." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "OntologyProperty" ;
+ rdfs:subClassOf rdf:Property .
+
+owl:ReflexiveProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of reflexive properties." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "ReflexiveProperty" ;
+ rdfs:subClassOf owl:ObjectProperty .
+
+owl:Restriction
+ a rdfs:Class ;
+ rdfs:comment "The class of property restrictions." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "Restriction" ;
+ rdfs:subClassOf owl:Class .
+
+owl:SymmetricProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of symmetric properties." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "SymmetricProperty" ;
+ rdfs:subClassOf owl:ObjectProperty .
+
+owl:Thing
+ a owl:Class ;
+ rdfs:comment "The class of OWL individuals." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "Thing" .
+
+owl:TransitiveProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of transitive properties." ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "TransitiveProperty" ;
+ rdfs:subClassOf owl:ObjectProperty .
+
+owl:allValuesFrom
+ a rdf:Property ;
+ rdfs:comment "The property that determines the class that a universal property restriction refers to." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "allValuesFrom" ;
+ rdfs:range rdfs:Class .
+
+owl:annotatedProperty
+ a rdf:Property ;
+ rdfs:comment "The property that determines the predicate of an annotated axiom or annotated annotation." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "annotatedProperty" ;
+ rdfs:range rdfs:Resource .
+
+owl:annotatedSource
+ a rdf:Property ;
+ rdfs:comment "The property that determines the subject of an annotated axiom or annotated annotation." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "annotatedSource" ;
+ rdfs:range rdfs:Resource .
+
+owl:annotatedTarget
+ a rdf:Property ;
+ rdfs:comment "The property that determines the object of an annotated axiom or annotated annotation." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "annotatedTarget" ;
+ rdfs:range rdfs:Resource .
+
+owl:assertionProperty
+ a rdf:Property ;
+ rdfs:comment "The property that determines the predicate of a negative property assertion." ;
+ rdfs:domain owl:NegativePropertyAssertion ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "assertionProperty" ;
+ rdfs:range rdf:Property .
+
+owl:backwardCompatibleWith
+ a owl:AnnotationProperty ,
+ owl:OntologyProperty ;
+ rdfs:comment "The annotation property that indicates that a given ontology is backward compatible with another ontology." ;
+ rdfs:domain owl:Ontology ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "backwardCompatibleWith" ;
+ rdfs:range owl:Ontology .
+
+owl:bottomDataProperty
+ a owl:DatatypeProperty ;
+ rdfs:comment "The data property that does not relate any individual to any data value." ;
+ rdfs:domain owl:Thing ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "bottomDataProperty" ;
+ rdfs:range rdfs:Literal .
+
+owl:bottomObjectProperty
+ a owl:ObjectProperty ;
+ rdfs:comment "The object property that does not relate any two individuals." ;
+ rdfs:domain owl:Thing ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "bottomObjectProperty" ;
+ rdfs:range owl:Thing .
+
+owl:cardinality
+ a rdf:Property ;
+ rdfs:comment "The property that determines the cardinality of an exact cardinality restriction." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "cardinality" ;
+ rdfs:range xsd:nonNegativeInteger .
+
+owl:complementOf
+ a rdf:Property ;
+ rdfs:comment "The property that determines that a given class is the complement of another class." ;
+ rdfs:domain owl:Class ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "complementOf" ;
+ rdfs:range owl:Class .
+
+owl:datatypeComplementOf
+ a rdf:Property ;
+ rdfs:comment "The property that determines that a given data range is the complement of another data range with respect to the data domain." ;
+ rdfs:domain rdfs:Datatype ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "datatypeComplementOf" ;
+ rdfs:range rdfs:Datatype .
+
+owl:deprecated
+ a owl:AnnotationProperty ;
+ rdfs:comment "The annotation property that indicates that a given entity has been deprecated." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "deprecated" ;
+ rdfs:range rdfs:Resource .
+
+owl:differentFrom
+ a rdf:Property ;
+ rdfs:comment "The property that determines that two given individuals are different." ;
+ rdfs:domain owl:Thing ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "differentFrom" ;
+ rdfs:range owl:Thing .
+
+owl:disjointUnionOf
+ a rdf:Property ;
+ rdfs:comment "The property that determines that a given class is equivalent to the disjoint union of a collection of other classes." ;
+ rdfs:domain owl:Class ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "disjointUnionOf" ;
+ rdfs:range rdf:List .
+
+owl:disjointWith
+ a rdf:Property ;
+ rdfs:comment "The property that determines that two given classes are disjoint." ;
+ rdfs:domain owl:Class ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "disjointWith" ;
+ rdfs:range owl:Class .
+
+owl:distinctMembers
+ a rdf:Property ;
+ rdfs:comment "The property that determines the collection of pairwise different individuals in a owl:AllDifferent axiom." ;
+ rdfs:domain owl:AllDifferent ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "distinctMembers" ;
+ rdfs:range rdf:List .
+
+owl:equivalentClass
+ a rdf:Property ;
+ rdfs:comment "The property that determines that two given classes are equivalent, and that is used to specify datatype definitions." ;
+ rdfs:domain rdfs:Class ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "equivalentClass" ;
+ rdfs:range rdfs:Class .
+
+owl:equivalentProperty
+ a rdf:Property ;
+ rdfs:comment "The property that determines that two given properties are equivalent." ;
+ rdfs:domain rdf:Property ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "equivalentProperty" ;
+ rdfs:range rdf:Property .
+
+owl:hasKey
+ a rdf:Property ;
+ rdfs:comment "The property that determines the collection of properties that jointly build a key." ;
+ rdfs:domain owl:Class ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "hasKey" ;
+ rdfs:range rdf:List .
+
+owl:hasSelf
+ a rdf:Property ;
+ rdfs:comment "The property that determines the property that a self restriction refers to." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "hasSelf" ;
+ rdfs:range rdfs:Resource .
+
+owl:hasValue
+ a rdf:Property ;
+ rdfs:comment "The property that determines the individual that a has-value restriction refers to." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "hasValue" ;
+ rdfs:range rdfs:Resource .
+
+owl:imports
+ a owl:OntologyProperty ;
+ rdfs:comment "The property that is used for importing other ontologies into a given ontology." ;
+ rdfs:domain owl:Ontology ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "imports" ;
+ rdfs:range owl:Ontology .
+
+owl:incompatibleWith
+ a owl:AnnotationProperty ,
+ owl:OntologyProperty ;
+ rdfs:comment "The annotation property that indicates that a given ontology is incompatible with another ontology." ;
+ rdfs:domain owl:Ontology ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "incompatibleWith" ;
+ rdfs:range owl:Ontology .
+
+owl:intersectionOf
+ a rdf:Property ;
+ rdfs:comment "The property that determines the collection of classes or data ranges that build an intersection." ;
+ rdfs:domain rdfs:Class ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "intersectionOf" ;
+ rdfs:range rdf:List .
+
+owl:inverseOf
+ a rdf:Property ;
+ rdfs:comment "The property that determines that two given properties are inverse." ;
+ rdfs:domain owl:ObjectProperty ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "inverseOf" ;
+ rdfs:range owl:ObjectProperty .
+
+owl:maxCardinality
+ a rdf:Property ;
+ rdfs:comment "The property that determines the cardinality of a maximum cardinality restriction." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "maxCardinality" ;
+ rdfs:range xsd:nonNegativeInteger .
+
+owl:maxQualifiedCardinality
+ a rdf:Property ;
+ rdfs:comment "The property that determines the cardinality of a maximum qualified cardinality restriction." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "maxQualifiedCardinality" ;
+ rdfs:range xsd:nonNegativeInteger .
+
+owl:members
+ a rdf:Property ;
+ rdfs:comment "The property that determines the collection of members in either a owl:AllDifferent, owl:AllDisjointClasses or owl:AllDisjointProperties axiom." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "members" ;
+ rdfs:range rdf:List .
+
+owl:minCardinality
+ a rdf:Property ;
+ rdfs:comment "The property that determines the cardinality of a minimum cardinality restriction." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "minCardinality" ;
+ rdfs:range xsd:nonNegativeInteger .
+
+owl:minQualifiedCardinality
+ a rdf:Property ;
+ rdfs:comment "The property that determines the cardinality of a minimum qualified cardinality restriction." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "minQualifiedCardinality" ;
+ rdfs:range xsd:nonNegativeInteger .
+
+owl:onClass
+ a rdf:Property ;
+ rdfs:comment "The property that determines the class that a qualified object cardinality restriction refers to." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "onClass" ;
+ rdfs:range owl:Class .
+
+owl:onDataRange
+ a rdf:Property ;
+ rdfs:comment "The property that determines the data range that a qualified data cardinality restriction refers to." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "onDataRange" ;
+ rdfs:range rdfs:Datatype .
+
+owl:onDatatype
+ a rdf:Property ;
+ rdfs:comment "The property that determines the datatype that a datatype restriction refers to." ;
+ rdfs:domain rdfs:Datatype ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "onDatatype" ;
+ rdfs:range rdfs:Datatype .
+
+owl:onProperties
+ a rdf:Property ;
+ rdfs:comment "The property that determines the n-tuple of properties that a property restriction on an n-ary data range refers to." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "onProperties" ;
+ rdfs:range rdf:List .
+
+owl:onProperty
+ a rdf:Property ;
+ rdfs:comment "The property that determines the property that a property restriction refers to." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "onProperty" ;
+ rdfs:range rdf:Property .
+
+owl:oneOf
+ a rdf:Property ;
+ rdfs:comment "The property that determines the collection of individuals or data values that build an enumeration." ;
+ rdfs:domain rdfs:Class ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "oneOf" ;
+ rdfs:range rdf:List .
+
+owl:priorVersion
+ a owl:AnnotationProperty ,
+ owl:OntologyProperty ;
+ rdfs:comment "The annotation property that indicates the predecessor ontology of a given ontology." ;
+ rdfs:domain owl:Ontology ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "priorVersion" ;
+ rdfs:range owl:Ontology .
+
+owl:propertyChainAxiom
+ a rdf:Property ;
+ rdfs:comment "The property that determines the n-tuple of properties that build a sub property chain of a given property." ;
+ rdfs:domain owl:ObjectProperty ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "propertyChainAxiom" ;
+ rdfs:range rdf:List .
+
+owl:propertyDisjointWith
+ a rdf:Property ;
+ rdfs:comment "The property that determines that two given properties are disjoint." ;
+ rdfs:domain rdf:Property ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "propertyDisjointWith" ;
+ rdfs:range rdf:Property .
+
+owl:qualifiedCardinality
+ a rdf:Property ;
+ rdfs:comment "The property that determines the cardinality of an exact qualified cardinality restriction." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "qualifiedCardinality" ;
+ rdfs:range xsd:nonNegativeInteger .
+
+owl:sameAs
+ a rdf:Property ;
+ rdfs:comment "The property that determines that two given individuals are equal." ;
+ rdfs:domain owl:Thing ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "sameAs" ;
+ rdfs:range owl:Thing .
+
+owl:someValuesFrom
+ a rdf:Property ;
+ rdfs:comment "The property that determines the class that an existential property restriction refers to." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "someValuesFrom" ;
+ rdfs:range rdfs:Class .
+
+owl:sourceIndividual
+ a rdf:Property ;
+ rdfs:comment "The property that determines the subject of a negative property assertion." ;
+ rdfs:domain owl:NegativePropertyAssertion ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "sourceIndividual" ;
+ rdfs:range owl:Thing .
+
+owl:targetIndividual
+ a rdf:Property ;
+ rdfs:comment "The property that determines the object of a negative object property assertion." ;
+ rdfs:domain owl:NegativePropertyAssertion ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "targetIndividual" ;
+ rdfs:range owl:Thing .
+
+owl:targetValue
+ a rdf:Property ;
+ rdfs:comment "The property that determines the value of a negative data property assertion." ;
+ rdfs:domain owl:NegativePropertyAssertion ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "targetValue" ;
+ rdfs:range rdfs:Literal .
+
+owl:topDataProperty
+ a owl:DatatypeProperty ;
+ rdfs:comment "The data property that relates every individual to every data value." ;
+ rdfs:domain owl:Thing ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "topDataProperty" ;
+ rdfs:range rdfs:Literal .
+
+owl:topObjectProperty
+ a owl:ObjectProperty ;
+ rdfs:comment "The object property that relates every two individuals." ;
+ rdfs:domain owl:Thing ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "topObjectProperty" ;
+ rdfs:range owl:Thing .
+
+owl:unionOf
+ a rdf:Property ;
+ rdfs:comment "The property that determines the collection of classes or data ranges that build a union." ;
+ rdfs:domain rdfs:Class ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "unionOf" ;
+ rdfs:range rdf:List .
+
+owl:versionIRI
+ a owl:OntologyProperty ;
+ rdfs:comment "The property that identifies the version IRI of an ontology." ;
+ rdfs:domain owl:Ontology ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "versionIRI" ;
+ rdfs:range owl:Ontology .
+
+owl:versionInfo
+ a owl:AnnotationProperty ;
+ rdfs:comment "The annotation property that provides version information for an ontology or another OWL construct." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "versionInfo" ;
+ rdfs:range rdfs:Resource .
+
+owl:withRestrictions
+ a rdf:Property ;
+ rdfs:comment "The property that determines the collection of facet-value pairs that define a datatype restriction." ;
+ rdfs:domain rdfs:Datatype ;
+ rdfs:isDefinedBy owl: ;
+ rdfs:label "withRestrictions" ;
+ rdfs:range rdf:List .
diff --git a/schemas/rdf.ttl b/schemas/rdf.ttl
new file mode 100644
index 00000000..2fc978d6
--- /dev/null
+++ b/schemas/rdf.ttl
@@ -0,0 +1,177 @@
+@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 dc: <http://purl.org/dc/elements/1.1/> .
+
+rdf:
+ a owl:Ontology ;
+ dc:date "2019-12-16" ;
+ dc:description "This is the RDF Schema for the RDF vocabulary terms in the RDF Namespace, defined in RDF 1.1 Concepts." ;
+ dc:title "The RDF Concepts Vocabulary (RDF)" .
+
+rdf:Alt
+ a rdfs:Class ;
+ rdfs:comment "The class of containers of alternatives." ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "Alt" ;
+ rdfs:subClassOf rdfs:Container .
+
+rdf:Bag
+ a rdfs:Class ;
+ rdfs:comment "The class of unordered containers." ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "Bag" ;
+ rdfs:subClassOf rdfs:Container .
+
+rdf:CompoundLiteral
+ a rdfs:Class ;
+ rdfs:comment "A class representing a compound literal." ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "CompoundLiteral" ;
+ rdfs:seeAlso <https://www.w3.org/TR/json-ld11/#the-rdf-compoundliteral-class-and-the-rdf-language-and-rdf-direction-properties> ;
+ rdfs:subClassOf rdfs:Resource .
+
+rdf:HTML
+ a rdfs:Datatype ;
+ rdfs:comment "The datatype of RDF literals storing fragments of HTML content" ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "HTML" ;
+ rdfs:seeAlso <http://www.w3.org/TR/rdf11-concepts/#section-html> ;
+ rdfs:subClassOf rdfs:Literal .
+
+rdf:JSON
+ a rdfs:Datatype ;
+ rdfs:comment "The datatype of RDF literals storing JSON content." ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "JSON" ;
+ rdfs:seeAlso <https://www.w3.org/TR/json-ld11/#the-rdf-json-datatype> ;
+ rdfs:subClassOf rdfs:Literal .
+
+rdf:List
+ a rdfs:Class ;
+ rdfs:comment "The class of RDF Lists." ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "List" ;
+ rdfs:subClassOf rdfs:Resource .
+
+rdf:PlainLiteral
+ a rdfs:Datatype ;
+ rdfs:comment "The class of plain (i.e. untyped) literal values, as used in RIF and OWL 2" ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "PlainLiteral" ;
+ rdfs:seeAlso <http://www.w3.org/TR/rdf-plain-literal/> ;
+ rdfs:subClassOf rdfs:Literal .
+
+rdf:Property
+ a rdfs:Class ;
+ rdfs:comment "The class of RDF properties." ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "Property" ;
+ rdfs:subClassOf rdfs:Resource .
+
+rdf:Seq
+ a rdfs:Class ;
+ rdfs:comment "The class of ordered containers." ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "Seq" ;
+ rdfs:subClassOf rdfs:Container .
+
+rdf:Statement
+ a rdfs:Class ;
+ rdfs:comment "The class of RDF statements." ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "Statement" ;
+ rdfs:subClassOf rdfs:Resource .
+
+rdf:XMLLiteral
+ a rdfs:Datatype ;
+ rdfs:comment "The datatype of XML literal values." ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "XMLLiteral" ;
+ rdfs:subClassOf rdfs:Literal .
+
+rdf:direction
+ a rdf:Property ;
+ rdfs:comment "The base direction component of a CompoundLiteral." ;
+ rdfs:domain rdf:CompoundLiteral ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "direction" ;
+ rdfs:seeAlso <https://www.w3.org/TR/json-ld11/#the-rdf-compoundliteral-class-and-the-rdf-language-and-rdf-direction-properties> .
+
+rdf:first
+ a rdf:Property ;
+ rdfs:comment "The first item in the subject RDF list." ;
+ rdfs:domain rdf:List ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "first" ;
+ rdfs:range rdfs:Resource .
+
+rdf:langString
+ a rdfs:Datatype ;
+ rdfs:comment "The datatype of language-tagged string values" ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "langString" ;
+ rdfs:seeAlso <http://www.w3.org/TR/rdf11-concepts/#section-Graph-Literal> ;
+ rdfs:subClassOf rdfs:Literal .
+
+rdf:language
+ a rdf:Property ;
+ rdfs:comment "The language component of a CompoundLiteral." ;
+ rdfs:domain rdf:CompoundLiteral ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "language" ;
+ rdfs:seeAlso <https://www.w3.org/TR/json-ld11/#the-rdf-compoundliteral-class-and-the-rdf-language-and-rdf-direction-properties> .
+
+()
+ a rdf:List ;
+ rdfs:comment "The empty list, with no items in it. If the rest of a list is nil then the list has no more items in it." ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "nil" .
+
+rdf:object
+ a rdf:Property ;
+ rdfs:comment "The object of the subject RDF statement." ;
+ rdfs:domain rdf:Statement ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "object" ;
+ rdfs:range rdfs:Resource .
+
+rdf:predicate
+ a rdf:Property ;
+ rdfs:comment "The predicate of the subject RDF statement." ;
+ rdfs:domain rdf:Statement ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "predicate" ;
+ rdfs:range rdfs:Resource .
+
+rdf:rest
+ a rdf:Property ;
+ rdfs:comment "The rest of the subject RDF list after the first item." ;
+ rdfs:domain rdf:List ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "rest" ;
+ rdfs:range rdf:List .
+
+rdf:subject
+ a rdf:Property ;
+ rdfs:comment "The subject of the subject RDF statement." ;
+ rdfs:domain rdf:Statement ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "subject" ;
+ rdfs:range rdfs:Resource .
+
+rdf:type
+ a rdf:Property ;
+ rdfs:comment "The subject is an instance of a class." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "type" ;
+ rdfs:range rdfs:Class .
+
+rdf:value
+ a rdf:Property ;
+ rdfs:comment "Idiomatic property used for structured values." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy rdf: ;
+ rdfs:label "value" ;
+ rdfs:range rdfs:Resource .
diff --git a/schemas/rdfs.ttl b/schemas/rdfs.ttl
new file mode 100644
index 00000000..8eea65d9
--- /dev/null
+++ b/schemas/rdfs.ttl
@@ -0,0 +1,124 @@
+@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 dc: <http://purl.org/dc/elements/1.1/> .
+
+rdfs:
+ a owl:Ontology ;
+ dc:title "The RDF Schema vocabulary (RDFS)" ;
+ rdfs:seeAlso <http://www.w3.org/2000/01/rdf-schema-more> .
+
+rdfs:Class
+ a rdfs:Class ;
+ rdfs:comment "The class of classes." ;
+ rdfs:isDefinedBy rdfs: ;
+ rdfs:label "Class" ;
+ rdfs:subClassOf rdfs:Resource .
+
+rdfs:Container
+ a rdfs:Class ;
+ rdfs:comment "The class of RDF containers." ;
+ rdfs:isDefinedBy rdfs: ;
+ rdfs:label "Container" ;
+ rdfs:subClassOf rdfs:Resource .
+
+rdfs:ContainerMembershipProperty
+ a rdfs:Class ;
+ rdfs:comment """The class of container membership properties, rdf:_1, rdf:_2, ...,
+ all of which are sub-properties of 'member'.""" ;
+ rdfs:isDefinedBy rdfs: ;
+ rdfs:label "ContainerMembershipProperty" ;
+ rdfs:subClassOf rdf:Property .
+
+rdfs:Datatype
+ a rdfs:Class ;
+ rdfs:comment "The class of RDF datatypes." ;
+ rdfs:isDefinedBy rdfs: ;
+ rdfs:label "Datatype" ;
+ rdfs:subClassOf rdfs:Class .
+
+rdfs:Literal
+ a rdfs:Class ;
+ rdfs:comment "The class of literal values, eg. textual strings and integers." ;
+ rdfs:isDefinedBy rdfs: ;
+ rdfs:label "Literal" ;
+ rdfs:subClassOf rdfs:Resource .
+
+rdfs:Resource
+ a rdfs:Class ;
+ rdfs:comment "The class resource, everything." ;
+ rdfs:isDefinedBy rdfs: ;
+ rdfs:label "Resource" .
+
+rdfs:comment
+ a rdf:Property ;
+ rdfs:comment "A description of the subject resource." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy rdfs: ;
+ rdfs:label "comment" ;
+ rdfs:range rdfs:Literal .
+
+rdfs:domain
+ a rdf:Property ;
+ rdfs:comment "A domain of the subject property." ;
+ rdfs:domain rdf:Property ;
+ rdfs:isDefinedBy rdfs: ;
+ rdfs:label "domain" ;
+ rdfs:range rdfs:Class .
+
+rdfs:isDefinedBy
+ a rdf:Property ;
+ rdfs:comment "The defininition of the subject resource." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy rdfs: ;
+ rdfs:label "isDefinedBy" ;
+ rdfs:range rdfs:Resource ;
+ rdfs:subPropertyOf rdfs:seeAlso .
+
+rdfs:label
+ a rdf:Property ;
+ rdfs:comment "A human-readable name for the subject." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy rdfs: ;
+ rdfs:label "label" ;
+ rdfs:range rdfs:Literal .
+
+rdfs:member
+ a rdf:Property ;
+ rdfs:comment "A member of the subject resource." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy rdfs: ;
+ rdfs:label "member" ;
+ rdfs:range rdfs:Resource .
+
+rdfs:range
+ a rdf:Property ;
+ rdfs:comment "A range of the subject property." ;
+ rdfs:domain rdf:Property ;
+ rdfs:isDefinedBy rdfs: ;
+ rdfs:label "range" ;
+ rdfs:range rdfs:Class .
+
+rdfs:seeAlso
+ a rdf:Property ;
+ rdfs:comment "Further information about the subject resource." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy rdfs: ;
+ rdfs:label "seeAlso" ;
+ rdfs:range rdfs:Resource .
+
+rdfs:subClassOf
+ a rdf:Property ;
+ rdfs:comment "The subject is a subclass of a class." ;
+ rdfs:domain rdfs:Class ;
+ rdfs:isDefinedBy rdfs: ;
+ rdfs:label "subClassOf" ;
+ rdfs:range rdfs:Class .
+
+rdfs:subPropertyOf
+ a rdf:Property ;
+ rdfs:comment "The subject is a subproperty of a property." ;
+ rdfs:domain rdf:Property ;
+ rdfs:isDefinedBy rdfs: ;
+ rdfs:label "subPropertyOf" ;
+ rdfs:range rdf:Property .
diff --git a/schemas/xsd.ttl b/schemas/xsd.ttl
new file mode 100644
index 00000000..0bf5cf93
--- /dev/null
+++ b/schemas/xsd.ttl
@@ -0,0 +1,363 @@
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+xsd:
+ a owl:Ontology ;
+ rdfs:comment "XML Schema Datatypes" .
+
+xsd:anySimpleType
+ a rdfs:Datatype ;
+ rdfs:comment "The base class of any primitive XSD dataype." ;
+ rdfs:label "any simple type" .
+
+xsd:anyURI
+ a rdfs:Datatype ;
+ rdfs:label "any URI" ;
+ owl:onDatatype xsd:anySimpleType .
+
+xsd:base64Binary
+ a rdfs:Datatype ;
+ rdfs:comment "Base64-encoded arbitrary binary data." ;
+ rdfs:label "base64 binary" ;
+ owl:onDatatype xsd:anySimpleType ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "(([A-Za-z0-9+/] *[A-Za-z0-9+/] *[A-Za-z0-9+/] *[A-Za-z0-9+/] *)*(([A-Za-z0-9+/] *[A-Za-z0-9+/] *[A-Za-z0-9+/] *[A-Za-z0-9+/])|([A-Za-z0-9+/] *[A-Za-z0-9+/] *[AEIMQUYcgkosw048] *=)|([A-Za-z0-9+/] *[AQgw] *= *=)))?"
+ ]
+ ) .
+
+xsd:boolean
+ a rdfs:Datatype ;
+ rdfs:label "boolean" ;
+ owl:onDatatype xsd:anySimpleType ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "(true|false|0|1)"
+ ]
+ ) .
+
+xsd:byte
+ a rdfs:Datatype ;
+ rdfs:label "byte" ;
+ owl:onDatatype xsd:short ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 127
+ ]
+ [
+ xsd:minInclusive -128
+ ]
+ ) .
+
+xsd:date
+ a rdfs:Datatype ;
+ rdfs:label "date" ;
+ owl:onDatatype xsd:anySimpleType ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "-?[0-9][0-9][0-9][0-9][0-9]*-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])(Z|[-+][0-2][0-9]:[0-5][0-9])?"
+ ]
+ ) .
+
+xsd:dateTime
+ a rdfs:Datatype ;
+ rdfs:label "date time" ;
+ owl:onDatatype xsd:anySimpleType ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "-?[0-9][0-9][0-9][0-9][0-9]*-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T(([0-1][0-9])|(2[0-4])):[0-5][0-9]:[0-5][0-9](.[0-9]+)?(Z|[-+][0-2][0-9]:[0-5][0-9])?"
+ ]
+ ) .
+
+xsd:decimal
+ a rdfs:Datatype ;
+ rdfs:comment "A subset of the real numbers, which can be represented by decimal numerals." ;
+ rdfs:label "decimal" ;
+ owl:onDatatype xsd:anySimpleType ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "-?INF|NaN|[+-]?(([0-9]+[.]?[0-9]*)|([0-9]*[.]?[0-9]+))([eE][-+]?[0-9]+)?"
+ ]
+ ) .
+
+xsd:double
+ a rdfs:Datatype ;
+ rdfs:comment "IEEE double-precision 64-bit floating point." ;
+ rdfs:label "double" ;
+ owl:onDatatype xsd:anySimpleType ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "-?INF|NaN|[+-]?(([0-9]+[.]?[0-9]*)|([0-9]*[.]?[0-9]+))([eE][-+]?[0-9]+)?"
+ ]
+ ) .
+
+xsd:duration
+ a rdfs:Datatype ;
+ rdfs:label "duration" ;
+ owl:onDatatype xsd:anySimpleType ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "-?P([0-9]+Y)?([0-9]+M)?([0-9]+D)?(T([0-9]+H)?([0-9]+M)?([0-9]+(\\.[0-9]+)?S)?)?"
+ ]
+ [
+ xsd:whiteSpace "collapse"
+ ]
+ ) .
+
+xsd:float
+ a rdfs:Datatype ;
+ rdfs:comment "IEEE single-precision 32-bit floating point." ;
+ rdfs:label "float" ;
+ owl:onDatatype xsd:anySimpleType ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "-?INF|NaN|[+-]?(([0-9]+[.]?[0-9]*)|([0-9]*[.]?[0-9]+))([eE][-+]?[0-9]+)?"
+ ]
+ [
+ xsd:whiteSpace "collapse"
+ ]
+ ) .
+
+xsd:fractionDigits
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "The total number of digits to the right of the decimal point required to represent a value." ;
+ rdfs:label "fraction digits" ;
+ rdfs:range xsd:nonNegativeInteger .
+
+xsd:hexBinary
+ a rdfs:Datatype ;
+ rdfs:comment "Hex-encoded arbitrary binary data." ;
+ rdfs:label "hex binary" ;
+ owl:onDatatype xsd:anySimpleType ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "([0-9A-Fa-f][0-9A-Fa-f])*"
+ ]
+ ) .
+
+xsd:int
+ a rdfs:Datatype ;
+ rdfs:label "int" ;
+ owl:onDatatype xsd:long ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 2147483647
+ ]
+ [
+ xsd:minInclusive -2147483648
+ ]
+ ) .
+
+xsd:integer
+ a rdfs:Datatype ;
+ rdfs:label "integer" ;
+ owl:onDatatype xsd:decimal ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "[-+]?[0-9]+"
+ ]
+ [
+ xsd:fractionDigits 0
+ ]
+ ) .
+
+xsd:language
+ a rdfs:Datatype ;
+ rdfs:label "language" ;
+ owl:onDatatype xsd:token ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "[a-zA-Z][a-zA-Z]?[a-zA-Z]?[a-zA-Z]?[a-zA-Z]?[a-zA-Z]?[a-zA-Z]?[a-zA-Z]?(-[a-zA-Z0-9][a-zA-Z0-9]?[a-zA-Z0-9]?[a-zA-Z0-9]?[a-zA-Z0-9]?[a-zA-Z0-9]?[a-zA-Z0-9]?[a-zA-Z0-9]?)*"
+ ]
+ ) .
+
+xsd:long
+ a rdfs:Datatype ;
+ rdfs:label "long" ;
+ owl:onDatatype xsd:integer ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 9223372036854775807
+ ]
+ [
+ xsd:minInclusive -9223372036854775808
+ ]
+ ) .
+
+xsd:maxExclusive
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "The exclusive upper bound of an ordered datatype." ;
+ rdfs:label "max exclusive" .
+
+xsd:maxInclusive
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "The inclusive upper bound of an ordered datatype." ;
+ rdfs:label "max inclusive" .
+
+xsd:minExclusive
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "The exclusive lower bound of an ordered datatype." ;
+ rdfs:label "min exclusive" .
+
+xsd:minInclusive
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "The inclusive lower bound of an ordered datatype." ;
+ rdfs:label "min inclusive" .
+
+xsd:negativeInteger
+ a rdfs:Datatype ;
+ rdfs:label "negative integer" ;
+ owl:onDatatype xsd:nonPositiveInteger ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive -1
+ ]
+ ) .
+
+xsd:nonNegativeInteger
+ a rdfs:Datatype ;
+ rdfs:label "non-negative integer" ;
+ owl:onDatatype xsd:integer ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "[+]?[0-9]+"
+ ]
+ [
+ xsd:minInclusive 0
+ ]
+ ) .
+
+xsd:nonPositiveInteger
+ a rdfs:Datatype ;
+ rdfs:label "non-positive integer" ;
+ owl:onDatatype xsd:integer ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "(0|-[0-9]+)"
+ ]
+ [
+ xsd:maxInclusive 0
+ ]
+ ) .
+
+xsd:normalizedString
+ a rdfs:Datatype ;
+ rdfs:comment "The set of strings that do not contain the carriage return (#xD), line feed (#xA) nor tab (#x9) characters." ;
+ rdfs:label "normalized string" ;
+ owl:onDatatype xsd:string .
+
+xsd:pattern
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "A regular expression that matches complete valid literals." ;
+ rdfs:label "pattern" .
+
+xsd:positiveInteger
+ a rdfs:Datatype ;
+ rdfs:label "positive integer" ;
+ owl:onDatatype xsd:nonNegativeInteger ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "[+]?[0-9]*[1-9]+[0-9]*"
+ ]
+ [
+ xsd:minInclusive 1
+ ]
+ ) .
+
+xsd:short
+ a rdfs:Datatype ;
+ rdfs:label "short" ;
+ owl:onDatatype xsd:int ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 32767
+ ]
+ [
+ xsd:minInclusive -32768
+ ]
+ ) .
+
+xsd:string
+ a rdfs:Datatype ;
+ rdfs:comment "A character string." ;
+ rdfs:label "string" ;
+ owl:onDatatype xsd:anySimpleType .
+
+xsd:time
+ a rdfs:Datatype ;
+ rdfs:label "time" ;
+ owl:onDatatype xsd:anySimpleType ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "(([0-1][0-9])|(2[0-4])):[0-5][0-9]:[0-5][0-9](.[0-9]+)?(Z|[-+][0-2][0-9]:[0-5][0-9])?"
+ ]
+ ) .
+
+xsd:token
+ a rdfs:Datatype ;
+ rdfs:comment "The set of strings that do not contain the carriage return (#xD), line feed (#xA) nor tab (#x9) characters, that have no leading or trailing spaces (#x20) and that have no internal sequences of two or more spaces." ;
+ rdfs:label "token" ;
+ owl:onDatatype xsd:normalizedString .
+
+xsd:unsignedByte
+ a rdfs:Datatype ;
+ rdfs:label "unsigned byte" ;
+ owl:onDatatype xsd:unsignedShort ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 255
+ ]
+ ) .
+
+xsd:unsignedInt
+ a rdfs:Datatype ;
+ rdfs:label "unsigned int" ;
+ owl:onDatatype xsd:unsignedLong ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 4294967295
+ ]
+ ) .
+
+xsd:unsignedLong
+ a rdfs:Datatype ;
+ rdfs:label "unsigned long" ;
+ owl:onDatatype xsd:nonNegativeInteger ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 18446744073709551615
+ ]
+ ) .
+
+xsd:unsignedShort
+ a rdfs:Datatype ;
+ rdfs:label "unsigned short" ;
+ owl:onDatatype xsd:unsignedInt ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 65535
+ ]
+ ) .
+
+xsd:whiteSpace
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:comment "A string that describes whitespace normalization for a string type." ;
+ rdfs:label "white space" ;
+ rdfs:range [
+ a rdfs:Datatype ;
+ owl:onDatatype xsd:string ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "(preserve|replace|collapse)"
+ ]
+ )
+ ] .
diff --git a/scripts/fetch_schemas.sh b/scripts/fetch_schemas.sh
new file mode 100755
index 00000000..ae0baf16
--- /dev/null
+++ b/scripts/fetch_schemas.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+# dc
+curl https://www.dublincore.org/specifications/dublin-core/dcmi-terms/dublin_core_elements.ttl \
+ | tools/serd-filter -I turtle -O turtle -v '?s <http://www.w3.org/2004/02/skos/core#note> ?o .' \
+ | tools/serd-filter -I turtle -O turtle -v '?s <http://purl.org/dc/terms/description> ?o .' \
+ | tools/serd-filter -I turtle -O turtle -v '?s <http://purl.org/dc/terms/issued> ?o .' \
+ | tools/serd-sort -I turtle -O turtle > ../schemas/dc.ttl
+
+# dcterms
+curl http://www.dublincore.org/specifications/dublin-core/dcmi-terms/dublin_core_terms.ttl \
+ | cat <(echo -e '@prefix dc: <http://purl.org/dc/elements/1.1/> .\n@prefix dcam: <http://purl.org/dc/dcam/> .\n@prefix dcterms: <http://purl.org/dc/terms/> .\n@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n') - \
+ | tools/serd-sort -I turtle -O turtle \
+ > ../schemas/dcterms.ttl
+
+# dcam
+curl https://www.dublincore.org/specifications/dublin-core/dcmi-terms/dublin_core_abstract_model.ttl \
+ | tools/serd-sort -I turtle -O turtle > ../schemas/dcam.ttl
+
+# rdf
+curl http://www.w3.org/1999/02/22-rdf-syntax-ns# \
+ | tools/serd-sort -I turtle -O turtle > ../schemas/rdf.ttl
+
+# rdfs
+curl http://www.w3.org/2000/01/rdf-schema# \
+ | tools/serd-sort -I turtle -O turtle > ../schemas/rdfs.ttl
+
+# owl
+curl http://www.w3.org/2002/07/owl# \
+ | tools/serd-filter -I turtle -O turtle -v '?s <http://www.w3.org/2002/07/owl#imports> ?o .' \
+ | tools/serd-filter -I turtle -O turtle -v '?s <http://www.w3.org/2003/g/data-view#namespaceTransformation> ?o .' \
+ | tools/serd-sort -I turtle -O turtle \
+ | sed -e "s/\r//g" > ../schemas/owl.ttl
+
+# doap
+curl https://raw.githubusercontent.com/ewilderj/doap/master/schema/doap.rdf \
+ | rapper -o turtle - 'http://example.org/' \
+ | tools/serd-filter -I turtle -O turtle -v '?s <http://www.w3.org/2002/07/owl#imports> ?o .' \
+ | tools/serd-sort -I turtle -O turtle > ../schemas/doap.ttl
+
+# foaf
+curl http://xmlns.com/foaf/spec/index.rdf \
+ | rapper -o turtle - 'http://example.org/' \
+ | tools/serd-sort -I turtle -O turtle > ../schemas/foaf.ttl
+
diff --git a/serd.ttl b/serd.ttl
index bc7d6f77..53f881ba 100644
--- a/serd.ttl
+++ b/serd.ttl
@@ -1,7 +1,25 @@
@prefix doap: <http://usefulinc.com/ns/doap#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
-@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+@prefix serd: <http://drobilla.net/ns/serd#> .
+@prefix checks: <http://drobilla.net/ns/serd/checks#> .
+
+<http://www.w3.org/TR/n-quads/>
+ a doap:Specification ;
+ rdfs:label "NQuads" .
+
+<http://www.w3.org/TR/n-triples/>
+ a doap:Specification ;
+ rdfs:label "NTriples" .
+
+<http://www.w3.org/TR/trig/>
+ a doap:Specification ;
+ rdfs:label "TriG" .
+
+<http://www.w3.org/TR/turtle/>
+ a doap:Specification ;
+ rdfs:label "Turtle" .
<http://drobilla.net/drobilla#me>
a foaf:Person ;
@@ -30,3 +48,140 @@
a doap:GitBranch ;
doap:location <http://gitlab.com/drobilla/serd.git>
] .
+
+serd:ValidatorCheck
+ a rdfs:Class ;
+ rdfs:label "Validator Check" ;
+ rdfs:comment "A serd validator check" .
+
+checks:allValuesFrom
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that all properties with owl:allValuesFrom restrictions have valid value types." .
+
+checks:anyUri
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that the value of any property with range xsd:anyURI is a URI." .
+
+checks:cardinalityEqual
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that any instance of a class with a owl:cardinality property restriction has exactly that many values of that property." .
+
+checks:cardinalityMax
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that any instance of a class with a owl:maxCardinality property restriction has no more than that many values of that property." .
+
+checks:cardinalityMin
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that any instance of a class with a owl:minCardinality property restriction has at least that many values of that property." .
+
+checks:classLabel
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that every rdfs:Class has an rdfs:label." .
+
+checks:classCycle
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that no class is a sub-class of itself, recursively. This ensures that the graph is acyclic with respect to rdfs:subClassOf. If this check fails, all further checks are aborted." .
+
+checks:datatypeCycle
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that no datatype is a sub-datatype of itself, recursively. This ensures that the graph is acyclic with respect to owl:onDatatype. If this check fails, all further checks are aborted." .
+
+checks:datatypeProperty
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that datatype properties have literal (not instance) values." .
+
+checks:datatypeType
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that every datatype is defined as a rdfs:Datatype." .
+
+checks:deprecatedClass
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that there are no instances of deprecated classes." .
+
+checks:deprecatedProperty
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that there are no uses of deprecated properties." .
+
+checks:functionalProperty
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that no instance has several values of a functional property." .
+
+checks:instanceLiteral
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that there are no instances where a literal is expected." .
+
+checks:instanceType
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that every instance with an explicit type matches that type. This is a broad check that triggers other type-related checks, but mainly it will check that every instance of a class conforms to any restrictions on that class." .
+
+checks:explicitInstanceType
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that every instance explicitly has every type required of it. This is a (often overly) strict check that assumes a closed world and requires every instance to explicitly have the type(s) required of it." .
+
+checks:inverseFunctionalProperty
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that at most one instance has a given value of an inverse functional property." .
+
+checks:literalInstance
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that there are no literals where an instance is expected." .
+
+checks:literalMaxExclusive
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that literal values are not greater than or equal to any applicable xsd:maxExclusive datatype restrictions." .
+
+checks:literalMaxInclusive
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that literal values are not greater than any applicable xsd:maxInclusive datatype restrictions." .
+
+checks:literalMinExclusive
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that literal values are not less than or equal to any applicable xsd:minExclusive datatype restrictions." .
+
+checks:literalMinInclusive
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that literal values are not less than any applicable xsd:minInclusive datatype restrictions." .
+
+checks:literalPattern
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that literals with xsd:pattern restrictions match the regular expression pattern for their datatype." .
+
+checks:literalRestriction
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that literals with supported restrictions conform to those restrictions. This is a high-level check that triggers the more specific individual literal restriction checks." .
+
+checks:literalValue
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that literals with supported XSD datatypes are valid. The set of supported types is the same as when writing canonical forms." .
+
+checks:objectProperty
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that object properties have instance (not literal) values." .
+
+checks:plainLiteralDatatype
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that there are no typed literals where a plain literal is expected. A plain literal may have an optional language tag, but not a datatype." .
+
+checks:predicateType
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that every predicate is defined as an rdf:Property." .
+
+checks:propertyCycle
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that no property is a sub-property of itself, recursively. This ensures that the graph is acyclic with respect to rdfs:subPropertyOf. If this check fails, all further checks are aborted." .
+
+checks:propertyDomain
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that any instance with a property with an rdfs:domain is in that domain." .
+
+checks:propertyLabel
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that every rdf:Property has an rdfs:label." .
+
+checks:propertyRange
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that the value for any property with an rdfs:range is in that range." .
+
+checks:someValuesFrom
+ a serd:ValidatorCheck ;
+ rdfs:comment "Checks that instances of classes with owl:someValuesFrom property restrictions have at least one matching property value." .
diff --git a/src/log.c b/src/log.c
index dd62e5cc..41b4c8e5 100644
--- a/src/log.c
+++ b/src/log.c
@@ -158,8 +158,14 @@ serd_vxlogf(const SerdWorld* const world,
// Format and print the message itself
vfprintf(stderr, fmt, args);
- fprintf(stderr, "\n");
+ // Print clang-tidy-style check name (validation errors)
+ const char* const check = get_log_field(n_fields, fields, "SERD_CHECK");
+ if (check) {
+ fprintf(stderr, " [%s]", check);
+ }
+
+ fprintf(stderr, "\n");
return SERD_SUCCESS;
}
diff --git a/src/string.c b/src/string.c
index 1b9bb64a..77cd0191 100644
--- a/src/string.c
+++ b/src/string.c
@@ -75,6 +75,8 @@ serd_strerror(const SerdStatus status)
return "Invalid or unresolved URI";
case SERD_BAD_WRITE:
return "Error writing to file";
+ case SERD_BAD_DATA:
+ return "Invalid data";
}
return "Unknown error";
diff --git a/src/validate.c b/src/validate.c
new file mode 100644
index 00000000..b300c0af
--- /dev/null
+++ b/src/validate.c
@@ -0,0 +1,2042 @@
+/*
+ Copyright 2012-2021 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include "memory.h"
+
+#include "exess/exess.h"
+#include "rerex/rerex.h"
+#include "serd/serd.h"
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define NS_owl "http://www.w3.org/2002/07/owl#"
+#define NS_rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+#define NS_rdfs "http://www.w3.org/2000/01/rdf-schema#"
+#define NS_xsd "http://www.w3.org/2001/XMLSchema#"
+
+#define SERD_FOREACH(name, range) \
+ for (const SerdStatement*(name) = NULL; \
+ !serd_cursor_is_end(range) && ((name) = serd_cursor_get(range)); \
+ serd_cursor_advance(range))
+
+#define SERD_FOREACH_NODE(field, name, range) \
+ for (const SerdNode*(name) = NULL; \
+ !serd_cursor_is_end(range) && \
+ ((name) = serd_statement_node( \
+ (const SerdStatement* SERD_NONNULL)serd_cursor_get(range), field)); \
+ serd_cursor_advance(range))
+
+#define QUOTE_FMT "%s%s%s"
+#define QUOTE_ARGS(quote) quote.prefix.buf, quote.string.buf, quote.suffix.buf
+
+#define N_CHECKS 34
+
+static const char* const check_names[N_CHECKS] = {
+ "nothing", //
+ "allValuesFrom", //
+ "anyUri", //
+ "cardinalityEqual", //
+ "cardinalityMax", //
+ "cardinalityMin", //
+ "classCycle", //
+ "classLabel", //
+ "datatypeCycle", //
+ "datatypeProperty", //
+ "datatypeType", //
+ "deprecatedClass", //
+ "deprecatedProperty", //
+ "explicitInstanceType", //
+ "functionalProperty", //
+ "instanceLiteral", //
+ "instanceType", //
+ "inverseFunctionalProperty", //
+ "literalInstance", //
+ "literalMaxExclusive", //
+ "literalMaxInclusive", //
+ "literalMinExclusive", //
+ "literalMinInclusive", //
+ "literalPattern", //
+ "literalRestriction", //
+ "literalValue", //
+ "objectProperty", //
+ "plainLiteralDatatype", //
+ "predicateType", //
+ "propertyCycle", //
+ "propertyDomain", //
+ "propertyLabel", //
+ "propertyRange", //
+ "someValuesFrom", //
+};
+
+/// Bitwise OR of SerdValidatorCheck values
+typedef uint64_t SerdValidatorChecks;
+
+typedef unsigned long Count;
+
+typedef struct {
+ const SerdNode* owl_Class;
+ const SerdNode* owl_DatatypeProperty;
+ const SerdNode* owl_DeprecatedClass;
+ const SerdNode* owl_DeprecatedProperty;
+ const SerdNode* owl_FunctionalProperty;
+ const SerdNode* owl_InverseFunctionalProperty;
+ const SerdNode* owl_ObjectProperty;
+ const SerdNode* owl_Restriction;
+ const SerdNode* owl_Thing;
+ const SerdNode* owl_allValuesFrom;
+ const SerdNode* owl_cardinality;
+ const SerdNode* owl_deprecated;
+ const SerdNode* owl_equivalentClass;
+ const SerdNode* owl_maxCardinality;
+ const SerdNode* owl_minCardinality;
+ const SerdNode* owl_onDatatype;
+ const SerdNode* owl_onProperty;
+ const SerdNode* owl_someValuesFrom;
+ const SerdNode* owl_unionOf;
+ const SerdNode* owl_withRestrictions;
+ const SerdNode* rdf_PlainLiteral;
+ const SerdNode* rdf_Property;
+ const SerdNode* rdf_XMLLiteral;
+ const SerdNode* rdf_first;
+ const SerdNode* rdf_rest;
+ const SerdNode* rdf_type;
+ const SerdNode* rdfs_Class;
+ const SerdNode* rdfs_Datatype;
+ const SerdNode* rdfs_Literal;
+ const SerdNode* rdfs_Resource;
+ const SerdNode* rdfs_domain;
+ const SerdNode* rdfs_label;
+ const SerdNode* rdfs_range;
+ const SerdNode* rdfs_subClassOf;
+ const SerdNode* rdfs_subPropertyOf;
+ const SerdNode* xsd_anyURI;
+ const SerdNode* xsd_maxExclusive;
+ const SerdNode* xsd_maxInclusive;
+ const SerdNode* xsd_minExclusive;
+ const SerdNode* xsd_minInclusive;
+ const SerdNode* xsd_pattern;
+} URIs;
+
+struct SerdValidatorImpl {
+ const SerdWorld* world;
+ const SerdEnv* env;
+ const SerdModel* model;
+ const SerdNode* graph;
+ const SerdNode* true_node;
+ URIs uris;
+ SerdValidatorChecks checks;
+ unsigned n_errors;
+ unsigned n_checks;
+ bool suppressed;
+};
+
+typedef struct {
+ const char* name;
+} Check;
+
+static SerdStatus
+check_instance_type(SerdValidator* ctx,
+ SerdValidatorCheck check,
+ const SerdNode* root_klass,
+ const SerdStatement* statement,
+ const SerdNode* instance,
+ const SerdNode* klass);
+
+static SerdStatus
+check_instance_restriction(SerdValidator* ctx,
+ const SerdNode* root_klass,
+ const SerdStatement* statement,
+ const SerdNode* instance,
+ const SerdNode* restriction);
+
+typedef struct {
+ SerdStringView prefix;
+ SerdStringView string;
+ SerdStringView suffix;
+} NodeQuote;
+
+static NodeQuote
+format_node(const SerdValidator* const ctx, const SerdNode* const node)
+{
+ const SerdStringView empty = SERD_EMPTY_STRING();
+ NodeQuote q = {empty, empty, empty};
+ SerdStringView prefix = empty;
+ SerdStringView suffix = empty;
+ const SerdStringView node_view = serd_node_string_view(node);
+
+ switch (serd_node_type(node)) {
+ case SERD_LITERAL:
+ q.prefix = SERD_STRING("\"");
+ q.string = node_view;
+ q.suffix = SERD_STRING("\"");
+ break;
+ case SERD_URI:
+ if (!serd_env_qualify(ctx->env, node_view, &prefix, &suffix)) {
+ q.prefix = prefix;
+ q.string = SERD_STRING(":");
+ q.suffix = suffix;
+ } else {
+ q.prefix = SERD_STRING("<");
+ q.string = node_view;
+ q.suffix = SERD_STRING(">");
+ }
+ break;
+ case SERD_BLANK:
+ q.prefix = SERD_STRING("_:");
+ q.string = node_view;
+ break;
+ case SERD_VARIABLE:
+ q.prefix = SERD_STRING("?");
+ q.string = node_view;
+ break;
+ };
+
+ return q;
+}
+
+SERD_LOG_FUNC(5, 0)
+static void
+vreportf(SerdValidator* const ctx,
+ const SerdValidatorCheck check,
+ const SerdLogLevel level,
+ const SerdStatement* const statement,
+ const char* const fmt,
+ va_list args)
+{
+ const char* file = NULL;
+ char line[24] = {0};
+ char col[24] = {0};
+ char status_string[12] = {0};
+
+ snprintf(status_string, sizeof(status_string), "%d", SERD_BAD_DATA);
+
+ const SerdCaret* const caret =
+ statement ? serd_statement_caret(statement) : NULL;
+
+ if (caret) {
+ file = serd_node_string(serd_caret_name(caret));
+
+ snprintf(line, sizeof(line), "%u", serd_caret_line(caret));
+ snprintf(col, sizeof(col), "%u", serd_caret_column(caret));
+ }
+
+ const SerdLogField fields[] = {{"SERD_STATUS", status_string},
+ {"SERD_CHECK", check_names[check]},
+ {"SERD_FILE", file},
+ {"SERD_LINE", line},
+ {"SERD_COL", col}};
+
+ serd_vxlogf(ctx->world, level, caret ? 5u : 2u, fields, fmt, args);
+}
+
+SERD_LOG_FUNC(5, 6)
+static SerdStatus
+report_check(SerdValidator* const ctx,
+ const SerdStatement* const statement,
+ const SerdValidatorCheck check,
+ const bool condition,
+ const char* const fmt,
+ ...)
+{
+ if (!ctx->suppressed) {
+ if (!(ctx->checks & (1ull << check))) {
+ return SERD_SUCCESS;
+ }
+
+ ctx->n_checks += 1;
+ ctx->n_errors += condition ? 0 : 1;
+
+ if (!condition) {
+ va_list args;
+ va_start(args, fmt);
+ vreportf(ctx, check, SERD_LOG_LEVEL_ERROR, statement, fmt, args);
+ va_end(args);
+ }
+ }
+
+ return condition ? SERD_SUCCESS : SERD_BAD_DATA;
+}
+
+SERD_LOG_FUNC(4, 5)
+static void
+log_note(SerdValidator* const ctx,
+ const SerdStatement* const statement,
+ const SerdValidatorCheck check,
+ const char* const fmt,
+ ...)
+{
+ if (!ctx->suppressed) {
+ va_list args;
+ va_start(args, fmt);
+ vreportf(ctx, check, SERD_LOG_LEVEL_NOTICE, statement, fmt, args);
+ va_end(args);
+ }
+}
+
+/*
+ Return true iff `child` is a descendant of `parent` by `pred` arcs.
+
+ That is, returns true if there is a path from `child` to `parent` by
+ following `pred` arcs starting from child.
+*/
+static bool
+is_descendant(SerdValidator* const ctx,
+ const SerdNode* const child,
+ const SerdNode* const parent,
+ const SerdNode* const pred)
+{
+ if (serd_node_equals(child, parent) ||
+ serd_model_ask(
+ ctx->model, child, ctx->uris.owl_equivalentClass, parent, NULL)) {
+ return true;
+ }
+
+ SerdCursor* const i = serd_model_find(ctx->model, child, pred, NULL, NULL);
+ SERD_FOREACH_NODE (SERD_OBJECT, o, i) {
+ if (!serd_node_equals(child, o) && is_descendant(ctx, o, parent, pred)) {
+ serd_cursor_free(i);
+ return true;
+ }
+ }
+
+ serd_cursor_free(i);
+ return false;
+}
+
+// Return true iff `klass` is a subclass of `super`
+static bool
+is_subclass(SerdValidator* const ctx,
+ const SerdNode* const klass,
+ const SerdNode* const super)
+{
+ return serd_node_equals(klass, super) ||
+ is_descendant(ctx, klass, super, ctx->uris.rdfs_subClassOf);
+}
+
+// Return true iff `datatype` is a subdatatype of `super`
+static bool
+is_subdatatype(SerdValidator* const ctx,
+ const SerdNode* const datatype,
+ const SerdNode* const super)
+{
+ return serd_node_equals(datatype, super) ||
+ is_descendant(ctx, datatype, super, ctx->uris.owl_onDatatype);
+}
+
+static void
+update_status(SerdStatus* const old, const SerdStatus next)
+{
+ *old = next > *old ? next : *old;
+}
+
+static SerdStatus
+merge_status(const SerdStatus a, const SerdStatus b)
+{
+ return a > b ? a : b;
+}
+
+static int
+bound_cmp(SerdValidator* const ctx,
+ const SerdStatement* const literal_statement,
+ const SerdNode* const literal,
+ const SerdNode* const type,
+ const SerdStatement* const bound_statement,
+ const SerdNode* const bound)
+{
+ const ExessDatatype value_type =
+ exess_datatype_from_uri(serd_node_string(type));
+
+ if (value_type == EXESS_NOTHING) {
+ return strcmp(serd_node_string(literal), serd_node_string(bound));
+ }
+
+ ExessValue bound_value = {false};
+
+ const ExessVariableResult br = exess_read_value(
+ value_type, sizeof(bound_value), &bound_value, serd_node_string(bound));
+
+ if (br.status) {
+ return !!serd_logf_at(ctx->world,
+ SERD_LOG_LEVEL_ERROR,
+ serd_statement_caret(bound_statement),
+ "Failed to parse bound literal \"%s\" (%s)",
+ serd_node_string(bound),
+ exess_strerror(br.status));
+ }
+
+ ExessValue literal_value = {false};
+
+ const ExessVariableResult lr = exess_read_value(value_type,
+ sizeof(literal_value),
+ &literal_value,
+ serd_node_string(literal));
+
+ if (lr.status) {
+ return !!serd_logf_at(ctx->world,
+ SERD_LOG_LEVEL_ERROR,
+ serd_statement_caret(literal_statement),
+ "Failed to parse literal \"%s\" (%s)",
+ serd_node_string(literal),
+ exess_strerror(lr.status));
+ }
+
+ return exess_value_compare(value_type,
+ lr.write_count,
+ &literal_value,
+ value_type,
+ br.write_count,
+ &bound_value);
+}
+
+static bool
+less(const int cmp)
+{
+ return cmp < 0;
+}
+
+static bool
+less_equal(const int cmp)
+{
+ return cmp <= 0;
+}
+
+static bool
+greater(const int cmp)
+{
+ return cmp > 0;
+}
+
+static bool
+greater_equal(const int cmp)
+{
+ return cmp >= 0;
+}
+
+static SerdStatus
+check_bound(SerdValidator* const ctx,
+ const SerdValidatorCheck check,
+ const SerdStatement* const statement,
+ const SerdNode* const literal,
+ const SerdNode* const type,
+ const SerdNode* const restriction,
+ const SerdNode* const bound_property,
+ bool (*pred)(int),
+ const char* const message)
+{
+ SerdCursor* const b =
+ serd_model_find(ctx->model, restriction, bound_property, 0, 0);
+
+ if (serd_cursor_is_end(b)) {
+ serd_cursor_free(b);
+ return SERD_SUCCESS;
+ }
+
+ const SerdStatement* const bound_statement =
+ (const SerdStatement* SERD_NONNULL)serd_cursor_get(b);
+
+ const SerdNode* const bound = serd_statement_object(bound_statement);
+
+ const int cmp =
+ bound_cmp(ctx, statement, literal, type, bound_statement, bound);
+
+ serd_cursor_free(b);
+
+ return report_check(ctx,
+ statement,
+ check,
+ pred(cmp),
+ "Value \"%s\" %s \"%s\"",
+ serd_node_string(literal),
+ message,
+ serd_node_string(bound));
+}
+
+static RerexPattern*
+parse_regex(const SerdWorld* const world,
+ const SerdStatement* const pattern_statement,
+ const char* const regex)
+{
+ const SerdCaret* const caret =
+ pattern_statement ? serd_statement_caret(pattern_statement) : NULL;
+
+ RerexPattern* re = NULL;
+ size_t end = 0;
+ const RerexStatus st = rerex_compile(regex, &end, &re);
+ if (st) {
+ serd_logf_at(world,
+ SERD_LOG_LEVEL_ERROR,
+ caret,
+ "Error in pattern \"%s\" at offset %lu (%s)",
+ regex,
+ (unsigned long)end,
+ rerex_strerror(st));
+ return NULL;
+ }
+
+ return re;
+}
+
+static bool
+regex_match(SerdValidator* const ctx,
+ const SerdStatement* const pattern_statement,
+ const char* const regex,
+ const char* const str)
+{
+ RerexPattern* const re = parse_regex(ctx->world, pattern_statement, regex);
+ if (!re) {
+ return false;
+ }
+
+ RerexMatcher* const matcher = rerex_new_matcher(re);
+ const bool ret = rerex_match(matcher, str);
+
+ rerex_free_matcher(matcher);
+ rerex_free_pattern(re);
+
+ return ret;
+}
+
+static SerdStatus
+check_literal_restriction(SerdValidator* const ctx,
+ const SerdStatement* const statement,
+ const SerdNode* const literal,
+ const SerdNode* const type,
+ const SerdNode* const restriction)
+{
+ SerdStatus st = SERD_SUCCESS;
+
+ // Check xsd:pattern
+ const SerdStatement* const pat_statement = serd_model_get_statement(
+ ctx->model, restriction, ctx->uris.xsd_pattern, 0, 0);
+ if (pat_statement) {
+ const char* const str = serd_node_string(literal);
+ const SerdNode* const pat_node = serd_statement_object(pat_statement);
+ const char* const pat = serd_node_string(pat_node);
+
+ st = merge_status(st,
+ report_check(ctx,
+ statement,
+ SERD_CHECK_LITERAL_PATTERN,
+ regex_match(ctx, pat_statement, pat, str),
+ "Value \"%s\" doesn't match pattern \"%s\"",
+ serd_node_string(literal),
+ pat));
+ }
+
+ // Check inclusive/exclusive min and max
+
+ typedef bool (*BoundCmpPredicate)(int);
+
+ typedef struct {
+ SerdValidatorCheck check_id;
+ const SerdNode* restriction_property;
+ BoundCmpPredicate pred;
+ const char* const message;
+ } BoundCheck;
+
+ const BoundCheck bound_checks[] = {
+ {SERD_CHECK_LITERAL_MIN_INCLUSIVE,
+ ctx->uris.xsd_minInclusive,
+ greater_equal,
+ "<"},
+ {SERD_CHECK_LITERAL_MAX_INCLUSIVE,
+ ctx->uris.xsd_maxInclusive,
+ less_equal,
+ ">"},
+ {SERD_CHECK_LITERAL_MIN_EXCLUSIVE,
+ ctx->uris.xsd_minExclusive,
+ greater,
+ "<="},
+ {SERD_CHECK_LITERAL_MAX_EXCLUSIVE, ctx->uris.xsd_maxExclusive, less, ">="},
+ };
+
+ for (size_t i = 0; i < sizeof(bound_checks) / sizeof(BoundCheck); ++i) {
+ st = merge_status(st,
+ check_bound(ctx,
+ bound_checks[i].check_id,
+ statement,
+ literal,
+ type,
+ restriction,
+ bound_checks[i].restriction_property,
+ bound_checks[i].pred,
+ bound_checks[i].message));
+ }
+
+ return st;
+}
+
+static bool
+literal_is_valid(SerdValidator* const ctx,
+ const SerdStatement* const statement,
+ const SerdNode* const literal,
+ const SerdNode* const type)
+{
+ if (!type) {
+ return true;
+ }
+
+ // Check that datatype is defined
+ const SerdNode* const node_datatype = serd_node_datatype(literal);
+ if (node_datatype && report_check(ctx,
+ statement,
+ SERD_CHECK_DATATYPE_TYPE,
+ serd_model_ask(ctx->model,
+ node_datatype,
+ ctx->uris.rdf_type,
+ ctx->uris.rdfs_Datatype,
+ NULL),
+ "Undefined datatype <%s>",
+ serd_node_string(node_datatype))) {
+ return false;
+ }
+
+ const NodeQuote type_quote = format_node(ctx, type);
+
+ const ExessDatatype value_type =
+ node_datatype ? exess_datatype_from_uri(serd_node_string(node_datatype))
+ : EXESS_NOTHING;
+
+ if (value_type != EXESS_NOTHING) {
+ /* Check if the literal parses correctly by measuring the canonical string.
+ This is better than trying to read a variant here, because it
+ automatically supports some unbounded datatypes like xsd:decimal and
+ xsd:base64Binary without needing to allocate space for the value. */
+
+ const ExessResult r =
+ exess_write_canonical(serd_node_string(literal), value_type, 0, NULL);
+
+ if (report_check(ctx,
+ statement,
+ SERD_CHECK_LITERAL_VALUE,
+ r.status == EXESS_SUCCESS,
+ "Invalid xsd:%s literal \"%s\" (%s)",
+ serd_node_string(node_datatype) + sizeof(EXESS_XSD_URI) -
+ 1,
+ serd_node_string(literal),
+ exess_strerror(r.status))) {
+ return false;
+ }
+ }
+
+ // Find restrictions list
+ const SerdNode* head =
+ serd_model_get(ctx->model, type, ctx->uris.owl_withRestrictions, 0, 0);
+
+ // Walk list, checking each restriction
+ while (head) {
+ SerdCursor* const i_first =
+ serd_model_find(ctx->model, head, ctx->uris.rdf_first, 0, 0);
+
+ if (serd_cursor_is_end(i_first)) {
+ serd_cursor_free(i_first);
+ break;
+ }
+
+ const SerdStatement* const s_first =
+ (const SerdStatement* SERD_NONNULL)serd_cursor_get(i_first);
+
+ const SerdNode* const first = serd_statement_object(s_first);
+
+ // Check this restriction
+ if (check_literal_restriction(ctx, statement, literal, type, first)) {
+ log_note(ctx,
+ s_first,
+ SERD_CHECK_LITERAL_RESTRICTION,
+ "Restriction on datatype " QUOTE_FMT,
+ QUOTE_ARGS(type_quote));
+ serd_cursor_free(i_first);
+ return false;
+ }
+
+ // Seek to next list node
+ head = serd_model_get(ctx->model, head, ctx->uris.rdf_rest, 0, 0);
+ serd_cursor_free(i_first);
+ }
+
+ // Recurse up datatype hierarchy
+ const SerdNode* const super =
+ serd_model_get(ctx->model, type, ctx->uris.owl_onDatatype, 0, 0);
+
+ // FIXME: check for cycles
+ return super ? literal_is_valid(ctx, statement, literal, super) : true;
+}
+
+static bool
+is_a(SerdValidator* const ctx,
+ const SerdNode* const node,
+ const SerdNode* const type)
+{
+ if (serd_model_ask(ctx->model, node, ctx->uris.rdf_type, type, 0)) {
+ return true; // Instance explicitly has this type
+ }
+
+ SerdCursor* const node_types =
+ serd_model_find(ctx->model, node, ctx->uris.rdf_type, NULL, NULL);
+
+ SERD_FOREACH_NODE (SERD_OBJECT, node_type, node_types) {
+ if (is_subclass(ctx, node_type, type)) {
+ serd_cursor_free(node_types);
+ return true; // Instance explicitly has a subtype of this type
+ }
+ }
+
+ serd_cursor_free(node_types);
+ return false;
+}
+
+static SerdStatus
+check_instance_union_type(SerdValidator* const ctx,
+ const SerdValidatorCheck check,
+ const SerdNode* const root_klass,
+ const SerdStatement* const statement,
+ const SerdNode* const instance,
+ const SerdNode* const klass)
+{
+ SerdStatus st = SERD_SUCCESS;
+
+ // Check owl:unionOf
+ /* if (serd_node_type(klass) == SERD_BLANK) { */
+ const SerdNode* const union_list =
+ serd_model_get(ctx->model, klass, ctx->uris.owl_unionOf, NULL, NULL);
+
+ for (const SerdNode* l = union_list; l;
+ l = serd_model_get(ctx->model, l, ctx->uris.rdf_rest, NULL, NULL)) {
+ const SerdNode* const element =
+ serd_model_get(ctx->model, l, ctx->uris.rdf_first, NULL, NULL);
+ if (element) {
+ ctx->suppressed = true;
+
+ st = check_instance_type(
+ ctx, check, root_klass, statement, instance, element);
+
+ ctx->suppressed = false;
+ if (!st) {
+ return SERD_SUCCESS;
+ }
+ }
+ }
+
+ if (union_list) {
+ return report_check(ctx,
+ statement,
+ check,
+ false,
+ "Instance " QUOTE_FMT " is not any type in union",
+ QUOTE_ARGS(format_node(ctx, instance)));
+ }
+ /* } */
+
+ return st;
+}
+
+static SerdStatus
+check_instance_super_types(SerdValidator* const ctx,
+ const SerdValidatorCheck check,
+ const SerdStatement* const statement,
+ const SerdNode* const instance,
+ const SerdNode* const klass)
+{
+ SerdCursor* const supers =
+ serd_model_find(ctx->model, klass, ctx->uris.rdfs_subClassOf, NULL, NULL);
+
+ SERD_FOREACH_NODE (SERD_OBJECT, super, supers) {
+ if (!serd_node_equals(klass, super) &&
+ !serd_node_equals(super, ctx->uris.rdfs_Class) &&
+ !serd_node_equals(super, ctx->uris.owl_Class)) {
+ if (check_instance_type(ctx, check, klass, statement, instance, super)) {
+ if (serd_node_type(super) == SERD_URI) {
+ log_note(ctx,
+ serd_cursor_get(supers),
+ check,
+ "A " QUOTE_FMT " is a " QUOTE_FMT,
+ QUOTE_ARGS(format_node(ctx, klass)),
+ QUOTE_ARGS(format_node(ctx, super)));
+ }
+
+ serd_cursor_free(supers);
+ return SERD_BAD_DATA;
+ }
+ }
+ }
+ serd_cursor_free(supers);
+
+ return SERD_SUCCESS;
+}
+
+static SerdStatus
+check_instance_type(SerdValidator* const ctx,
+ const SerdValidatorCheck check,
+ const SerdNode* const root_klass,
+ const SerdStatement* const statement,
+ const SerdNode* const instance,
+ const SerdNode* const klass)
+{
+ SerdStatus st = SERD_SUCCESS;
+
+ // Every instance is inherently a rdfs:Resource and owl:Thing
+ if (serd_node_equals(klass, ctx->uris.rdfs_Resource) ||
+ serd_node_equals(klass, ctx->uris.owl_Thing)) {
+ return SERD_SUCCESS;
+ }
+
+ // If the class is xsd:anyURI, check that the instance node is a URI
+ if (serd_node_equals(klass, ctx->uris.xsd_anyURI) ||
+ is_subdatatype(ctx, klass, ctx->uris.xsd_anyURI)) {
+ return report_check(ctx,
+ statement,
+ SERD_CHECK_ANY_URI,
+ serd_node_type(instance) == SERD_URI,
+ "Node " QUOTE_FMT " isn't a URI",
+ QUOTE_ARGS(format_node(ctx, instance)));
+ }
+
+ // Check that instance is not somehow also a rdfs:Literal or rdfs:Datatype
+ if (report_check(ctx,
+ statement,
+ SERD_CHECK_INSTANCE_LITERAL,
+ (!is_subclass(ctx, klass, ctx->uris.rdfs_Literal) &&
+ !is_a(ctx, klass, ctx->uris.rdfs_Datatype)),
+ "Instance " QUOTE_FMT " isn't a literal",
+ QUOTE_ARGS(format_node(ctx, instance)))) {
+ return SERD_BAD_DATA;
+ }
+
+ // Check the instance against the class restriction (if applicable)
+ if ((st = check_instance_restriction(
+ ctx, root_klass, statement, instance, klass))) {
+ return st;
+ }
+
+ // Check the instance against each type in the union class (if applicable)
+ if ((st = check_instance_union_type(
+ ctx, check, root_klass, statement, instance, klass))) {
+ return st;
+ }
+
+ // Check the instance against any superclasses of the class
+ if ((st =
+ check_instance_super_types(ctx, check, statement, instance, klass))) {
+ return st;
+ }
+
+ // No contradictions, so succeed if the instance explicitly has this type
+ if (is_a(ctx, instance, klass)) {
+ return SERD_SUCCESS;
+ }
+
+ if ((ctx->checks & (1ul << SERD_CHECK_EXPLICIT_INSTANCE_TYPE)) &&
+ serd_node_type(instance) != SERD_BLANK) {
+ return report_check(ctx,
+ statement,
+ SERD_CHECK_EXPLICIT_INSTANCE_TYPE,
+ false,
+ "Instance " QUOTE_FMT
+ " does not explicitly have type " QUOTE_FMT,
+ QUOTE_ARGS(format_node(ctx, instance)),
+ QUOTE_ARGS(format_node(ctx, klass)));
+ }
+
+ return SERD_SUCCESS;
+}
+
+static SerdStatus
+check_type(SerdValidator* const ctx,
+ const SerdValidatorCheck check,
+ const SerdStatement* const statement,
+ const SerdNode* const node,
+ const SerdNode* const type)
+{
+ const NodeQuote type_quote = format_node(ctx, type);
+
+ // Everything is an rdfs:Resource
+ if (serd_node_equals(type, ctx->uris.rdfs_Resource)) {
+ return SERD_SUCCESS;
+ }
+
+ switch (serd_node_type(node)) {
+ case SERD_LITERAL:
+ // Every literal is an rdfs:Literal
+ if (serd_node_equals(type, ctx->uris.rdfs_Literal)) {
+ return SERD_SUCCESS;
+ }
+
+ // A plain literal can not have a datatype
+ if (serd_node_equals(type, ctx->uris.rdf_PlainLiteral)) {
+ if (report_check(ctx,
+ statement,
+ SERD_CHECK_PLAIN_LITERAL_DATATYPE,
+ !serd_node_datatype(node),
+ "Typed literal \"%s\" isn't a plain literal",
+ serd_node_string(node))) {
+ return SERD_BAD_DATA;
+ }
+ } else if (report_check(ctx,
+ statement,
+ SERD_CHECK_LITERAL_INSTANCE,
+ is_a(ctx, type, ctx->uris.rdfs_Datatype),
+ "Literal \"%s\" isn't an instance of " QUOTE_FMT,
+ serd_node_string(node),
+ QUOTE_ARGS(type_quote))) {
+ return SERD_BAD_DATA;
+ }
+
+ return literal_is_valid(ctx, statement, node, type) ? SERD_SUCCESS
+ : SERD_BAD_DATA;
+
+ case SERD_URI:
+ if (serd_node_equals(type, ctx->uris.xsd_anyURI)) {
+ return SERD_SUCCESS;
+ }
+ break;
+
+ case SERD_BLANK:
+ case SERD_VARIABLE:
+ break;
+ }
+
+ return check_instance_type(ctx, check, type, statement, node, type);
+}
+
+static Count
+count_non_blanks(SerdCursor* const i, const SerdField field)
+{
+ Count n = 0u;
+ SERD_FOREACH (s, i) {
+ const SerdNode* node = serd_statement_node(s, field);
+ if (node && serd_node_type(node) != SERD_BLANK) {
+ ++n;
+ }
+ }
+ return n;
+}
+
+static SerdStatus
+check_cardinality_restriction(SerdValidator* const ctx,
+ const SerdNode* const root_klass,
+ const SerdNode* const restriction,
+ const SerdStatement* const statement,
+ const SerdNode* const instance)
+{
+ const SerdNode* const prop = serd_model_get(
+ ctx->model, restriction, ctx->uris.owl_onProperty, NULL, NULL);
+
+ const SerdStatement* const equal_statement = serd_model_get_statement(
+ ctx->model, restriction, ctx->uris.owl_cardinality, NULL, NULL);
+
+ const SerdStatement* const min_statement = serd_model_get_statement(
+ ctx->model, restriction, ctx->uris.owl_minCardinality, NULL, NULL);
+
+ const SerdStatement* const max_statement = serd_model_get_statement(
+ ctx->model, restriction, ctx->uris.owl_maxCardinality, NULL, NULL);
+
+ if (!equal_statement && !min_statement && !max_statement) {
+ return SERD_SUCCESS;
+ }
+
+ const NodeQuote prop_quote = format_node(ctx, prop);
+ const NodeQuote klass_quote = format_node(ctx, root_klass);
+
+ SerdStatus st = SERD_SUCCESS;
+ const Count n_values =
+ (Count)serd_model_count(ctx->model, instance, prop, NULL, NULL);
+
+ // Check owl:cardinality
+ if (equal_statement) {
+ const SerdNode* card = serd_statement_object(equal_statement);
+ const Count expected = strtoul(serd_node_string(card), NULL, 10);
+ if ((st = report_check(ctx,
+ statement,
+ SERD_CHECK_CARDINALITY_EQUAL,
+ n_values == expected,
+ "Instance " QUOTE_FMT " has %lu " QUOTE_FMT
+ " properties",
+ QUOTE_ARGS(format_node(ctx, instance)),
+ n_values,
+ QUOTE_ARGS(prop_quote)))) {
+ log_note(ctx,
+ equal_statement,
+ SERD_CHECK_CARDINALITY_EQUAL,
+ "A " QUOTE_FMT " must have exactly %lu",
+ QUOTE_ARGS(klass_quote),
+ expected);
+ return st;
+ }
+ }
+
+ // Check owl:minCardinality
+ if (min_statement) {
+ const SerdNode* card = serd_statement_object(min_statement);
+ const Count n_min = strtoul(serd_node_string(card), NULL, 10);
+ if ((st = report_check(ctx,
+ statement,
+ SERD_CHECK_CARDINALITY_MIN,
+ n_values >= n_min,
+ "Instance " QUOTE_FMT " has %lu " QUOTE_FMT
+ " properties",
+ QUOTE_ARGS(format_node(ctx, instance)),
+ n_values,
+ QUOTE_ARGS(prop_quote)))) {
+ log_note(ctx,
+ min_statement,
+ SERD_CHECK_CARDINALITY_MIN,
+ "A " QUOTE_FMT " must have at least %lu",
+ QUOTE_ARGS(klass_quote),
+ n_min);
+ return st;
+ }
+ }
+
+ // Check owl:maxCardinality
+ if (max_statement) {
+ const SerdNode* const card = serd_statement_object(max_statement);
+ const Count n_max = strtoul(serd_node_string(card), NULL, 10);
+ if ((st = report_check(ctx,
+ statement,
+ SERD_CHECK_CARDINALITY_MAX,
+ n_values <= n_max,
+ "Instance " QUOTE_FMT " has %lu " QUOTE_FMT
+ " properties",
+ QUOTE_ARGS(format_node(ctx, instance)),
+ n_values,
+ QUOTE_ARGS(prop_quote)))) {
+ log_note(ctx,
+ max_statement,
+ SERD_CHECK_CARDINALITY_MAX,
+ "A " QUOTE_FMT " must have at most %lu",
+ QUOTE_ARGS(klass_quote),
+ n_max);
+ return st;
+ }
+ }
+
+ return st;
+}
+
+static SerdStatus
+check_property_value_restriction(SerdValidator* const ctx,
+ const SerdNode* const root_klass,
+ const SerdNode* const restriction,
+ const SerdStatement* const statement,
+ const SerdNode* const instance)
+{
+ SerdStatus st = SERD_SUCCESS;
+
+ const SerdNode* const prop = serd_model_get(
+ ctx->model, restriction, ctx->uris.owl_onProperty, NULL, NULL);
+
+ const SerdStatement* const all_statement = serd_model_get_statement(
+ ctx->model, restriction, ctx->uris.owl_allValuesFrom, NULL, NULL);
+
+ const SerdStatement* const some_statement = serd_model_get_statement(
+ ctx->model, restriction, ctx->uris.owl_someValuesFrom, NULL, NULL);
+
+ if (!all_statement && !some_statement) {
+ return SERD_SUCCESS;
+ }
+
+ const NodeQuote prop_quote = format_node(ctx, prop);
+ const NodeQuote klass_quote = format_node(ctx, root_klass);
+
+ SerdCursor* const values =
+ serd_model_find(ctx->model, instance, prop, NULL, NULL);
+
+ if (all_statement) {
+ const SerdNode* const type = serd_statement_object(all_statement);
+ const NodeQuote type_quote = format_node(ctx, type);
+ SERD_FOREACH (v, values) {
+ const SerdNode* const value = serd_statement_object(v);
+ const SerdStatus all_st = report_check(
+ ctx,
+ v,
+ SERD_CHECK_ALL_VALUES_FROM,
+ !check_type(ctx, SERD_CHECK_ALL_VALUES_FROM, v, value, type),
+ "Value isn't a " QUOTE_FMT,
+ QUOTE_ARGS(type_quote));
+
+ if (all_st) {
+ st = merge_status(st, all_st);
+ log_note(ctx,
+ all_statement,
+ SERD_CHECK_ALL_VALUES_FROM,
+ "Required for any " QUOTE_FMT " of a " QUOTE_FMT,
+ QUOTE_ARGS(prop_quote),
+ QUOTE_ARGS(klass_quote));
+ }
+ }
+ }
+
+ if (some_statement) {
+ const SerdNode* const type = serd_statement_object(some_statement);
+ const NodeQuote type_quote = format_node(ctx, type);
+
+ // Search for some value with the required type
+ bool found = false;
+ {
+ ctx->suppressed = true;
+ SERD_FOREACH_NODE (SERD_OBJECT, value, values) {
+ if (!check_type(
+ ctx, SERD_CHECK_SOME_VALUES_FROM, statement, value, type)) {
+ found = true;
+ break;
+ }
+ }
+ ctx->suppressed = false;
+ }
+
+ assert(!ctx->suppressed);
+ const SerdStatus some_st =
+ report_check(ctx,
+ statement,
+ SERD_CHECK_SOME_VALUES_FROM,
+ found,
+ QUOTE_FMT " has no " QUOTE_FMT " that is a " QUOTE_FMT,
+ QUOTE_ARGS(format_node(ctx, instance)),
+ QUOTE_ARGS(prop_quote),
+ QUOTE_ARGS(type_quote));
+
+ if (some_st && (ctx->checks & (1ull << SERD_CHECK_SOME_VALUES_FROM))) {
+ log_note(ctx,
+ some_statement,
+ SERD_CHECK_SOME_VALUES_FROM,
+ "An instance of " QUOTE_FMT " must have at least 1",
+ QUOTE_ARGS(klass_quote));
+ }
+
+ st = merge_status(st, some_st);
+ }
+
+ serd_cursor_free(values);
+
+ return st;
+}
+
+static SerdStatus
+check_instance_restriction(SerdValidator* const ctx,
+ const SerdNode* const root_klass,
+ const SerdStatement* const statement,
+ const SerdNode* const instance,
+ const SerdNode* const restriction)
+{
+ SerdStatus st = SERD_SUCCESS;
+
+ st = merge_status(st,
+ check_cardinality_restriction(
+ ctx, root_klass, restriction, statement, instance));
+
+ st = merge_status(st,
+ check_property_value_restriction(
+ ctx, root_klass, restriction, statement, instance));
+
+ return st;
+}
+
+/* Top-Level Checks */
+
+static SerdStatus
+check_class_label(SerdValidator* const ctx)
+{
+ const SerdModel* const model = ctx->model;
+ const URIs* const uris = &ctx->uris;
+ SerdStatus st = SERD_SUCCESS;
+
+ // For each rdfs:Class
+ SerdCursor* const klasses =
+ serd_model_find(model, NULL, uris->rdf_type, uris->rdfs_Class, ctx->graph);
+ SERD_FOREACH (k, klasses) {
+ const SerdNode* const klass = serd_statement_subject(k);
+
+ // Check that it has an rdfs:label in the same graph
+ st = merge_status(
+ st,
+ report_check(
+ ctx,
+ k,
+ SERD_CHECK_CLASS_LABEL,
+ serd_model_ask(ctx->model, klass, uris->rdfs_label, 0, ctx->graph),
+ "Class <%s> has no label",
+ serd_node_string(klass)));
+ }
+ serd_cursor_free(klasses);
+
+ return st;
+}
+
+static SerdStatus
+check_datatype_property(SerdValidator* const ctx)
+{
+ const SerdModel* const model = ctx->model;
+ const URIs* uris = &ctx->uris;
+ SerdStatus st = SERD_SUCCESS;
+
+ // For each owl:DatatypeProperty
+ SerdCursor* const properties = serd_model_find(
+ model, NULL, uris->rdf_type, uris->owl_DatatypeProperty, NULL);
+ SERD_FOREACH (p, properties) {
+ const SerdNode* const prop = serd_statement_subject(p);
+ const NodeQuote prop_quote = format_node(ctx, prop);
+
+ // For each statement of this property in the target graph
+ SerdCursor* const statements =
+ serd_model_find(model, NULL, prop, NULL, ctx->graph);
+ SERD_FOREACH (s, statements) {
+ const SerdNode* const object = serd_statement_object(s);
+
+ // Check that the object is a literal
+ if ((st = report_check(ctx,
+ s,
+ SERD_CHECK_DATATYPE_PROPERTY,
+ serd_node_type(object) == SERD_LITERAL,
+ QUOTE_FMT " isn't a literal",
+ QUOTE_ARGS(format_node(ctx, object))))) {
+ log_note(ctx,
+ p,
+ SERD_CHECK_DATATYPE_PROPERTY,
+ "A " QUOTE_FMT " must be a literal",
+ QUOTE_ARGS(prop_quote));
+ }
+ }
+ serd_cursor_free(statements);
+ }
+ serd_cursor_free(properties);
+
+ return st;
+}
+
+static SerdStatus
+check_deprecated(SerdValidator* const ctx)
+{
+ const SerdModel* const model = ctx->model;
+ SerdStatus st = SERD_SUCCESS;
+
+ // For each deprecated thing
+ SerdCursor* const things = serd_model_find(
+ model, NULL, ctx->uris.owl_deprecated, ctx->true_node, NULL);
+ SERD_FOREACH (t, things) {
+ const SerdNode* const thing = serd_statement_subject(t);
+ const NodeQuote thing_quote = format_node(ctx, thing);
+
+ if (is_a(ctx, thing, ctx->uris.rdf_Property)) {
+ // For each statement of this property in the target graph
+ SerdCursor* const statements =
+ serd_model_find(model, NULL, thing, NULL, ctx->graph);
+ SERD_FOREACH (s, statements) {
+ st = report_check(ctx,
+ s,
+ SERD_CHECK_DEPRECATED_PROPERTY,
+ false,
+ "Use of deprecated property");
+ log_note(ctx,
+ t,
+ SERD_CHECK_DEPRECATED_PROPERTY,
+ "Property " QUOTE_FMT " is deprecated",
+ QUOTE_ARGS(thing_quote));
+ }
+ serd_cursor_free(statements);
+
+ } else if (is_a(ctx, thing, ctx->uris.rdfs_Class)) {
+ // For each explicit instance of this class in the target graph
+ SerdCursor* const statements =
+ serd_model_find(model, NULL, ctx->uris.rdf_type, thing, ctx->graph);
+ SERD_FOREACH (s, statements) {
+ st = report_check(ctx,
+ s,
+ SERD_CHECK_DEPRECATED_CLASS,
+ false,
+ "Instance of deprecated class");
+ log_note(ctx,
+ t,
+ SERD_CHECK_DEPRECATED_CLASS,
+ "Class " QUOTE_FMT " is deprecated",
+ QUOTE_ARGS(thing_quote));
+ }
+ serd_cursor_free(statements);
+ }
+ }
+ serd_cursor_free(things);
+
+ return st;
+}
+
+static SerdStatus
+check_functional_property(SerdValidator* const ctx)
+{
+ const SerdModel* const model = ctx->model;
+ const URIs* uris = &ctx->uris;
+ SerdStatus st = SERD_SUCCESS;
+
+ // For each owl:FunctionalProperty
+ SerdCursor* const properties = serd_model_find(
+ model, NULL, uris->rdf_type, uris->owl_FunctionalProperty, NULL);
+ SERD_FOREACH (p, properties) {
+ const SerdNode* const prop = serd_statement_subject(p);
+ const NodeQuote prop_quote = format_node(ctx, prop);
+
+ const SerdNode* last_subj = NULL;
+
+ // For each instance with this property in the target graph
+ SerdCursor* const statements =
+ serd_model_find(model, NULL, prop, NULL, ctx->graph);
+ SERD_FOREACH (s, statements) {
+ const SerdNode* const subj = serd_statement_subject(s);
+ if (serd_node_equals(subj, last_subj)) {
+ continue;
+ }
+
+ // Count the number of values on this instance
+ SerdCursor* const o =
+ serd_model_find(ctx->model, subj, prop, NULL, ctx->graph);
+ const Count n = count_non_blanks(o, SERD_OBJECT);
+
+ serd_cursor_free(o);
+ if (report_check(ctx,
+ s,
+ SERD_CHECK_FUNCTIONAL_PROPERTY,
+ n <= 1,
+ "Instance has %lu " QUOTE_FMT " properties",
+ n,
+ QUOTE_ARGS(prop_quote))) {
+ st = SERD_BAD_DATA;
+ log_note(ctx,
+ p,
+ SERD_CHECK_FUNCTIONAL_PROPERTY,
+ "An instance may have at most 1");
+ }
+
+ last_subj = subj;
+ }
+ serd_cursor_free(statements);
+ }
+ serd_cursor_free(properties);
+
+ return st;
+}
+
+// FIXME: name
+static SerdStatus
+check_instance(SerdValidator* const ctx)
+{
+ const SerdModel* const model = ctx->model;
+ const URIs* uris = &ctx->uris;
+ SerdStatus st = SERD_SUCCESS;
+
+ // For each rdf:type property in the target graph
+ SerdCursor* const types =
+ serd_model_find(model, NULL, uris->rdf_type, NULL, ctx->graph);
+ SERD_FOREACH (t, types) {
+ const SerdNode* const instance = serd_statement_subject(t);
+ const SerdNode* const type = serd_statement_object(t);
+ const NodeQuote type_quote = format_node(ctx, type);
+
+ if ((st = check_instance_type(
+ ctx, SERD_CHECK_INSTANCE_TYPE, type, t, instance, type))) {
+ log_note(ctx,
+ t,
+ SERD_CHECK_INSTANCE_TYPE,
+ "Instance is explicitly a " QUOTE_FMT,
+ QUOTE_ARGS(type_quote));
+ break;
+ }
+ }
+ serd_cursor_free(types);
+
+ return st;
+}
+
+static SerdStatus
+check_inverse_functional_property(SerdValidator* const ctx)
+{
+ const SerdModel* const model = ctx->model;
+ const URIs* uris = &ctx->uris;
+ SerdStatus st = SERD_SUCCESS;
+
+ // For each owl:InverseFunctionalProperty
+ SerdCursor* const properties = serd_model_find(
+ model, NULL, uris->rdf_type, uris->owl_InverseFunctionalProperty, NULL);
+ SERD_FOREACH (p, properties) {
+ const SerdNode* const prop = serd_statement_subject(p);
+ const NodeQuote prop_quote = format_node(ctx, prop);
+
+ const SerdNode* last_obj = NULL;
+
+ // For each value of this property in the target graph
+ SerdCursor* const statements =
+ serd_model_find(model, NULL, prop, NULL, ctx->graph);
+ SERD_FOREACH (statement, statements) {
+ const SerdNode* const obj = serd_statement_object(statement);
+ const NodeQuote obj_quote = format_node(ctx, obj);
+ if (serd_node_equals(obj, last_obj)) {
+ continue;
+ }
+
+ // Count the number of subjects with this value in the target graph
+ SerdCursor* s = serd_model_find(ctx->model, NULL, prop, obj, ctx->graph);
+ const Count n = count_non_blanks(s, SERD_SUBJECT);
+
+ if (n > 1) {
+ // Get the range again so we can print a note for every value
+ serd_cursor_free(s);
+ s = serd_model_find(ctx->model, NULL, prop, obj, ctx->graph);
+
+ SERD_FOREACH (value_statement, s) {
+ const SerdNode* const subj = serd_statement_subject(value_statement);
+ const NodeQuote subj_quote = format_node(ctx, subj);
+
+ report_check(ctx,
+ value_statement,
+ SERD_CHECK_INVERSE_FUNCTIONAL_PROPERTY,
+ false,
+ "Instance " QUOTE_FMT " shares the " QUOTE_FMT
+ " " QUOTE_FMT,
+ QUOTE_ARGS(subj_quote),
+ QUOTE_ARGS(prop_quote),
+ QUOTE_ARGS(obj_quote));
+ }
+
+ log_note(ctx,
+ p,
+ SERD_CHECK_INVERSE_FUNCTIONAL_PROPERTY,
+ "At most 1 instance may have a given " QUOTE_FMT,
+ QUOTE_ARGS(prop_quote));
+ }
+
+ serd_cursor_free(s);
+ last_obj = obj;
+ }
+ serd_cursor_free(statements);
+ }
+ serd_cursor_free(properties);
+
+ return st;
+}
+
+static SerdStatus
+check_object_property(SerdValidator* const ctx)
+{
+ const SerdModel* const model = ctx->model;
+ SerdStatus st = SERD_SUCCESS;
+
+ // For each owl:ObjectProperty
+ SerdCursor* const properties = serd_model_find(
+ model, NULL, ctx->uris.rdf_type, ctx->uris.owl_ObjectProperty, NULL);
+ SERD_FOREACH_NODE (SERD_SUBJECT, prop, properties) {
+ const NodeQuote prop_quote = format_node(ctx, prop);
+
+ // For each statement of this property in the target graph
+ SerdCursor* const statements =
+ serd_model_find(model, NULL, prop, NULL, ctx->graph);
+ SERD_FOREACH (s, statements) {
+ if (report_check(ctx,
+ s,
+ SERD_CHECK_OBJECT_PROPERTY,
+ serd_node_type(serd_statement_object(s)) != SERD_LITERAL,
+ "Object property has literal value")) {
+ st = SERD_BAD_DATA;
+ log_note(ctx,
+ serd_cursor_get(properties),
+ SERD_CHECK_OBJECT_PROPERTY,
+ "A " QUOTE_FMT " must be an instance",
+ QUOTE_ARGS(prop_quote));
+ }
+ }
+ serd_cursor_free(statements);
+ }
+ serd_cursor_free(properties);
+
+ return st;
+}
+
+static SerdStatus
+check_property_domain(SerdValidator* const ctx)
+{
+ const SerdModel* const model = ctx->model;
+ SerdStatus st = SERD_SUCCESS;
+
+ // For each property with an rdfs:domain
+ SerdCursor* const properties =
+ serd_model_find(model, NULL, ctx->uris.rdfs_domain, NULL, NULL);
+ SERD_FOREACH (p, properties) {
+ const SerdNode* const prop = serd_statement_subject(p);
+ const NodeQuote prop_quote = format_node(ctx, prop);
+ const SerdNode* const domain = serd_statement_object(p);
+ const NodeQuote domain_quote = format_node(ctx, domain);
+
+ // For each statement of this property in the target graph
+ SerdCursor* const statements =
+ serd_model_find(model, NULL, prop, NULL, ctx->graph);
+ SERD_FOREACH (statement, statements) {
+ const SerdNode* const subj = serd_statement_subject(statement);
+
+ // Check that the subject is in the domain
+ if (check_instance_type(
+ ctx, SERD_CHECK_PROPERTY_DOMAIN, domain, statement, subj, domain)) {
+ log_note(ctx,
+ p,
+ SERD_CHECK_PROPERTY_DOMAIN,
+ "An instance with a " QUOTE_FMT " must be a " QUOTE_FMT,
+ QUOTE_ARGS(prop_quote),
+ QUOTE_ARGS(domain_quote));
+ }
+ }
+ serd_cursor_free(statements);
+ }
+ serd_cursor_free(properties);
+
+ return st;
+}
+
+static SerdStatus
+check_property_label(SerdValidator* const ctx)
+{
+ const SerdModel* const model = ctx->model;
+ SerdStatus st = SERD_SUCCESS;
+
+ // For each ?property a rdf:Property in the target graph
+ SerdCursor* const properties = serd_model_find(
+ model, NULL, ctx->uris.rdf_type, ctx->uris.rdf_Property, ctx->graph);
+ SERD_FOREACH (p, properties) {
+ const SerdNode* const property = serd_statement_subject(p);
+
+ update_status(
+ &st,
+ report_check(ctx,
+ p,
+ SERD_CHECK_PROPERTY_LABEL,
+ serd_model_ask(
+ ctx->model, property, ctx->uris.rdfs_label, 0, ctx->graph),
+ "Property <%s> has no label",
+ serd_node_string(property)));
+ }
+ serd_cursor_free(properties);
+
+ return st;
+}
+
+static SerdStatus
+check_property_range(SerdValidator* const ctx)
+{
+ const SerdModel* const model = ctx->model;
+ SerdStatus st = SERD_SUCCESS;
+
+ // For each property with an rdfs:range
+ SerdCursor* const properties =
+ serd_model_find(model, NULL, ctx->uris.rdfs_range, NULL, NULL);
+ SERD_FOREACH (p, properties) {
+ const SerdNode* const prop = serd_statement_subject(p);
+ const SerdNode* const klass = serd_statement_object(p);
+ const NodeQuote prop_quote = format_node(ctx, prop);
+
+ // For each statement of this property in the target graph
+ SerdCursor* const statements =
+ serd_model_find(model, NULL, prop, NULL, ctx->graph);
+ SERD_FOREACH (statement, statements) {
+ const SerdNode* const obj = serd_statement_object(statement);
+
+ // Check that the object is in the range
+ const SerdStatus range_st =
+ check_type(ctx, SERD_CHECK_PROPERTY_RANGE, statement, obj, klass);
+ if (range_st) {
+ log_note(ctx,
+ p,
+ SERD_CHECK_PROPERTY_RANGE,
+ "Required for any " QUOTE_FMT " value",
+ QUOTE_ARGS(prop_quote));
+
+ st = st ? st : range_st;
+ }
+ }
+ serd_cursor_free(statements);
+ }
+ serd_cursor_free(properties);
+
+ return st;
+}
+
+static SerdStatus
+check_predicate_type(SerdValidator* const ctx)
+{
+ // For each predicate
+ SerdStatus st = SERD_SUCCESS;
+ const SerdNode* last_pred = NULL;
+ // FIXME: graph
+ SerdCursor* const all = serd_model_begin_ordered(ctx->model, SERD_ORDER_POS);
+ SERD_FOREACH (s, all) {
+ const SerdNode* const g = serd_statement_graph(s);
+ const SerdNode* const pred = serd_statement_predicate(s);
+ if (serd_node_equals(pred, last_pred) ||
+ (ctx->graph && g && !serd_node_equals(ctx->graph, g))) {
+ continue;
+ }
+
+ const bool defined = serd_model_ask(ctx->model, pred, NULL, NULL, NULL);
+
+ st = merge_status(st,
+ report_check(ctx,
+ s,
+ SERD_CHECK_PREDICATE_TYPE,
+ defined,
+ "Undefined property <%s>",
+ serd_node_string(pred)));
+
+ if (defined) {
+ st = merge_status(
+ st,
+ report_check(
+ ctx,
+ s,
+ SERD_CHECK_PREDICATE_TYPE,
+ serd_model_ask(ctx->model, pred, ctx->uris.rdf_type, NULL, NULL) &&
+ is_a(ctx, pred, ctx->uris.rdf_Property),
+ "<%s> isn't a property",
+ serd_node_string(pred)));
+ }
+
+ last_pred = pred;
+ }
+ serd_cursor_free(all);
+
+ return st;
+}
+
+static SerdStatus
+check_acyclic(SerdValidator* const ctx,
+ const SerdValidatorCheck check,
+ const SerdNode* const root,
+ const SerdNode* const node,
+ const SerdNode* const property,
+ const char* const fmt)
+{
+ SerdStatus st = SERD_SUCCESS;
+
+ // FIXME: graph
+ SerdCursor* const links =
+ serd_model_find(ctx->model, node, property, NULL, NULL);
+ SERD_FOREACH (l, links) {
+ const SerdNode* const object = serd_statement_object(l);
+ const NodeQuote object_quote = format_node(ctx, object);
+
+ if ((st = report_check(ctx,
+ l,
+ check,
+ !serd_node_equals(object, root),
+ fmt,
+ QUOTE_ARGS(object_quote)))) {
+ break;
+ }
+
+ if ((st = check_acyclic(ctx, check, root, object, property, fmt))) {
+ log_note(ctx, l, check, "Via " QUOTE_FMT, QUOTE_ARGS(object_quote));
+ break;
+ }
+ }
+ serd_cursor_free(links);
+
+ return st;
+}
+
+static SerdStatus
+check_subclass_cycle(SerdValidator* const ctx)
+{
+ const SerdModel* const model = ctx->model;
+ SerdStatus st = SERD_SUCCESS;
+
+ // For each subclass
+ SerdCursor* const properties =
+ serd_model_find(model, NULL, ctx->uris.rdfs_subClassOf, NULL, NULL);
+ SERD_FOREACH_NODE (SERD_SUBJECT, root, properties) {
+ st = merge_status(st,
+ check_acyclic(ctx,
+ SERD_CHECK_CLASS_CYCLE,
+ root,
+ root,
+ ctx->uris.rdfs_subClassOf,
+ "Class " QUOTE_FMT
+ " is a sub-class of itself"));
+ }
+ serd_cursor_free(properties);
+
+ return st;
+}
+
+static SerdStatus
+check_ondatatype_cycle(SerdValidator* const ctx)
+{
+ const SerdModel* const model = ctx->model;
+ SerdStatus st = SERD_SUCCESS;
+
+ // For refined datatype
+ SerdCursor* const properties =
+ serd_model_find(model, NULL, ctx->uris.owl_onDatatype, NULL, NULL);
+ SERD_FOREACH_NODE (SERD_SUBJECT, root, properties) {
+ st = merge_status(st,
+ check_acyclic(ctx,
+ SERD_CHECK_DATATYPE_CYCLE,
+ root,
+ root,
+ ctx->uris.owl_onDatatype,
+ "Class " QUOTE_FMT
+ " is a sub-datatype of itself"));
+ }
+ serd_cursor_free(properties);
+
+ return st;
+}
+
+static SerdStatus
+check_subproperty_cycle(SerdValidator* const ctx)
+{
+ const SerdModel* const model = ctx->model;
+ SerdStatus st = SERD_SUCCESS;
+
+ // For each subproperty relation
+ SerdCursor* const properties =
+ serd_model_find(model, NULL, ctx->uris.rdfs_subPropertyOf, NULL, NULL);
+ SERD_FOREACH_NODE (SERD_SUBJECT, root, properties) {
+ st = merge_status(st,
+ check_acyclic(ctx,
+ SERD_CHECK_PROPERTY_CYCLE,
+ root,
+ root,
+ ctx->uris.rdfs_subPropertyOf,
+ "Property " QUOTE_FMT
+ " is a sub-property of itself"));
+ }
+ serd_cursor_free(properties);
+
+ return st;
+}
+
+/* Statement Checks */
+
+static SerdStatus
+statement_check_valid_literal(SerdValidator* const ctx,
+ const SerdStatement* const statement)
+{
+ const SerdNode* const object = serd_statement_object(statement);
+ if (serd_node_type(object) != SERD_LITERAL) {
+ return SERD_SUCCESS;
+ }
+
+ if (!literal_is_valid(ctx, statement, object, serd_node_datatype(object))) {
+ /* log_note(ctx, l, check, "Via " QUOTE_FMT, QUOTE_ARGS(object_string)); */
+
+ return SERD_BAD_DATA;
+ }
+
+ return SERD_SUCCESS;
+}
+
+/* Entry Points */
+
+SerdValidator*
+serd_validator_new(SerdWorld* const world)
+{
+ assert(world);
+
+ SerdValidator* const validator =
+ (SerdValidator*)serd_wcalloc(world, 1, sizeof(SerdValidator));
+
+ if (!validator) {
+ return NULL;
+ }
+
+ SerdNodes* const nodes = serd_world_nodes(world);
+
+ validator->world = world;
+ validator->true_node = serd_nodes_value(nodes, serd_bool(true));
+
+#define URI(prefix, suffix) \
+ validator->uris.prefix##_##suffix = \
+ serd_nodes_uri(nodes, SERD_STRING(NS_##prefix #suffix))
+
+ URI(owl, Class);
+ URI(owl, DatatypeProperty);
+ URI(owl, DeprecatedClass);
+ URI(owl, DeprecatedProperty);
+ URI(owl, FunctionalProperty);
+ URI(owl, InverseFunctionalProperty);
+ URI(owl, ObjectProperty);
+ URI(owl, Restriction);
+ URI(owl, Thing);
+ URI(owl, allValuesFrom);
+ URI(owl, cardinality);
+ URI(owl, deprecated);
+ URI(owl, equivalentClass);
+ URI(owl, maxCardinality);
+ URI(owl, minCardinality);
+ URI(owl, onDatatype);
+ URI(owl, onProperty);
+ URI(owl, someValuesFrom);
+ URI(owl, unionOf);
+ URI(owl, withRestrictions);
+ URI(rdf, PlainLiteral);
+ URI(rdf, Property);
+ URI(rdf, XMLLiteral);
+ URI(rdf, first);
+ URI(rdf, rest);
+ URI(rdf, type);
+ URI(rdfs, Class);
+ URI(rdfs, Datatype);
+ URI(rdfs, Literal);
+ URI(rdfs, Resource);
+ URI(rdfs, domain);
+ URI(rdfs, label);
+ URI(rdfs, range);
+ URI(rdfs, subClassOf);
+ URI(rdfs, subPropertyOf);
+ URI(xsd, anyURI);
+ URI(xsd, maxExclusive);
+ URI(xsd, maxInclusive);
+ URI(xsd, minExclusive);
+ URI(xsd, minInclusive);
+ URI(xsd, pattern);
+
+#undef URI
+
+ return validator;
+}
+
+void
+serd_validator_free(SerdValidator* const validator)
+{
+ serd_wfree(validator->world, validator);
+}
+
+SerdStatus
+serd_validator_enable_check(SerdValidator* const validator,
+ const SerdValidatorCheck check)
+{
+ assert(validator);
+
+ validator->checks |= (SerdValidatorChecks)(1ull << check);
+ return SERD_SUCCESS;
+}
+
+SerdStatus
+serd_validator_disable_check(SerdValidator* const validator,
+ const SerdValidatorCheck check)
+{
+ assert(validator);
+
+ validator->checks &= ~((SerdValidatorChecks)(1ull << check));
+ return SERD_SUCCESS;
+}
+
+static SerdStatus
+for_each_matching_check(SerdValidator* const validator,
+ const char* const regex,
+ SerdStatus (*function)(SerdValidator*,
+ SerdValidatorCheck))
+{
+ RerexPattern* const re = parse_regex(validator->world, NULL, regex);
+ if (!re) {
+ return SERD_BAD_ARG;
+ }
+
+ bool matched = false;
+ RerexMatcher* matcher = rerex_new_matcher(re);
+
+ for (uint64_t i = 0; i < N_CHECKS; ++i) {
+ if (rerex_match(matcher, check_names[i])) {
+ function(validator, (SerdValidatorCheck)i);
+ matched = true;
+ }
+ }
+
+ rerex_free_matcher(matcher);
+ rerex_free_pattern(re);
+
+ return matched ? SERD_SUCCESS : SERD_FAILURE;
+}
+
+SerdStatus
+serd_validator_enable_checks(SerdValidator* const validator,
+ const char* const regex)
+{
+ assert(validator);
+ assert(regex);
+
+ if (!strcmp(regex, "all")) {
+ validator->checks =
+ ~((SerdValidatorChecks)(1ull << SERD_CHECK_EXPLICIT_INSTANCE_TYPE));
+ return SERD_SUCCESS;
+ }
+
+ if (!strcmp(regex, "everything")) {
+ validator->checks = ~0u;
+ return SERD_SUCCESS;
+ }
+
+ return for_each_matching_check(validator, regex, serd_validator_enable_check);
+}
+
+SerdStatus
+serd_validator_disable_checks(SerdValidator* const validator,
+ const char* const regex)
+{
+ assert(validator);
+ assert(regex);
+
+ if (!strcmp(regex, "all")) {
+ validator->checks = 0u;
+ return SERD_SUCCESS;
+ }
+
+ return for_each_matching_check(
+ validator, regex, serd_validator_disable_check);
+}
+
+static SerdStatus
+check_literals(SerdValidator* const ctx)
+{
+ SerdStatus st = SERD_SUCCESS;
+
+ if (ctx->checks & ((1ul << SERD_CHECK_DATATYPE_TYPE) | //
+ (1ul << SERD_CHECK_LITERAL_INSTANCE) |
+ (1ul << SERD_CHECK_LITERAL_MAX_EXCLUSIVE) |
+ (1ul << SERD_CHECK_LITERAL_MAX_INCLUSIVE) |
+ (1ul << SERD_CHECK_LITERAL_MIN_EXCLUSIVE) |
+ (1ul << SERD_CHECK_LITERAL_MIN_INCLUSIVE) |
+ (1ul << SERD_CHECK_LITERAL_PATTERN) |
+ (1ul << SERD_CHECK_LITERAL_RESTRICTION) |
+ (1ul << SERD_CHECK_LITERAL_VALUE))) {
+ SerdCursor* const all =
+ serd_model_begin_ordered(ctx->model, SERD_ORDER_SPO);
+ SERD_FOREACH (statement, all) {
+ update_status(&st, statement_check_valid_literal(ctx, statement));
+ }
+ serd_cursor_free(all);
+ }
+
+ return st;
+}
+
+SerdStatus
+serd_validate(SerdValidator* const validator,
+ const SerdModel* const model,
+ const SerdNode* const graph,
+ const SerdEnv* const env)
+{
+ assert(validator);
+ assert(model);
+
+ SerdValidator* const ctx = validator;
+ SerdStatus st = SERD_SUCCESS;
+
+ ctx->env = env;
+ ctx->model = model;
+ ctx->graph = graph;
+
+ /* Check class/datatype cycles first, so we can give up early if any are
+ found. Dealing with non-trivial cycles everywhere makes some other checks
+ too complicated, and would likely just make the output more confusing
+ anyway. */
+
+ if (ctx->checks & (1ul << SERD_CHECK_CLASS_CYCLE)) {
+ update_status(&st, check_subclass_cycle(ctx));
+ }
+
+ if (ctx->checks & (1ul << SERD_CHECK_DATATYPE_CYCLE)) {
+ update_status(&st, check_ondatatype_cycle(ctx));
+ }
+
+ if (ctx->checks & (1ul << SERD_CHECK_PROPERTY_CYCLE)) {
+ update_status(&st, check_subproperty_cycle(ctx));
+ }
+
+ if (st) {
+ serd_logf(
+ ctx->world,
+ SERD_LOG_LEVEL_NOTICE,
+ "Recursive type or property definition found, aborting further checks");
+ return st;
+ }
+
+ /* No dangerous cycles that might hang the validator, so proceed with normal
+ checks. */
+
+ if (ctx->checks & (1ul << SERD_CHECK_PREDICATE_TYPE)) {
+ update_status(&st, check_predicate_type(ctx));
+ }
+
+ if (ctx->checks & (1ul << SERD_CHECK_CLASS_LABEL)) {
+ update_status(&st, check_class_label(ctx));
+ }
+
+ if (ctx->checks & (1ul << SERD_CHECK_DATATYPE_PROPERTY)) {
+ update_status(&st, check_datatype_property(ctx));
+ }
+
+ if (ctx->checks & ((1ul << SERD_CHECK_DEPRECATED_PROPERTY) |
+ (1ul << SERD_CHECK_DEPRECATED_CLASS))) {
+ update_status(&st, check_deprecated(ctx));
+ }
+
+ if (ctx->checks & (1ul << SERD_CHECK_FUNCTIONAL_PROPERTY)) {
+ update_status(&st, check_functional_property(ctx));
+ }
+
+ if (ctx->checks & (1ul << SERD_CHECK_INSTANCE_TYPE)) {
+ update_status(&st, check_instance(ctx));
+ }
+
+ if (ctx->checks & (1ul << SERD_CHECK_INVERSE_FUNCTIONAL_PROPERTY)) {
+ update_status(&st, check_inverse_functional_property(ctx));
+ }
+
+ if (ctx->checks & (1ul << SERD_CHECK_OBJECT_PROPERTY)) {
+ update_status(&st, check_object_property(ctx));
+ }
+
+ if (ctx->checks & (1ul << SERD_CHECK_PROPERTY_DOMAIN)) {
+ update_status(&st, check_property_domain(ctx));
+ }
+
+ if (ctx->checks & (1ul << SERD_CHECK_PROPERTY_LABEL)) {
+ update_status(&st, check_property_label(ctx));
+ }
+
+ if (ctx->checks & (1ul << SERD_CHECK_PROPERTY_RANGE)) {
+ update_status(&st, check_property_range(ctx));
+ }
+
+ update_status(&st, check_literals(ctx));
+
+ ctx->graph = NULL;
+
+ if (ctx->n_errors > 0) {
+ serd_logf(ctx->world,
+ SERD_LOG_LEVEL_ERROR,
+ "Failed %u of %u validation checks",
+ ctx->n_errors,
+ ctx->n_checks);
+
+ return SERD_BAD_DATA;
+ }
+
+ serd_logf(ctx->world,
+ SERD_LOG_LEVEL_INFO,
+ "Passed all %u validation checks",
+ ctx->n_checks);
+
+ return SERD_SUCCESS;
+}
diff --git a/test/meson.build b/test/meson.build
index eae5d42e..cd0991f8 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -4,6 +4,7 @@ run_filter_suite = find_program('run_filter_suite.py')
run_pipe_suite = find_program('run_pipe_suite.py')
run_pretty_suite = find_program('run_pretty_suite.py')
run_sort_suite = find_program('run_sort_suite.py')
+run_validation_test_suite = find_program('run_validation_test_suite.py')
wrapper = meson.get_cross_property('exe_wrapper', '')
@@ -403,6 +404,27 @@ if is_variable('serd_filter')
# RDF-driven test suite
subdir('filter')
+ validate_test_script_args = (common_script_options +
+ ['--pipe', serd_pipe] +
+ ['--validate', serd_validate])
+
+ # Validation test suite
+ test('validate',
+ run_validation_test_suite,
+ args: validate_test_script_args + [
+ files('validate/manifest.ttl'),
+ 'http://drobilla.net/sw/serd/test/validate/',
+ serd_src_root / 'schemas/dc.ttl',
+ serd_src_root / 'schemas/dcam.ttl',
+ serd_src_root / 'schemas/dcterms.ttl',
+ serd_src_root / 'schemas/owl.ttl',
+ serd_src_root / 'schemas/rdf.ttl',
+ serd_src_root / 'schemas/rdfs.ttl',
+ serd_src_root / 'schemas/xsd.ttl',
+ ],
+ env: test_env,
+ suite: ['suite', 'validate'],
+ timeout: 240)
endif
# Run RDF-driven test suites using serd-pipe and serd-sort
diff --git a/test/pretty/meson.build b/test/pretty/meson.build
index 634f1e0e..673759dc 100644
--- a/test/pretty/meson.build
+++ b/test/pretty/meson.build
@@ -1,17 +1,27 @@
base_uri = 'http://drobilla.net/sw/serd/test/good/'
-args = [files('manifest.ttl')]
+args = [
+ files('manifest.ttl')
+]
test('pretty',
run_pretty_suite,
- args: pipe_test_script_args + ['-o', meson.current_build_dir() / 'pipe'] + args,
+ args: common_script_options + [
+ '--tool', serd_pipe,
+ '--out-dir',
+ meson.current_build_dir() / 'pipe'
+ ] + args,
env: test_env,
suite: ['suite', 'extra', 'pipe'],
timeout: 240)
test('pretty',
run_pretty_suite,
- args: sort_test_script_args + ['-o', meson.current_build_dir() / 'sort'] + args,
+ args: common_script_options + [
+ '--tool', serd_sort,
+ '--out-dir',
+ meson.current_build_dir() / 'sort'
+ ] + args,
env: test_env,
suite: ['suite', 'extra', 'sort'],
timeout: 240)
diff --git a/test/run_filter_suite.py b/test/run_filter_suite.py
index 222b98e4..0e414c69 100755
--- a/test/run_filter_suite.py
+++ b/test/run_filter_suite.py
@@ -124,9 +124,11 @@ def main():
parser.add_argument(
"--pipe", default="tools/serd-pipe", help="serd-pipe executable"
)
+
parser.add_argument(
"--filter", default="tools/serd-filter", help="serd-filter executable"
)
+
parser.add_argument("--wrapper", default="", help="executable wrapper")
parser.add_argument("manifest", help="test suite manifest.ttl file")
parser.add_argument("base_uri", help="base URI for tests")
diff --git a/test/run_validation_test_suite.py b/test/run_validation_test_suite.py
new file mode 100755
index 00000000..890ca7d6
--- /dev/null
+++ b/test/run_validation_test_suite.py
@@ -0,0 +1,278 @@
+#!/usr/bin/env python3
+
+"""Run the serd RDF validation test suite."""
+
+import argparse
+import os
+import re
+import shlex
+import subprocess
+import sys
+import tempfile
+import urllib.parse
+
+import serd_test_util
+
+NS_CHECKS = "http://drobilla.net/ns/serd/checks#"
+NS_MF = "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#"
+NS_SERD = "http://drobilla.net/ns/serd#"
+
+
+def log_error(message):
+ """Log an error message to stderr"""
+
+ sys.stderr.write("error: ")
+ sys.stderr.write(message)
+
+
+def _uri_path(uri):
+ path = urllib.parse.urlparse(uri).path
+ drive = os.path.splitdrive(path[1:])[0]
+ return os.path.realpath(path) if not drive else path[1:]
+
+
+def _run_positive_test(command, out_filename):
+ command_string = " ".join([shlex.quote(c) for c in command])
+
+ with open(out_filename, "w") as stdout:
+ with tempfile.TemporaryFile() as stderr:
+ proc = subprocess.run(
+ command, check=False, stdout=stdout, stderr=stderr
+ )
+ if proc.returncode != 0:
+ log_error("Unexpected command failure\n")
+ sys.stderr.write(command_string + "\n")
+ err_output = stderr.read().decode("utf-8")
+ sys.stderr.write(err_output + "\n")
+ return 1
+
+ return proc.returncode
+
+ return 1
+
+
+def _run_negative_test(command, check_names, out_filename, verbose):
+ command_string = " ".join([shlex.quote(c) for c in command])
+
+ with open(out_filename, "w") as stdout:
+ with tempfile.TemporaryFile() as stderr:
+ proc = subprocess.run(
+ command, check=False, stdout=stdout, stderr=stderr
+ )
+
+ stderr.seek(0, 0) # Seek to beginning
+ err_output = stderr.read().decode("utf-8")
+ if verbose:
+ sys.stderr.write(err_output)
+
+ # Check that the tool returned with status SERD_ERR_INVALID
+ if proc.returncode != 21:
+ log_error("Unexpected status {}\n".format(proc.returncode))
+ sys.stderr.write(command_string + "\n")
+ sys.stderr.write(err_output + "\n")
+ return 1
+
+ # Check that an error message was printed
+ stderr.seek(0, 2) # Seek to end
+ if stderr.tell() == 0: # Empty
+ log_error("No error message printed\n")
+ sys.stderr.write(command_string + "\n")
+ return 1
+
+ # Check that the expected check printed an error message
+ stderr.seek(0) # Seek to start
+ triggered = True
+ for check_name in check_names:
+ if "[{}]".format(check_name) not in err_output:
+ triggered = False
+ break
+
+ if not triggered:
+ log_error("Test didn't trigger {}\n".format(check_names))
+ sys.stderr.write(command_string + "\n")
+ sys.stderr.write(err_output + "\n")
+ return 1
+
+ return 0
+
+ return 1
+
+
+def validation_test_suite(
+ manifest_path,
+ schemas,
+ base_uri,
+ report_filename,
+ pipe_command_prefix,
+ validate_command_prefix,
+ verbose,
+):
+ """Run all tests in a test suite manifest."""
+
+ schema_options = []
+ for schema in schemas:
+ schema_options += ["-s", schema]
+
+ test_dir = os.path.dirname(manifest_path)
+ model, instances = serd_test_util.load_rdf(
+ pipe_command_prefix + ["-B", base_uri], manifest_path
+ )
+
+ top_dir = os.path.commonpath([os.getcwd(), os.path.abspath(test_dir)])
+ out_test_dir = os.path.relpath(test_dir, top_dir)
+
+ os.makedirs(out_test_dir, exist_ok=True)
+
+ asserter = ""
+ if os.getenv("USER") == "drobilla":
+ asserter = "http://drobilla.net/drobilla#me"
+
+ class Results:
+ """Aggregated count of all tests and results."""
+
+ def __init__(self):
+ self.n_tests = 0
+ self.n_failures = 0
+
+ def run_tests(tests, expected_return, results):
+ for test in sorted(tests):
+ print(test)
+ test_uri = model[test][NS_MF + "action"][0]
+ test_uri_path = _uri_path(test_uri)
+ test_name = os.path.basename(test_uri_path)
+ test_path = os.path.join(test_dir, test_name)
+ out_filename = os.path.join(out_test_dir, test_name + ".out")
+
+ results.n_tests += 1
+
+ if expected_return == 0: # Positive test
+ options = ["-W", "everything"]
+ command = (
+ validate_command_prefix
+ + options
+ + schema_options
+ + [test_path]
+ )
+
+ status = _run_positive_test(command, out_filename)
+ passed = status == 0
+ results.n_failures += status
+
+ else: # Negative test
+ if NS_SERD + "triggersCheck" not in model[test]:
+ log_error("{} has no serd:triggersCheck".format(test_name))
+
+ check_names = []
+ if NS_SERD + "triggersCheck" in model[test]:
+ for check in model[test][NS_SERD + "triggersCheck"]:
+ check_names += [check[len(NS_CHECKS) :]]
+
+ options = []
+ for check_name in check_names:
+ options += ["-W", check_name]
+
+ command = (
+ validate_command_prefix
+ + options
+ + schema_options
+ + [test_path]
+ )
+ status = _run_negative_test(
+ command, check_names, out_filename, verbose
+ )
+ passed = status == 0
+ results.n_failures += status
+
+ if passed:
+ options = ["-W", "everything"]
+ for check_name in check_names:
+ options += ["-X", check_name]
+
+ command = (
+ validate_command_prefix
+ + options
+ + schema_options
+ + [test_path]
+ )
+ status = _run_positive_test(command, out_filename)
+ passed = status == 0
+ results.n_failures += status
+
+ # Write test report entry
+ if report_filename:
+ with open(report_filename, "a") as report:
+ report.write(
+ serd_test_util.earl_assertion(test, passed, asserter)
+ )
+
+ # Run all test types in the test suite
+ results = Results()
+ ns_serd = "http://drobilla.net/ns/serd#"
+ for test_class, instances in instances.items():
+ if test_class.startswith(ns_serd):
+ expected = 1 if "Negative" in test_class else 0
+ run_tests(instances, expected, results)
+
+ # Print result summary
+ if results.n_failures > 0:
+ log_error(
+ "{}/{} tests failed\n".format(results.n_failures, results.n_tests)
+ )
+ else:
+ sys.stdout.write("All {} tests passed\n".format(results.n_tests))
+
+ return results.n_failures
+
+
+def main():
+ """Run the command line tool."""
+
+ parser = argparse.ArgumentParser(
+ usage="%(prog)s [OPTION]... MANIFEST BASE_URI SCHEMA...",
+ description=__doc__,
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ )
+
+ parser.add_argument(
+ "--pipe", default="tools/serd-pipe", help="serd-pipe executable"
+ )
+
+ parser.add_argument(
+ "--validate",
+ default="tools/serd-validate",
+ help="serd-validate executable",
+ )
+
+ parser.add_argument("--report", help="path to write result report to")
+ parser.add_argument("--wrapper", default="", help="executable wrapper")
+ parser.add_argument(
+ "-v", "--verbose", action="store_true", help="print expected errors"
+ )
+ parser.add_argument("manifest", help="test suite manifest.ttl file")
+ parser.add_argument("base_uri", help="base URI for tests")
+ parser.add_argument("schema", nargs="+", help="schema file")
+
+ args = parser.parse_args(sys.argv[1:])
+ pipe_command_prefix = shlex.split(args.wrapper) + [args.pipe]
+ validate_command_prefix = shlex.split(args.wrapper) + [args.validate]
+
+ return validation_test_suite(
+ args.manifest,
+ args.schema,
+ args.base_uri,
+ args.report,
+ pipe_command_prefix,
+ validate_command_prefix,
+ args.verbose,
+ )
+
+
+if __name__ == "__main__":
+ try:
+ sys.exit(main())
+ except subprocess.CalledProcessError as error:
+ if error.stderr is not None:
+ sys.stderr.write(error.stderr.decode("utf-8"))
+
+ log_error(str(error) + "\n")
+ sys.exit(error.returncode)
diff --git a/test/test_string.c b/test/test_string.c
index cdc4bc33..52c43c8e 100644
--- a/test/test_string.c
+++ b/test/test_string.c
@@ -33,7 +33,7 @@ test_strerror(void)
const char* msg = serd_strerror(SERD_SUCCESS);
assert(!strcmp(msg, "Success"));
- for (int i = SERD_FAILURE; i <= SERD_BAD_WRITE; ++i) {
+ for (int i = SERD_FAILURE; i <= SERD_BAD_DATA; ++i) {
msg = serd_strerror((SerdStatus)i);
assert(strcmp(msg, "Success"));
}
diff --git a/test/validate/bad-all-values-from.ttl b/test/validate/bad-all-values-from.ttl
new file mode 100644
index 00000000..4d82bf3d
--- /dev/null
+++ b/test/validate/bad-all-values-from.ttl
@@ -0,0 +1,24 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+eg:index
+ a rdf:Property ;
+ rdfs:label "index" .
+
+eg:Thing
+ a rdfs:Class ;
+ rdfs:label "Thing" ;
+ rdfs:subClassOf [
+ a owl:Restriction ;
+ owl:onProperty eg:index ;
+ owl:allValuesFrom xsd:nonNegativeInteger
+ ] .
+
+eg:s
+ a eg:Thing ;
+ eg:index 1.2 ,
+ 3 .
+
diff --git a/test/validate/bad-anyuri.ttl b/test/validate/bad-anyuri.ttl
new file mode 100644
index 00000000..ae5e88f0
--- /dev/null
+++ b/test/validate/bad-anyuri.ttl
@@ -0,0 +1,13 @@
+@prefix eg: <http://example.org/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+eg:uri
+ a rdf:Property ;
+ rdfs:label "uri" ;
+ rdfs:range xsd:anyURI .
+
+eg:s
+ eg:uri _:blank .
+
diff --git a/test/validate/bad-cardinality-high.ttl b/test/validate/bad-cardinality-high.ttl
new file mode 100644
index 00000000..2ff8ede3
--- /dev/null
+++ b/test/validate/bad-cardinality-high.ttl
@@ -0,0 +1,19 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:Thing
+ a rdfs:Class ;
+ rdfs:label "Thing" ;
+ rdfs:subClassOf [
+ a owl:Restriction ;
+ owl:onProperty rdf:value ;
+ owl:maxCardinality 1
+ ] .
+
+eg:s
+ a eg:Thing ;
+ rdf:value 1 ,
+ 2 .
+
diff --git a/test/validate/bad-cardinality-low.ttl b/test/validate/bad-cardinality-low.ttl
new file mode 100644
index 00000000..60bfc9f8
--- /dev/null
+++ b/test/validate/bad-cardinality-low.ttl
@@ -0,0 +1,17 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:Thing
+ a rdfs:Class ;
+ rdfs:label "Thing" ;
+ rdfs:subClassOf [
+ a owl:Restriction ;
+ owl:onProperty rdf:value ;
+ owl:minCardinality 1
+ ] .
+
+eg:s
+ a eg:Thing .
+
diff --git a/test/validate/bad-cardinality.ttl b/test/validate/bad-cardinality.ttl
new file mode 100644
index 00000000..5300e566
--- /dev/null
+++ b/test/validate/bad-cardinality.ttl
@@ -0,0 +1,19 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:Thing
+ a rdfs:Class ;
+ rdfs:label "Thing" ;
+ rdfs:subClassOf [
+ a owl:Restriction ;
+ owl:onProperty rdf:value ;
+ owl:cardinality 3
+ ] .
+
+eg:s
+ a eg:Thing ;
+ rdf:value 1 ,
+ 2 .
+
diff --git a/test/validate/bad-class-type-undefined.ttl b/test/validate/bad-class-type-undefined.ttl
new file mode 100644
index 00000000..1e3c5eba
--- /dev/null
+++ b/test/validate/bad-class-type-undefined.ttl
@@ -0,0 +1,5 @@
+@prefix eg: <http://example.org/> .
+
+eg:s
+ a eg:Undefined .
+
diff --git a/test/validate/bad-class-type.ttl b/test/validate/bad-class-type.ttl
new file mode 100644
index 00000000..a0ddf454
--- /dev/null
+++ b/test/validate/bad-class-type.ttl
@@ -0,0 +1,9 @@
+@prefix eg: <http://example.org/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+
+eg:nonClass
+ a rdf:Bag .
+
+eg:s
+ a eg:nonClass .
+
diff --git a/test/validate/bad-datatype-cycle.ttl b/test/validate/bad-datatype-cycle.ttl
new file mode 100644
index 00000000..dd7cf0ce
--- /dev/null
+++ b/test/validate/bad-datatype-cycle.ttl
@@ -0,0 +1,18 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:Alpha
+ a rdfs:Datatype ;
+ rdfs:label "Alpha" ;
+ owl:onDatatype eg:Beta .
+
+eg:Beta
+ a rdfs:Datatype ;
+ rdfs:label "Beta" ;
+ owl:onDatatype eg:Omega .
+
+eg:Omega
+ a rdfs:Datatype ;
+ rdfs:label "Omega" ;
+ owl:onDatatype eg:Alpha .
diff --git a/test/validate/bad-datatype-property.ttl b/test/validate/bad-datatype-property.ttl
new file mode 100644
index 00000000..3c2f7a9f
--- /dev/null
+++ b/test/validate/bad-datatype-property.ttl
@@ -0,0 +1,19 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:name
+ a owl:DatatypeProperty ;
+ rdfs:label "name" .
+
+eg:Thing
+ a rdfs:Class ;
+ rdfs:label "Thing" .
+
+eg:s1
+ a eg:Thing .
+
+eg:s2
+ eg:name eg:s1 .
+
diff --git a/test/validate/bad-deprecated-class.ttl b/test/validate/bad-deprecated-class.ttl
new file mode 100644
index 00000000..51d76d8f
--- /dev/null
+++ b/test/validate/bad-deprecated-class.ttl
@@ -0,0 +1,12 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:Square
+ a rdfs:Class ;
+ owl:deprecated true ;
+ rdfs:label "Square" .
+
+eg:square
+ a eg:Square .
+
diff --git a/test/validate/bad-deprecated-property.ttl b/test/validate/bad-deprecated-property.ttl
new file mode 100644
index 00000000..b2bd392c
--- /dev/null
+++ b/test/validate/bad-deprecated-property.ttl
@@ -0,0 +1,13 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:stuff
+ a rdf:Property ;
+ owl:deprecated true ;
+ rdfs:label "stuff" .
+
+eg:s
+ eg:stuff eg:o .
+
diff --git a/test/validate/bad-disjoint-with.ttl b/test/validate/bad-disjoint-with.ttl
new file mode 100644
index 00000000..c56937f5
--- /dev/null
+++ b/test/validate/bad-disjoint-with.ttl
@@ -0,0 +1,18 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:Square
+ a owl:Class ;
+ rdfs:label "Square" ;
+ owl:disjointWith eg:Circle .
+
+eg:Circle
+ a owl:Class ;
+ rdfs:label "Circle" ;
+ owl:disjointWith eg:Square .
+
+eg:magicShape
+ a eg:Square ,
+ eg:Circle .
+
diff --git a/test/validate/bad-domain.ttl b/test/validate/bad-domain.ttl
new file mode 100644
index 00000000..52c1849c
--- /dev/null
+++ b/test/validate/bad-domain.ttl
@@ -0,0 +1,27 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:Thing
+ a rdfs:Class ;
+ rdfs:subClassOf [
+ a owl:Restriction ;
+ owl:onProperty rdf:value ;
+ owl:cardinality 1
+ ] ;
+ rdfs:label "Thing" .
+
+eg:NonThing
+ a rdfs:Class ;
+ rdfs:label "NonThing" .
+
+eg:thingName
+ a rdf:Property ;
+ rdfs:label "thing name" ;
+ rdfs:domain eg:Thing .
+
+eg:nonthing
+ a eg:NonThing ;
+ eg:thingName "nonthing" .
+
diff --git a/test/validate/bad-functional-property.ttl b/test/validate/bad-functional-property.ttl
new file mode 100644
index 00000000..53a73ccd
--- /dev/null
+++ b/test/validate/bad-functional-property.ttl
@@ -0,0 +1,13 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:identity
+ rdfs:label "identity" ;
+ a owl:FunctionalProperty .
+
+eg:s
+ eg:identity "me" ,
+ "you" .
+
diff --git a/test/validate/bad-inverse-functional-property.ttl b/test/validate/bad-inverse-functional-property.ttl
new file mode 100644
index 00000000..95c0aaea
--- /dev/null
+++ b/test/validate/bad-inverse-functional-property.ttl
@@ -0,0 +1,15 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:identity
+ rdfs:label "identity" ;
+ a owl:InverseFunctionalProperty .
+
+eg:s1
+ eg:identity "me" .
+
+eg:s2
+ eg:identity "me" .
+
diff --git a/test/validate/bad-literal-pattern.ttl b/test/validate/bad-literal-pattern.ttl
new file mode 100644
index 00000000..fda954ed
--- /dev/null
+++ b/test/validate/bad-literal-pattern.ttl
@@ -0,0 +1,22 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+eg:CapitalLiteral
+ a rdfs:Datatype ;
+ rdfs:label "Capital Literal" ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "[A-Z][a-z]*"
+ ]
+ ) .
+
+eg:value
+ a rdf:Property ;
+ rdfs:label "value" ;
+ rdfs:range eg:CapitalLiteral .
+
+eg:s
+ eg:value "lowercase"^^eg:CapitalLiteral .
diff --git a/test/validate/bad-literal-value-high-exclusive.ttl b/test/validate/bad-literal-value-high-exclusive.ttl
new file mode 100644
index 00000000..fafc74c8
--- /dev/null
+++ b/test/validate/bad-literal-value-high-exclusive.ttl
@@ -0,0 +1,20 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+eg:Normal
+ a rdfs:Datatype ;
+ rdfs:label "Normal" ;
+ owl:onDatatype xsd:double ;
+ owl:withRestrictions (
+ [
+ xsd:maxExclusive 1.0
+ ] [
+ xsd:minExclusive 0.0
+ ]
+ ) .
+
+eg:s
+ rdf:value "1.0"^^eg:Normal .
diff --git a/test/validate/bad-literal-value-high-inclusive.ttl b/test/validate/bad-literal-value-high-inclusive.ttl
new file mode 100644
index 00000000..db19f1cd
--- /dev/null
+++ b/test/validate/bad-literal-value-high-inclusive.ttl
@@ -0,0 +1,20 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+eg:Normal
+ a rdfs:Datatype ;
+ rdfs:label "Normal" ;
+ owl:onDatatype xsd:double ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 1.0
+ ] [
+ xsd:minInclusive 0.0
+ ]
+ ) .
+
+eg:s
+ rdf:value "1.1"^^eg:Normal .
diff --git a/test/validate/bad-literal-value-low-exclusive.ttl b/test/validate/bad-literal-value-low-exclusive.ttl
new file mode 100644
index 00000000..082ae9d9
--- /dev/null
+++ b/test/validate/bad-literal-value-low-exclusive.ttl
@@ -0,0 +1,20 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+eg:Normal
+ a rdfs:Datatype ;
+ rdfs:label "Normal" ;
+ owl:onDatatype xsd:double ;
+ owl:withRestrictions (
+ [
+ xsd:maxExclusive 1.0
+ ] [
+ xsd:minExclusive 0.0
+ ]
+ ) .
+
+eg:s
+ rdf:value "0.0"^^eg:Normal .
diff --git a/test/validate/bad-literal-value-low-inclusive.ttl b/test/validate/bad-literal-value-low-inclusive.ttl
new file mode 100644
index 00000000..c7203c81
--- /dev/null
+++ b/test/validate/bad-literal-value-low-inclusive.ttl
@@ -0,0 +1,21 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+eg:Normal
+ a rdfs:Datatype ;
+ rdfs:label "Normal" ;
+ owl:onDatatype xsd:double ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 1.0
+ ] [
+ xsd:minInclusive 0.0
+ ]
+ ) .
+
+eg:s
+ rdf:value "-0.1"^^eg:Normal .
+
diff --git a/test/validate/bad-literal-value.ttl b/test/validate/bad-literal-value.ttl
new file mode 100644
index 00000000..af6af40e
--- /dev/null
+++ b/test/validate/bad-literal-value.ttl
@@ -0,0 +1,7 @@
+@prefix eg: <http://example.org/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+eg:s
+ rdf:value "98765"^^xsd:byte .
+
diff --git a/test/validate/bad-object-property.ttl b/test/validate/bad-object-property.ttl
new file mode 100644
index 00000000..335db339
--- /dev/null
+++ b/test/validate/bad-object-property.ttl
@@ -0,0 +1,12 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:child
+ rdfs:label "child" ;
+ a owl:ObjectProperty .
+
+eg:s
+ eg:child "literal" .
+
diff --git a/test/validate/bad-pattern.ttl b/test/validate/bad-pattern.ttl
new file mode 100644
index 00000000..fef79aeb
--- /dev/null
+++ b/test/validate/bad-pattern.ttl
@@ -0,0 +1,23 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+eg:BrokenLiteral
+ a rdfs:Datatype ;
+ rdfs:label "Broken Literal" ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "["
+ ]
+ ) .
+
+# eg:value
+# a rdf:Property ;
+# rdfs:label "value" ;
+# rdfs:range eg:BrokenLiteral .
+
+eg:s
+ rdf:value "no match"^^eg:BrokenLiteral .
+
diff --git a/test/validate/bad-plain-literal.ttl b/test/validate/bad-plain-literal.ttl
new file mode 100644
index 00000000..c6f60c2c
--- /dev/null
+++ b/test/validate/bad-plain-literal.ttl
@@ -0,0 +1,12 @@
+@prefix eg: <http://example.org/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:value
+ a rdf:Property ;
+ rdfs:label "value" ;
+ rdfs:range rdf:PlainLiteral .
+
+eg:s
+ eg:value "typed"^^rdf:XMLLiteral .
+
diff --git a/test/validate/bad-predicate-type-undefined.ttl b/test/validate/bad-predicate-type-undefined.ttl
new file mode 100644
index 00000000..246594ee
--- /dev/null
+++ b/test/validate/bad-predicate-type-undefined.ttl
@@ -0,0 +1,5 @@
+@prefix eg: <http://example.org/> .
+
+eg:s
+ eg:undefined 0 .
+
diff --git a/test/validate/bad-predicate-type.ttl b/test/validate/bad-predicate-type.ttl
new file mode 100644
index 00000000..84163d64
--- /dev/null
+++ b/test/validate/bad-predicate-type.ttl
@@ -0,0 +1,9 @@
+@prefix eg: <http://example.org/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+
+eg:nonProperty
+ a rdf:Bag .
+
+eg:s
+ eg:nonProperty 0 .
+
diff --git a/test/validate/bad-range-instance-not-literal.ttl b/test/validate/bad-range-instance-not-literal.ttl
new file mode 100644
index 00000000..5132a70f
--- /dev/null
+++ b/test/validate/bad-range-instance-not-literal.ttl
@@ -0,0 +1,19 @@
+@prefix eg: <http://example.org/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:Thing
+ a rdfs:Class ;
+ rdfs:label "Thing" .
+
+eg:value
+ a rdf:Property ;
+ rdfs:label "value" ;
+ rdfs:range rdfs:Literal .
+
+eg:thing
+ a eg:Thing .
+
+eg:s
+ eg:value eg:thing .
+
diff --git a/test/validate/bad-range-instance.ttl b/test/validate/bad-range-instance.ttl
new file mode 100644
index 00000000..d0a08a7a
--- /dev/null
+++ b/test/validate/bad-range-instance.ttl
@@ -0,0 +1,24 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:Thing
+ a rdfs:Class ;
+ rdfs:label "Thing" ;
+ rdfs:subClassOf [
+ a owl:Restriction ;
+ owl:onProperty rdf:value ;
+ owl:minCardinality 1
+ ] .
+
+eg:value
+ a rdf:Property ;
+ rdfs:label "value" ;
+ rdfs:range eg:Thing .
+
+eg:nonthing
+ rdfs:label "not a Thing" .
+
+eg:s
+ eg:value eg:nonthing .
diff --git a/test/validate/bad-range-literal-not-instance.ttl b/test/validate/bad-range-literal-not-instance.ttl
new file mode 100644
index 00000000..039fcea8
--- /dev/null
+++ b/test/validate/bad-range-literal-not-instance.ttl
@@ -0,0 +1,16 @@
+@prefix eg: <http://example.org/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:Thing
+ a rdfs:Class ;
+ rdfs:label "Thing" .
+
+eg:value
+ a rdf:Property ;
+ rdfs:label "value" ;
+ rdfs:range eg:Thing .
+
+eg:s
+ eg:value "literal" .
+
diff --git a/test/validate/bad-range-literal.ttl b/test/validate/bad-range-literal.ttl
new file mode 100644
index 00000000..d656aa16
--- /dev/null
+++ b/test/validate/bad-range-literal.ttl
@@ -0,0 +1,27 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+eg:Normal
+ a rdfs:Datatype ;
+ rdfs:label "Normal" ;
+ owl:onDatatype xsd:double ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 1.0
+ ] [
+ xsd:minInclusive 0.0
+ ]
+ ) .
+
+eg:scaled
+ a rdf:Property ;
+ rdfs:label "scaled" ;
+ rdfs:range eg:Normal .
+
+eg:s
+ eg:scaled 2.0 .
+
+
diff --git a/test/validate/bad-some-values-from.ttl b/test/validate/bad-some-values-from.ttl
new file mode 100644
index 00000000..9a8ee849
--- /dev/null
+++ b/test/validate/bad-some-values-from.ttl
@@ -0,0 +1,19 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:Thing
+ a rdfs:Class ;
+ rdfs:label "Thing" ;
+ rdfs:subClassOf [
+ a owl:Restriction ;
+ owl:onProperty rdfs:label ;
+ owl:someValuesFrom rdf:PlainLiteral
+ ] .
+
+eg:s
+ a eg:Thing ;
+ rdfs:label "not plain"^^rdf:XMLLiteral .
+
+
diff --git a/test/validate/bad-string-literal-value-high.ttl b/test/validate/bad-string-literal-value-high.ttl
new file mode 100644
index 00000000..93e675ef
--- /dev/null
+++ b/test/validate/bad-string-literal-value-high.ttl
@@ -0,0 +1,21 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+eg:startsWithC
+ a rdfs:Datatype ;
+ rdfs:label "Starts With C" ;
+ owl:onDatatype xsd:string ;
+ owl:withRestrictions (
+ [
+ xsd:maxExclusive "D"
+ ] [
+ xsd:minInclusive "B"
+ ]
+ ) .
+
+eg:s
+ rdf:value "Door"^^eg:startsWithC .
+
diff --git a/test/validate/bad-string-literal-value-low.ttl b/test/validate/bad-string-literal-value-low.ttl
new file mode 100644
index 00000000..2b6985f8
--- /dev/null
+++ b/test/validate/bad-string-literal-value-low.ttl
@@ -0,0 +1,21 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+eg:betweenBAndD
+ a rdfs:Datatype ;
+ rdfs:label "Between B and D" ;
+ owl:onDatatype xsd:string ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive "D"
+ ] [
+ xsd:minExclusive "B"
+ ]
+ ) .
+
+eg:s
+ rdf:value "Aardvark"^^eg:betweenBAndD .
+
diff --git a/test/validate/bad-subclass-cycle.ttl b/test/validate/bad-subclass-cycle.ttl
new file mode 100644
index 00000000..1e702832
--- /dev/null
+++ b/test/validate/bad-subclass-cycle.ttl
@@ -0,0 +1,13 @@
+@prefix eg: <http://example.org/> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:Square
+ a rdfs:Class ;
+ rdfs:subClassOf eg:Rectangle ;
+ rdfs:label "Square" .
+
+eg:Rectangle
+ a rdfs:Class ;
+ rdfs:subClassOf eg:Square ;
+ rdfs:label "Rectangle" .
+
diff --git a/test/validate/bad-subproperty-cycle.ttl b/test/validate/bad-subproperty-cycle.ttl
new file mode 100644
index 00000000..eb3bbee4
--- /dev/null
+++ b/test/validate/bad-subproperty-cycle.ttl
@@ -0,0 +1,14 @@
+@prefix eg: <http://example.org/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:stuff
+ a rdf:Property ;
+ rdfs:subPropertyOf eg:things ;
+ rdfs:label "stuff" .
+
+eg:things
+ a rdf:Property ;
+ rdfs:subPropertyOf eg:stuff ;
+ rdfs:label "things" .
+
diff --git a/test/validate/bad-superclass-restriction.ttl b/test/validate/bad-superclass-restriction.ttl
new file mode 100644
index 00000000..bd820de4
--- /dev/null
+++ b/test/validate/bad-superclass-restriction.ttl
@@ -0,0 +1,22 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:SuperThing
+ a rdfs:Class ;
+ rdfs:label "SuperThing" ;
+ rdfs:subClassOf [
+ a owl:Restriction ;
+ owl:onProperty rdf:value ;
+ owl:minCardinality 1
+ ] .
+
+eg:Thing
+ a rdfs:Class ;
+ rdfs:label "Thing" ;
+ rdfs:subClassOf eg:SuperThing .
+
+eg:s
+ a eg:Thing .
+
diff --git a/test/validate/bad-union-of.ttl b/test/validate/bad-union-of.ttl
new file mode 100644
index 00000000..0198ce1d
--- /dev/null
+++ b/test/validate/bad-union-of.ttl
@@ -0,0 +1,46 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+eg:index
+ a rdf:Property ;
+ rdfs:label "index" .
+
+eg:name
+ a rdf:Property ;
+ rdfs:label "name" .
+
+eg:ThingWithIndex
+ a rdfs:Class ;
+ rdfs:label "Thing With Index" ;
+ rdfs:subClassOf [
+ a owl:Restriction ;
+ owl:onProperty eg:index ;
+ owl:minCardinality 1
+ ] .
+
+eg:ThingWithName
+ a rdfs:Class ;
+ rdfs:label "Thing With Name" ;
+ rdfs:subClassOf [
+ a owl:Restriction ;
+ owl:onProperty eg:name ;
+ owl:minCardinality 1
+ ] .
+
+eg:something
+ a rdf:Property ;
+ rdfs:label "something" ;
+ rdfs:range [
+ owl:unionOf (
+ eg:ThingWithIndex
+ eg:ThingWithName
+ )
+ ] .
+
+eg:s1
+ eg:something [
+ rdfs:label "s1"
+ ] .
diff --git a/test/validate/bad-unknown-datatype.ttl b/test/validate/bad-unknown-datatype.ttl
new file mode 100644
index 00000000..ed879414
--- /dev/null
+++ b/test/validate/bad-unknown-datatype.ttl
@@ -0,0 +1,7 @@
+@prefix eg: <http://example.org/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:s
+ rdfs:label "bad datatype"^^rdf:UndefinedLiteral .
+
diff --git a/test/validate/good-anyuri.ttl b/test/validate/good-anyuri.ttl
new file mode 100644
index 00000000..e05f8b71
--- /dev/null
+++ b/test/validate/good-anyuri.ttl
@@ -0,0 +1,13 @@
+@prefix eg: <http://example.org/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+eg:uri
+ a rdf:Property ;
+ rdfs:label "uri" ;
+ rdfs:range xsd:anyURI .
+
+eg:s
+ eg:uri <http://example.org> .
+
diff --git a/test/validate/good-cardinality.ttl b/test/validate/good-cardinality.ttl
new file mode 100644
index 00000000..74615fdc
--- /dev/null
+++ b/test/validate/good-cardinality.ttl
@@ -0,0 +1,18 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:Thing
+ a rdfs:Class ;
+ rdfs:label "Thing" ;
+ rdfs:subClassOf [
+ a owl:Restriction ;
+ owl:onProperty rdf:value ;
+ owl:cardinality 2
+ ] .
+
+eg:s
+ a eg:Thing ;
+ rdf:value 1 ,
+ 2 .
diff --git a/test/validate/good-literal-value-high-inclusive.ttl b/test/validate/good-literal-value-high-inclusive.ttl
new file mode 100644
index 00000000..18cbed0e
--- /dev/null
+++ b/test/validate/good-literal-value-high-inclusive.ttl
@@ -0,0 +1,21 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+eg:Normal
+ a rdfs:Datatype ;
+ rdfs:label "Normal" ;
+ owl:onDatatype xsd:double ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 1.0
+ ]
+ [
+ xsd:minInclusive 0.0
+ ]
+ ) .
+
+eg:s
+ rdf:value "1.0"^^eg:Normal .
diff --git a/test/validate/good-literal-value-low-inclusive.ttl b/test/validate/good-literal-value-low-inclusive.ttl
new file mode 100644
index 00000000..e6ad334c
--- /dev/null
+++ b/test/validate/good-literal-value-low-inclusive.ttl
@@ -0,0 +1,21 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+eg:Normal
+ a rdfs:Datatype ;
+ rdfs:label "Normal" ;
+ owl:onDatatype xsd:double ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 1.0
+ ]
+ [
+ xsd:minInclusive 0.0
+ ]
+ ) .
+
+eg:s
+ rdf:value "0.0"^^eg:Normal .
diff --git a/test/validate/good-owl-thing.ttl b/test/validate/good-owl-thing.ttl
new file mode 100644
index 00000000..9c4b570d
--- /dev/null
+++ b/test/validate/good-owl-thing.ttl
@@ -0,0 +1,16 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:someThing
+ a rdf:Property ;
+ rdfs:label "some thing" ;
+ rdfs:range owl:Thing .
+
+eg:thisThing
+ rdfs:label "this thing" .
+
+eg:s
+ eg:someThing eg:thisThing .
+
diff --git a/test/validate/good-pattern.ttl b/test/validate/good-pattern.ttl
new file mode 100644
index 00000000..740ec22c
--- /dev/null
+++ b/test/validate/good-pattern.ttl
@@ -0,0 +1,22 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+eg:CapitalLiteral
+ a rdfs:Datatype ;
+ rdfs:label "Capital Literal" ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "[A-Z][a-z]*"
+ ]
+ ) .
+
+eg:value
+ a rdf:Property ;
+ rdfs:label "value" ;
+ rdfs:range eg:CapitalLiteral .
+
+eg:s
+ eg:value "Uppercase"^^eg:CapitalLiteral .
diff --git a/test/validate/good-rdfs-resource.ttl b/test/validate/good-rdfs-resource.ttl
new file mode 100644
index 00000000..26310553
--- /dev/null
+++ b/test/validate/good-rdfs-resource.ttl
@@ -0,0 +1,12 @@
+@prefix eg: <http://example.org/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:resource
+ a rdf:Property ;
+ rdfs:label "resource" ;
+ rdfs:range rdfs:Resource .
+
+eg:s
+ eg:resource <http://example.org> .
+
diff --git a/test/validate/good-some-values-from.ttl b/test/validate/good-some-values-from.ttl
new file mode 100644
index 00000000..23f977fd
--- /dev/null
+++ b/test/validate/good-some-values-from.ttl
@@ -0,0 +1,18 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:Thing
+ a rdfs:Class ;
+ rdfs:label "Thing" ;
+ rdfs:subClassOf [
+ a owl:Restriction ;
+ owl:onProperty rdfs:label ;
+ owl:someValuesFrom rdf:PlainLiteral
+ ] .
+
+eg:s
+ a eg:Thing ;
+ rdfs:label "not plain"^^rdf:XMLLiteral ,
+ "plain" .
diff --git a/test/validate/good-string-literal-value-low.ttl b/test/validate/good-string-literal-value-low.ttl
new file mode 100644
index 00000000..7d71856b
--- /dev/null
+++ b/test/validate/good-string-literal-value-low.ttl
@@ -0,0 +1,21 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+eg:betweenBAndD
+ a rdfs:Datatype ;
+ rdfs:label "Between B and D" ;
+ owl:onDatatype xsd:string ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive "D"
+ ]
+ [
+ xsd:minExclusive "B"
+ ]
+ ) .
+
+eg:s
+ rdf:value "Cat"^^eg:betweenBAndD .
diff --git a/test/validate/good-union-of.ttl b/test/validate/good-union-of.ttl
new file mode 100644
index 00000000..67901f94
--- /dev/null
+++ b/test/validate/good-union-of.ttl
@@ -0,0 +1,51 @@
+@prefix eg: <http://example.org/> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+eg:index
+ a rdf:Property ;
+ rdfs:label "index" .
+
+eg:name
+ a rdf:Property ;
+ rdfs:label "name" .
+
+eg:ThingWithIndex
+ a rdfs:Class ;
+ rdfs:label "Thing With Index" ;
+ rdfs:subClassOf [
+ a owl:Restriction ;
+ owl:onProperty eg:index ;
+ owl:minCardinality 1
+ ] .
+
+eg:ThingWithName
+ a rdfs:Class ;
+ rdfs:label "Thing With Name" ;
+ rdfs:subClassOf [
+ a owl:Restriction ;
+ owl:onProperty eg:name ;
+ owl:minCardinality 1
+ ] .
+
+eg:something
+ a rdf:Property ;
+ rdfs:label "something" ;
+ rdfs:range [
+ owl:unionOf (
+ eg:ThingWithIndex
+ eg:ThingWithName
+ )
+ ] .
+
+eg:s1
+ eg:something [
+ eg:index 42
+ ] .
+
+eg:s2
+ eg:something [
+ eg:name "ess"
+ ] .
diff --git a/test/validate/manifest.ttl b/test/validate/manifest.ttl
new file mode 100644
index 00000000..c2e9988a
--- /dev/null
+++ b/test/validate/manifest.ttl
@@ -0,0 +1,362 @@
+@prefix checks: <http://drobilla.net/ns/serd/checks#> .
+@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix rdft: <http://www.w3.org/ns/rdftest#> .
+@prefix serd: <http://drobilla.net/ns/serd#> .
+
+rdft:Test
+ a rdfs:Class ;
+ rdfs:subClassOf mf:ManifestEntry .
+
+serd:TestTurtleNegativeValidate
+ a rdfs:Class ;
+ rdfs:label "Turtle Negative Validation" ;
+ rdfs:subClassOf rdft:Test ,
+ [
+ a owl:Restriction ;
+ owl:onProperty serd:triggersCheck ;
+ owl:minCardinality 1
+ ] .
+
+serd:TestTurtlePositiveValidate
+ a rdfs:Class ;
+ rdfs:label "Turtle Positive Validation" ;
+ rdfs:subClassOf rdft:Test .
+
+serd:triggersCheck
+ a rdf:Property ;
+ rdfs:label "triggers check" ;
+ rdfs:range serd:ValidatorCheck .
+
+serd:triggersCheck
+ a rdf:Property ;
+ rdfs:label "triggers check" ;
+ rdfs:range serd:ValidatorCheck .
+
+<>
+ a mf:Manifest ;
+ rdfs:comment "Serd validation test cases" ;
+ mf:entries (
+ <#bad-all-values-from>
+ <#bad-anyuri>
+ <#bad-cardinality-high>
+ <#bad-cardinality-low>
+ <#bad-cardinality>
+ <#bad-datatype-cycle>
+ <#bad-datatype-property>
+ <#bad-deprecated-class>
+ <#bad-deprecated-property>
+ <#bad-domain>
+ <#bad-functional-property>
+ <#bad-inverse-functional-property>
+ <#bad-literal-pattern>
+ <#bad-literal-value-high-exclusive>
+ <#bad-literal-value-high-inclusive>
+ <#bad-literal-value-low-exclusive>
+ <#bad-literal-value-low-inclusive>
+ <#bad-literal-value>
+ <#bad-object-property>
+ <#bad-pattern>
+ <#bad-plain-literal>
+ <#bad-predicate-type-undefined>
+ <#bad-predicate-type>
+ <#bad-range-instance-not-literal>
+ <#bad-range-instance>
+ <#bad-range-literal-not-instance>
+ <#bad-range-literal>
+ <#bad-some-values-from>
+ <#bad-string-literal-value-high>
+ <#bad-string-literal-value-low>
+ <#bad-subclass-cycle>
+ <#bad-subproperty-cycle>
+ <#bad-superclass-restriction>
+ <#bad-union-of>
+ <#bad-unknown-datatype>
+ <#good-anyuri>
+ <#good-cardinality>
+ <#good-literal-value-high-inclusive>
+ <#good-literal-value-low-inclusive>
+ <#good-owl-thing>
+ <#good-pattern>
+ <#good-rdfs-resource>
+ <#good-some-values-from>
+ <#good-string-literal-value-low>
+ <#good-union-of>
+ ) .
+
+<#bad-all-values-from>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:allValuesFrom ,
+ checks:instanceType ;
+ serd:triggersCheck checks:literalPattern ;
+ serd:triggersCheck checks:literalRestriction ;
+ mf:name "bad-all-values-from" ;
+ mf:action <bad-all-values-from.ttl> .
+
+<#bad-anyuri>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:anyUri ,
+ checks:propertyRange ;
+ mf:name "bad-anyuri" ;
+ mf:action <bad-anyuri.ttl> .
+
+<#bad-cardinality-low>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:cardinalityMin ,
+ checks:instanceType ;
+ mf:name "bad-cardinality-low" ;
+ mf:action <bad-cardinality-low.ttl> .
+
+<#bad-cardinality-high>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:cardinalityMax ,
+ checks:instanceType ;
+ mf:name "bad-cardinality-high" ;
+ mf:action <bad-cardinality-high.ttl> .
+
+<#bad-cardinality>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:cardinalityEqual ,
+ checks:instanceType ;
+ mf:name "bad-cardinality" ;
+ mf:action <bad-cardinality.ttl> .
+
+<#bad-datatype-cycle>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:datatypeCycle ;
+ mf:name "bad-datatype-cycle" ;
+ mf:action <bad-datatype-cycle.ttl> .
+
+<#bad-datatype-property>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:datatypeProperty ;
+ mf:name "bad-datatype-property" ;
+ mf:action <bad-datatype-property.ttl> .
+
+<#bad-deprecated-class>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:deprecatedClass ;
+ mf:name "bad-deprecated-class" ;
+ mf:action <bad-deprecated-class.ttl> .
+
+<#bad-deprecated-property>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:deprecatedProperty ;
+ mf:name "bad-deprecated-property" ;
+ mf:action <bad-deprecated-property.ttl> .
+
+<#bad-domain>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:cardinalityEqual ,
+ checks:propertyDomain ;
+ mf:name "bad-domain" ;
+ mf:action <bad-domain.ttl> .
+
+<#bad-functional-property>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:functionalProperty ;
+ mf:name "bad-functional-property" ;
+ mf:action <bad-functional-property.ttl> .
+
+<#bad-inverse-functional-property>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:inverseFunctionalProperty ;
+ mf:name "bad-inverse-functional-property" ;
+ mf:action <bad-inverse-functional-property.ttl> .
+
+<#bad-literal-pattern>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:literalPattern ;
+ mf:name "bad-literal-pattern" ;
+ mf:action <bad-literal-pattern.ttl> .
+
+<#bad-literal-value-low-inclusive>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:literalMinInclusive ;
+ mf:name "bad-literal-value-low-inclusive" ;
+ mf:action <bad-literal-value-low-inclusive.ttl> .
+
+<#bad-literal-value-high-inclusive>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:literalMaxInclusive ;
+ mf:name "bad-literal-value-high-inclusive" ;
+ mf:action <bad-literal-value-high-inclusive.ttl> .
+
+<#bad-literal-value-low-exclusive>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:literalMinExclusive ;
+ mf:name "bad-literal-value-low-exclusive" ;
+ mf:action <bad-literal-value-low-exclusive.ttl> .
+
+<#bad-literal-value-high-exclusive>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:literalMaxExclusive ;
+ mf:name "bad-literal-value-high-exclusive" ;
+ mf:action <bad-literal-value-high-exclusive.ttl> .
+
+<#bad-literal-value>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:literalValue ;
+ mf:name "bad-literal-value" ;
+ mf:action <bad-literal-value.ttl> .
+
+<#bad-string-literal-value-low>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:literalMinExclusive ;
+ mf:name "bad-string-literal-value-low" ;
+ mf:action <bad-string-literal-value-low.ttl> .
+
+<#bad-string-literal-value-high>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:literalMaxExclusive ;
+ mf:name "bad-string-literal-value-high" ;
+ mf:action <bad-string-literal-value-high.ttl> .
+
+<#bad-subclass-cycle>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:classCycle ;
+ mf:name "bad-subclass-cycle" ;
+ mf:action <bad-subclass-cycle.ttl> .
+
+<#bad-subproperty-cycle>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:propertyCycle ;
+ mf:name "bad-subproperty-cycle" ;
+ mf:action <bad-subproperty-cycle.ttl> .
+
+<#bad-superclass-restriction>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:instanceType ,
+ checks:cardinalityMin ;
+ mf:name "bad-superclass-restriction" ;
+ mf:action <bad-superclass-restriction.ttl> .
+
+<#bad-object-property>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:objectProperty ;
+ mf:name "bad-object-property" ;
+ mf:action <bad-object-property.ttl> .
+
+<#bad-pattern>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:literalPattern ;
+ mf:name "bad-pattern" ;
+ mf:action <bad-pattern.ttl> .
+
+<#bad-plain-literal>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:plainLiteralDatatype ,
+ checks:propertyRange ;
+ mf:name "bad-plain-literal" ;
+ mf:action <bad-plain-literal.ttl> .
+
+<#bad-range-instance-not-literal>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:instanceLiteral ,
+ checks:propertyRange ;
+ mf:name "bad-range-instance-not-literal" ;
+ mf:action <bad-range-instance-not-literal.ttl> .
+
+<#bad-range-instance>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:cardinalityMin ,
+ checks:propertyRange ;
+ mf:name "bad-range-instance" ;
+ mf:action <bad-range-instance.ttl> .
+
+<#bad-range-literal-not-instance>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:literalInstance ,
+ checks:propertyRange ;
+ mf:name "bad-range-literal-not-instance" ;
+ mf:action <bad-range-literal-not-instance.ttl> .
+
+<#bad-range-literal>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:literalMaxInclusive ,
+ checks:propertyRange ;
+ mf:name "bad-range-literal" ;
+ mf:action <bad-range-literal.ttl> .
+
+<#bad-some-values-from>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:someValuesFrom ,
+ checks:instanceType ;
+ mf:name "bad-some-values-from" ;
+ mf:action <bad-some-values-from.ttl> .
+
+<#bad-union-of>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:propertyRange ;
+ mf:name "bad-union-of" ;
+ mf:action <bad-union-of.ttl> .
+
+<#bad-unknown-datatype>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:datatypeType ;
+ mf:name "bad-unknown-datatype" ;
+ mf:action <bad-unknown-datatype.ttl> .
+
+<#bad-predicate-type-undefined>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:predicateType ;
+ mf:name "bad-predicate-type-undefined" ;
+ mf:action <bad-predicate-type-undefined.ttl> .
+
+<#bad-predicate-type>
+ a serd:TestTurtleNegativeValidate ;
+ serd:triggersCheck checks:predicateType ;
+ mf:name "bad-predicate-type" ;
+ mf:action <bad-predicate-type.ttl> .
+
+<#good-anyuri>
+ a serd:TestTurtlePositiveValidate ;
+ mf:name "good-anyuri" ;
+ mf:action <good-anyuri.ttl> .
+
+<#good-cardinality>
+ a serd:TestTurtlePositiveValidate ;
+ mf:name "good-cardinality" ;
+ mf:action <good-cardinality.ttl> .
+
+<#good-literal-value-low-inclusive>
+ a serd:TestTurtlePositiveValidate ;
+ mf:name "good-literal-value-low-inclusive" ;
+ mf:action <good-literal-value-low-inclusive.ttl> .
+
+<#good-literal-value-high-inclusive>
+ a serd:TestTurtlePositiveValidate ;
+ mf:name "good-literal-value-high-inclusive" ;
+ mf:action <good-literal-value-high-inclusive.ttl> .
+
+<#good-some-values-from>
+ a serd:TestTurtlePositiveValidate ;
+ mf:name "good-some-values-from" ;
+ mf:action <good-some-values-from.ttl> .
+
+<#good-owl-thing>
+ a serd:TestTurtlePositiveValidate ;
+ mf:name "good-owl-thing" ;
+ mf:action <good-owl-thing.ttl> .
+
+<#good-pattern>
+ a serd:TestTurtlePositiveValidate ;
+ mf:name "good-pattern" ;
+ mf:action <good-pattern.ttl> .
+
+<#good-rdfs-resource>
+ a serd:TestTurtlePositiveValidate ;
+ mf:name "good-rdfs-resource" ;
+ mf:action <good-rdfs-resource.ttl> .
+
+<#good-string-literal-value-low>
+ a serd:TestTurtlePositiveValidate ;
+ mf:name "good-string-literal-value-low" ;
+ mf:action <good-string-literal-value-low.ttl> .
+
+<#good-union-of>
+ a serd:TestTurtlePositiveValidate ;
+ mf:name "good-union-of" ;
+ mf:action <good-union-of.ttl> .
diff --git a/tools/meson.build b/tools/meson.build
index d4964784..da29c6dc 100644
--- a/tools/meson.build
+++ b/tools/meson.build
@@ -5,12 +5,6 @@ if get_option('static')
tool_link_args += ['-static']
endif
-tools = [
- 'filter',
- 'pipe',
- 'sort',
-]
-
serd_filter = executable('serd-filter',
['serd-filter.c', 'console.c'],
c_args: tool_c_args,
@@ -31,3 +25,10 @@ serd_sort = executable('serd-sort',
link_args: tool_link_args,
install: true,
dependencies: serd_dep)
+
+serd_validate = executable('serd-validate',
+ ['serd-validate.c', 'console.c'],
+ c_args: tool_c_args,
+ link_args: tool_link_args,
+ install: true,
+ dependencies: serd_dep)
diff --git a/tools/serd-validate.c b/tools/serd-validate.c
new file mode 100644
index 00000000..fd203611
--- /dev/null
+++ b/tools/serd-validate.c
@@ -0,0 +1,479 @@
+/*
+ Copyright 2011-2022 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include "console.h"
+
+#include "serd/serd.h"
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define NS_OWL "http://www.w3.org/2002/07/owl#"
+#define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+#define NS_RDFS "http://www.w3.org/2000/01/rdf-schema#"
+
+/* Application (after parsing command-line arguments) */
+
+#define SERDI_ERROR(msg) fprintf(stderr, "serdi: " msg)
+#define SERDI_ERRORF(fmt, ...) fprintf(stderr, "serdi: " fmt, __VA_ARGS__)
+
+typedef struct {
+ const char* base_uri_string;
+ const char* out_filename;
+ char* const* inputs;
+ intptr_t n_inputs;
+ SerdSyntaxOptions input_options;
+ size_t block_size;
+ size_t stack_size;
+ bool verbose;
+ bool quiet;
+} Options;
+
+static SerdStatus
+consume_source(SerdWorld* const world,
+ const Options opts,
+ SerdSyntax syntax,
+ SerdEnv* const env,
+ const SerdSink* const sink,
+ SerdInputStream input,
+ const SerdNode* const name)
+{
+ if (!input.stream) {
+ return SERD_UNKNOWN_ERROR;
+ }
+
+ SerdStatus st = SERD_SUCCESS;
+ SerdReader* const reader = serd_reader_new(
+ world, syntax, opts.input_options.flags, env, sink, opts.stack_size);
+
+ if (!(st = serd_reader_start(reader, &input, name, opts.block_size))) {
+ st = serd_reader_read_document(reader);
+ }
+
+ serd_reader_free(reader);
+ serd_close_input(&input);
+ return st;
+}
+
+static SerdStatus
+read_file(SerdWorld* const world,
+ const Options opts,
+ SerdEnv* const env,
+ const SerdSink* const sink,
+ const char* const filename)
+{
+ SerdStatus st = SERD_SUCCESS;
+ if (!opts.base_uri_string && strcmp(filename, "-")) {
+ if ((st = serd_set_base_uri_from_path(env, filename))) {
+ SERDI_ERRORF("failed to determine base URI from path %s\n", filename);
+ return st;
+ }
+ }
+
+ const SerdNode* const name =
+ serd_nodes_string(serd_world_nodes(world), SERD_STRING(filename));
+
+ st = consume_source(
+ world,
+ opts,
+ serd_choose_syntax(world, opts.input_options, filename, SERD_TRIG),
+ env,
+ sink,
+ serd_open_tool_input(filename),
+ name);
+
+ return st;
+}
+
+static SerdEnv*
+build_env(SerdWorld* const world, Options opts)
+{
+ char* const* const inputs = opts.inputs;
+ const intptr_t n_inputs = opts.n_inputs;
+
+ if (!opts.base_uri_string && n_inputs == 1) {
+ // Choose base URI from the single input path
+ char* const input_path = serd_canonical_path(NULL, inputs[0]);
+
+ SerdNode* base =
+ input_path
+ ? serd_new_file_uri(NULL, SERD_STRING(input_path), SERD_EMPTY_STRING())
+ : NULL;
+ if (!base) {
+ SERDI_ERRORF("unable to determine base URI from path %s\n", inputs[0]);
+ }
+
+ SerdEnv* const env = serd_env_new(
+ world, base ? serd_node_string_view(base) : SERD_EMPTY_STRING());
+
+ serd_free(NULL, input_path);
+ serd_node_free(NULL, base);
+ return env;
+ }
+
+ return serd_env_new(world,
+ opts.base_uri_string ? SERD_STRING(opts.base_uri_string)
+ : SERD_EMPTY_STRING());
+}
+
+static SerdModel*
+build_model(SerdWorld* const world, const Options opts, bool with_graphs)
+{
+ (void)opts; // FIXME
+
+ SerdModel* const model = serd_model_new(
+ world,
+ with_graphs ? SERD_ORDER_GSPO : SERD_ORDER_SPO,
+ (with_graphs * (unsigned)SERD_STORE_GRAPHS) | SERD_STORE_CARETS);
+
+ with_graphs = true; // FIXME
+
+ if (with_graphs) {
+ serd_model_add_index(model, SERD_ORDER_GSPO);
+ }
+
+ serd_model_add_index(model, SERD_ORDER_POS);
+ serd_model_add_index(model, SERD_ORDER_GPOS);
+
+ serd_model_add_index(model, SERD_ORDER_PSO);
+ serd_model_add_index(model, SERD_ORDER_GPSO);
+
+ serd_model_add_index(model, SERD_ORDER_OPS);
+ if (with_graphs) {
+ serd_model_add_index(model, SERD_ORDER_GOPS);
+ }
+
+ return model;
+}
+
+static bool
+input_has_graphs(const Options opts)
+{
+ if (opts.input_options.syntax) {
+ return serd_syntax_has_graphs(opts.input_options.syntax);
+ }
+
+ for (intptr_t i = 0u; i < opts.n_inputs; ++i) {
+ if (serd_syntax_has_graphs(serd_guess_syntax(opts.inputs[i]))) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static SerdStatus
+read_inputs(SerdWorld* world,
+ const Options opts,
+ SerdEnv* env,
+ const SerdSink* const sink)
+{
+ SerdStatus st = SERD_SUCCESS;
+
+ size_t prefix_len = 0;
+ char* prefix = NULL;
+ if (opts.n_inputs > 1) {
+ prefix_len = 32; // FIXME
+ prefix = (char*)calloc(1, prefix_len);
+ }
+
+ for (intptr_t i = 0; !st && i < opts.n_inputs; ++i) {
+ if (opts.n_inputs > 1) {
+ snprintf(prefix, prefix_len, "f%" PRIdPTR, i);
+ }
+
+ st = read_file(world, opts, env, sink, opts.inputs[i]);
+ }
+
+ free(prefix);
+ return st;
+}
+
+/*
+ Return a model where every object is the file URI of a schema to load.
+
+ The statements in the result are like `?ontology rdfs:seeAlso ?resource`,
+ where `?ontology` is the URI of the owl:Ontology instance and `?resource` is
+ a file URI.
+*/
+static SerdModel*
+get_schemas_model(const Options opts,
+ SerdWorld* const world,
+ SerdModel* const model)
+{
+ static const SerdStringView s_rdf_type = SERD_STRING(NS_RDF "type");
+ static const SerdStringView s_owl_Ontology = SERD_STRING(NS_OWL "Ontology");
+ static const SerdStringView s_rdfs_seeAlso = SERD_STRING(NS_RDFS "seeAlso");
+
+ SerdNodes* const nodes = serd_world_nodes(world);
+ SerdModel* const schemas_model =
+ serd_model_new(world, SERD_ORDER_SPO, SERD_STORE_CARETS);
+
+ const SerdNode* const rdf_type = serd_nodes_uri(nodes, s_rdf_type);
+ const SerdNode* const owl_Ontology = serd_nodes_uri(nodes, s_owl_Ontology);
+ const SerdNode* const rdfs_seeAlso = serd_nodes_uri(nodes, s_rdfs_seeAlso);
+
+ SerdCursor* const i =
+ serd_model_find(model, NULL, rdf_type, owl_Ontology, NULL);
+
+ for (; !serd_cursor_is_end(i); serd_cursor_advance(i)) {
+ const SerdStatement* const typing = serd_cursor_get(i);
+ const SerdNode* const ontology = serd_statement_subject(typing);
+
+ const SerdStatement* const link =
+ serd_model_get_statement(model, ontology, rdfs_seeAlso, NULL, NULL);
+ if (link) {
+ const SerdNode* const resource = serd_statement_object(link);
+ if (resource && serd_node_type(resource) == SERD_URI) {
+ if (opts.verbose) {
+ serd_logf_at(world,
+ SERD_LOG_LEVEL_NOTICE,
+ serd_statement_caret(link),
+ "Loading %s",
+ serd_node_string(resource));
+ }
+
+ const char* const resource_uri = serd_node_string(resource);
+ if (!strncmp(resource_uri, "file://", strlen("file://"))) {
+ serd_model_add(schemas_model, ontology, rdfs_seeAlso, resource, NULL);
+ }
+ }
+ }
+ }
+
+ serd_cursor_free(i);
+
+ return schemas_model;
+}
+
+static SerdStatus
+run(Options opts, int argc, char** argv)
+{
+ const bool with_graphs = input_has_graphs(opts);
+
+ SerdOutputStream out = serd_open_tool_output(opts.out_filename);
+ if (!out.stream) {
+ perror("error opening output file");
+ return SERD_UNKNOWN_ERROR;
+ }
+
+ SerdWorld* const world = serd_world_new(NULL);
+
+ const SerdNode* const schema_graph =
+ serd_nodes_uri(serd_world_nodes(world),
+ SERD_STRING("http://drobilla.net/sw/serd#schemas"));
+
+ const SerdNode* const data_graph = serd_nodes_uri(
+ serd_world_nodes(world), SERD_STRING("http://drobilla.net/sw/serd#data"));
+
+ SerdEnv* const env = build_env(world, opts);
+ SerdModel* const model = build_model(world, opts, with_graphs);
+ SerdSink* const schema_sink = serd_inserter_new(model, schema_graph);
+ SerdSink* const data_sink = serd_inserter_new(model, data_graph);
+ if (!schema_sink || !data_sink) {
+ SERDI_ERROR("failed to construct data pipeline, aborting\n");
+ return SERD_BAD_ARG; // FIXME: ?
+ }
+
+ if (opts.quiet) {
+ serd_set_log_func(world, serd_quiet_log_func, NULL);
+ }
+
+ SerdStatus st = read_inputs(world, opts, env, data_sink);
+
+ if (st <= SERD_FAILURE) { // FIXME: ?
+ SerdValidator* const validator = serd_validator_new(world);
+ bool checks_given = false;
+
+ for (int i = 1; i < argc; ++i) {
+ if (argv[i][0] == '-') {
+ if (argv[i][1] == 'W') {
+ serd_validator_enable_checks(validator, argv[++i]);
+ checks_given = true;
+ } else if (argv[i][1] == 'X') {
+ serd_validator_disable_checks(validator, argv[++i]);
+ checks_given = true;
+ } else if (argv[i][1] == 's') {
+ st = read_file(world, opts, env, schema_sink, argv[++i]);
+ }
+ }
+ }
+
+ if (!checks_given) {
+ serd_validator_enable_checks(validator, "all");
+ }
+
+ {
+ SerdModel* const schemas_model = get_schemas_model(opts, world, model);
+
+ SerdCursor* const i = serd_model_begin(schemas_model);
+ for (; !serd_cursor_is_end(i); serd_cursor_advance(i)) {
+ const SerdStatement* const link = serd_cursor_get(i);
+ const SerdNode* const resource = serd_statement_object(link);
+ const char* const resource_uri = serd_node_string(resource);
+
+ char* const path = serd_parse_file_uri(NULL, resource_uri, NULL);
+ if (path) {
+ st = read_file(world, opts, env, schema_sink, path);
+ serd_free(NULL, path);
+ }
+ }
+
+ serd_cursor_free(i);
+ serd_model_free(schemas_model);
+ }
+
+ st = serd_validate(validator, model, data_graph, env);
+
+ serd_validator_free(validator);
+ }
+
+ serd_sink_free(data_sink);
+ serd_sink_free(schema_sink);
+ serd_model_free(model);
+ serd_env_free(env);
+ serd_world_free(world);
+
+ if (serd_close_output(&out)) {
+ perror("serdi: write error");
+ st = SERD_UNKNOWN_ERROR;
+ }
+
+ return st;
+}
+
+/* Command-line interface (before setting up serd) */
+
+static SerdStatus
+print_usage(const char* const name, const bool error)
+{
+ static const char* const description =
+ "Validate RDF data against RDFS and OWL schemas.\n"
+ "INPUT can be a local filename, or \"-\" to read from standard input.\n\n"
+ " -B BASE_URI Base URI or path for resolving relative references.\n"
+ " -I SYNTAX Input syntax (turtle/ntriples/trig/nquads),\n"
+ " or option (lax/variables/relative/global/generated).\n"
+ " -V Display version information and exit.\n"
+ " -W CHECKS Enable checks matching regex CHECKS (or \"all\").\n"
+ " -X CHECKS Exclude checks matching regex CHECKS (or \"all\").\n"
+ " -b BYTES I/O block size.\n"
+ " -h Display this help and exit.\n"
+ " -k BYTES Parser stack size.\n"
+ " -v Print verbose messages about loaded resources.\n"
+ " -s SCHEMA Schema input file.\n";
+
+ FILE* const os = error ? stderr : stdout;
+ fprintf(os, "%s", error ? "\n" : "");
+ fprintf(os, "Usage: %s [OPTION]... INPUT...\n", name);
+ fprintf(os, "%s", description);
+ return error ? SERD_BAD_ARG : SERD_SUCCESS;
+}
+
+static SerdStatus
+parse_option(OptionIter* iter, Options* const opts)
+{
+#define ARG_ERRORF(fmt, ...) \
+ fprintf(stderr, "%s: " fmt, iter->argv[0], __VA_ARGS__)
+
+ const char opt = iter->argv[iter->a][iter->f];
+ const char* argument = NULL;
+
+ switch (opt) {
+ case 'B':
+ return serd_get_argument(iter, &opts->base_uri_string);
+
+ case 'I':
+ return serd_parse_input_argument(iter, &opts->input_options);
+
+ case 'V':
+ return serd_print_version("serd-validate");
+
+ case 'W':
+ case 'X':
+ // Just enable validation and skip the pattern, checks are parsed later
+ return serd_get_argument(iter, &argument);
+
+ case 'b':
+ return serd_get_size_argument(iter, &opts->block_size);
+
+ case 'h':
+ print_usage(iter->argv[0], false);
+ return SERD_FAILURE;
+
+ case 'k':
+ return serd_get_size_argument(iter, &opts->stack_size);
+
+ case 'q':
+ opts->quiet = true;
+ return serd_option_iter_advance(iter);
+
+ case 's':
+ // Schema input, ignore here since these are loaded later
+ return serd_get_argument(iter, &argument);
+
+ case 'v':
+ opts->verbose = true;
+ return serd_option_iter_advance(iter);
+
+ case 'w':
+ return serd_get_argument(iter, &opts->out_filename);
+
+ default:
+ break;
+ }
+
+ ARG_ERRORF("invalid option -- '%c'\n", opt);
+ return print_usage(iter->argv[0], true);
+}
+
+int
+main(int argc, char** argv)
+{
+ Options opts = {NULL,
+ NULL,
+ NULL,
+ 0,
+ {SERD_SYNTAX_EMPTY, 0u, false},
+ 4096u,
+ 4194304u,
+ false,
+ false};
+
+ // Parse all command line options (which must precede inputs)
+ SerdStatus st = SERD_SUCCESS;
+ OptionIter iter = {argv, argc, 1, 1};
+ while (!serd_option_iter_is_end(iter)) {
+ if ((st = parse_option(&iter, &opts))) {
+ return (st > SERD_FAILURE);
+ }
+ }
+
+ // Every argument past the last option is an input
+ opts.inputs = argv + iter.a;
+ opts.n_inputs = argc - iter.a;
+ if (opts.n_inputs == 0) {
+ fprintf(stderr, "%s: missing input\n", argv[0]);
+ print_usage(argv[0], true);
+ return EXIT_FAILURE;
+ }
+
+ st = st ? st : run(opts, argc, argv);
+
+ return (st <= SERD_FAILURE) ? 0 : (int)st;
+}