@skaterqiang/protege-js 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/LICENSE +48 -0
- package/README.md +287 -0
- package/docs/API.md +654 -0
- package/docs/design/OWL2-/347/274/272/345/217/243/345/210/206/346/236/220.md +140 -0
- package/docs/owl2-rl/OWL2-RL-/350/247/204/345/210/231/347/273/206/345/210/231.md +168 -0
- package/docs/owl2-rl/owl2-profiles-spec-zh.html +1914 -0
- package/docs/owl2-rl/owl2-profiles-spec.html +1916 -0
- package/docs/owl2-rl/rl_rules.json +392 -0
- package/main.js +46 -0
- package/package.json +76 -0
- package/sample/README.md +74 -0
- package/sample/case1-ecommerce-risk.js +170 -0
- package/sample/case10-ecommerce-customer-service.js +454 -0
- package/sample/case2-family-swrl.js +147 -0
- package/sample/case3-pharma-safety.js +132 -0
- package/sample/case4-knowledge-publishing.js +111 -0
- package/sample/case5-obda-profiles.js +153 -0
- package/sample/case6-medical-ontology.js +152 -0
- package/sample/case7-manufacturing-ppr.js +155 -0
- package/sample/case8-real-medical-bfo-ogms.js +353 -0
- package/sample/case9-real-manufacturing-iao.js +360 -0
- package/sample/ontologies/bfo.owl +1715 -0
- package/sample/ontologies/iao.owl +8532 -0
- package/sample/ontologies/ogms.owl +4320 -0
- package/sample/ontologies/ro-core.owl +1010 -0
- package/scripts/verify-api.js +53 -0
- package/src/index.js +176 -0
- package/src/inference/OWL2ProfileReasoners.js +67 -0
- package/src/inference/OWL2RLReasoner.js +65 -0
- package/src/inference/ReasonerQueries.js +111 -0
- package/src/inference/SWRLReasoner.js +409 -0
- package/src/inference/TripleStore.js +82 -0
- package/src/inference/rdf.js +83 -0
- package/src/inference/rules/owl2el.js +33 -0
- package/src/inference/rules/owl2ql.js +29 -0
- package/src/inference/rules/owl2rl.js +823 -0
- package/src/io/FunctionalSyntaxParser.js +399 -0
- package/src/io/FunctionalSyntaxWriter.js +180 -0
- package/src/io/ManchesterSyntaxParser.js +398 -0
- package/src/io/OWLXMLParser.js +356 -0
- package/src/io/OntologyLoader.js +125 -0
- package/src/io/RDFGraphToOntology.js +702 -0
- package/src/io/RDFXMLParser.js +319 -0
- package/src/io/RDFXMLWriter.js +196 -0
- package/src/io/SWRLParser.js +150 -0
- package/src/io/TurtleParser.js +363 -0
- package/src/io/TurtleWriter.js +393 -0
- package/src/model/IRI.js +62 -0
- package/src/model/OWLAxiom.js +507 -0
- package/src/model/OWLClassExpression.js +242 -0
- package/src/model/OWLEntity.js +142 -0
- package/src/model/OWLLiteral.js +30 -0
- package/src/model/OWLModelManager.js +106 -0
- package/src/model/OWLOntology.js +119 -0
- package/src/model/SWRL.js +152 -0
- package/src/model/event/OWLModelManagerEvent.js +33 -0
- package/src/model/hierarchy/HierarchyProvider.js +214 -0
- package/src/profiles/OWL2Profiles.js +185 -0
- package/src/server/public/app.js +97 -0
- package/src/server/public/index.html +49 -0
- package/src/server/webServer.js +138 -0
- package/src/validation/GlobalRestrictionsValidator.js +172 -0
- package/test/core.test.js +111 -0
- package/test/exports-map.test.js +165 -0
- package/test/owl2/axioms.test.js +160 -0
- package/test/owl2/classExpressions.test.js +99 -0
- package/test/owl2/functional-syntax-writer.test.js +65 -0
- package/test/owl2/functional.test.js +113 -0
- package/test/owl2/global-restrictions.test.js +76 -0
- package/test/owl2/longtail.test.js +74 -0
- package/test/owl2/manchester-syntax.test.js +100 -0
- package/test/owl2/ontology-loader-imports.test.js +62 -0
- package/test/owl2/owlxml-parser.test.js +112 -0
- package/test/owl2/profile-reasoners.test.js +108 -0
- package/test/owl2/profiles.test.js +66 -0
- package/test/owl2/rdf-graph-to-ontology.test.js +177 -0
- package/test/owl2/rdfxml-punning.test.js +25 -0
- package/test/owl2/rdfxml-writer.test.js +55 -0
- package/test/owl2/rdfxml.test.js +91 -0
- package/test/owl2/reasoner-queries.test.js +58 -0
- package/test/owl2/sample-e2e.test.js +195 -0
- package/test/owl2/swrl-parser.test.js +60 -0
- package/test/owl2/swrl.test.js +131 -0
- package/test/owl2/turtle-writer.test.js +84 -0
- package/test/owl2/turtle.test.js +124 -0
- package/test/owl2rl/cax.test.js +57 -0
- package/test/owl2rl/cls.test.js +210 -0
- package/test/owl2rl/dt.test.js +72 -0
- package/test/owl2rl/eq.test.js +95 -0
- package/test/owl2rl/prp.test.js +198 -0
- package/test/owl2rl/scm.test.js +203 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const test = require('node:test');
|
|
4
|
+
const assert = require('node:assert/strict');
|
|
5
|
+
|
|
6
|
+
const { ManchesterSyntaxParser } = require('../../src/io/ManchesterSyntaxParser');
|
|
7
|
+
|
|
8
|
+
const EX = 'http://ex.org/';
|
|
9
|
+
const parser = () => new ManchesterSyntaxParser({ prefixes: { ex: EX } });
|
|
10
|
+
|
|
11
|
+
test('Class frame: SubClassOf + DisjointWith', () => {
|
|
12
|
+
const ont = parser().parse(`
|
|
13
|
+
Class: ex:Person
|
|
14
|
+
SubClassOf: ex:Animal
|
|
15
|
+
DisjointWith: ex:Robot
|
|
16
|
+
`);
|
|
17
|
+
assert.equal(ont.getAxiomsOfType('Declaration').length, 1);
|
|
18
|
+
assert.equal(ont.getAxiomsOfType('SubClassOf').length, 1);
|
|
19
|
+
assert.equal(ont.getAxiomsOfType('DisjointClasses').length, 1);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('Class frame: some restriction', () => {
|
|
23
|
+
const ont = parser().parse(`
|
|
24
|
+
Class: ex:Parent
|
|
25
|
+
SubClassOf: ex:hasChild some ex:Person
|
|
26
|
+
`);
|
|
27
|
+
const subs = ont.getAxiomsOfType('SubClassOf');
|
|
28
|
+
assert.equal(subs.length, 1);
|
|
29
|
+
assert.equal(subs[0].superClass.type, 'ObjectSomeValuesFrom');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('Class frame: and / or / not', () => {
|
|
33
|
+
const ont = parser().parse(`
|
|
34
|
+
Class: ex:A
|
|
35
|
+
SubClassOf: ex:B and ex:C
|
|
36
|
+
EquivalentTo: ex:D or ex:E
|
|
37
|
+
DisjointWith: not ex:F
|
|
38
|
+
`);
|
|
39
|
+
assert.equal(ont.getAxiomsOfType('SubClassOf')[0].superClass.type, 'ObjectIntersectionOf');
|
|
40
|
+
assert.equal(ont.getAxiomsOfType('EquivalentClasses')[0].classExpressions[1].type, 'ObjectUnionOf');
|
|
41
|
+
assert.equal(ont.getAxiomsOfType('DisjointClasses')[0].classExpressions[1].type, 'ObjectComplementOf');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('ObjectProperty frame: domain/range/characteristics/inverseOf', () => {
|
|
45
|
+
const ont = parser().parse(`
|
|
46
|
+
ObjectProperty: ex:hasParent
|
|
47
|
+
Domain: ex:Person
|
|
48
|
+
Range: ex:Person
|
|
49
|
+
Characteristics: Transitive, Functional
|
|
50
|
+
InverseOf: ex:hasChild
|
|
51
|
+
`);
|
|
52
|
+
assert.equal(ont.getAxiomsOfType('ObjectPropertyDomain').length, 1);
|
|
53
|
+
assert.equal(ont.getAxiomsOfType('ObjectPropertyRange').length, 1);
|
|
54
|
+
assert.equal(ont.getAxiomsOfType('TransitiveObjectProperty').length, 1);
|
|
55
|
+
assert.equal(ont.getAxiomsOfType('FunctionalObjectProperty').length, 1);
|
|
56
|
+
assert.equal(ont.getAxiomsOfType('InverseObjectProperties').length, 1);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('Individual frame: Types + Facts + SameAs', () => {
|
|
60
|
+
const ont = parser().parse(`
|
|
61
|
+
Individual: ex:alice
|
|
62
|
+
Types: ex:Person
|
|
63
|
+
Facts: ex:hasParent ex:bob
|
|
64
|
+
SameAs: ex:alicia
|
|
65
|
+
`);
|
|
66
|
+
assert.equal(ont.getAxiomsOfType('ClassAssertion').length, 1);
|
|
67
|
+
assert.equal(ont.getAxiomsOfType('ObjectPropertyAssertion').length, 1);
|
|
68
|
+
assert.equal(ont.getAxiomsOfType('SameIndividual').length, 1);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('Class frame: oneOf enumeration', () => {
|
|
72
|
+
const ont = parser().parse(`
|
|
73
|
+
Class: ex:Color
|
|
74
|
+
EquivalentTo: {ex:red, ex:green, ex:blue}
|
|
75
|
+
`);
|
|
76
|
+
const eq = ont.getAxiomsOfType('EquivalentClasses')[0];
|
|
77
|
+
assert.equal(eq.classExpressions[1].type, 'ObjectOneOf');
|
|
78
|
+
assert.equal(eq.classExpressions[1].operands.length, 3);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('Class frame: cardinality', () => {
|
|
82
|
+
const ont = parser().parse(`
|
|
83
|
+
Class: ex:Parent
|
|
84
|
+
SubClassOf: ex:hasChild min 2
|
|
85
|
+
`);
|
|
86
|
+
const sup = ont.getAxiomsOfType('SubClassOf')[0].superClass;
|
|
87
|
+
assert.equal(sup.type, 'ObjectMinCardinality');
|
|
88
|
+
assert.equal(sup.cardinality, 2);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('Prefix declarations', () => {
|
|
92
|
+
const p = new ManchesterSyntaxParser();
|
|
93
|
+
const ont = p.parse(`
|
|
94
|
+
Prefix: ex: <http://custom.org/>
|
|
95
|
+
Class: ex:Widget
|
|
96
|
+
SubClassOf: ex:Thing
|
|
97
|
+
`);
|
|
98
|
+
const sub = ont.getAxiomsOfType('SubClassOf')[0];
|
|
99
|
+
assert.equal(sub.subClass.getIRI().toString(), 'http://custom.org/Widget');
|
|
100
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const test = require('node:test');
|
|
4
|
+
const assert = require('node:assert/strict');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const os = require('os');
|
|
8
|
+
|
|
9
|
+
const { OntologyLoader } = require('../../src/io/OntologyLoader');
|
|
10
|
+
|
|
11
|
+
const EX = 'http://ex.org/';
|
|
12
|
+
|
|
13
|
+
function tmpDir() {
|
|
14
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), 'owl-imports-'));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
test('loadWithImports merges imported ontology axioms', () => {
|
|
18
|
+
const dir = tmpDir();
|
|
19
|
+
// Child ontology: declares class B
|
|
20
|
+
fs.writeFileSync(path.join(dir, 'child.owl'), `
|
|
21
|
+
Prefix(:=<${EX}>)
|
|
22
|
+
Ontology(<${EX}child>
|
|
23
|
+
Declaration(Class(<${EX}B>))
|
|
24
|
+
)
|
|
25
|
+
`.trim());
|
|
26
|
+
// Parent ontology: imports child, declares A subClassOf B
|
|
27
|
+
fs.writeFileSync(path.join(dir, 'parent.owl'), `
|
|
28
|
+
Prefix(:=<${EX}>)
|
|
29
|
+
Ontology(<${EX}parent>
|
|
30
|
+
Import(child.owl)
|
|
31
|
+
Declaration(Class(<${EX}A>))
|
|
32
|
+
SubClassOf(<${EX}A> <${EX}B>)
|
|
33
|
+
)
|
|
34
|
+
`.trim());
|
|
35
|
+
|
|
36
|
+
const loader = new OntologyLoader();
|
|
37
|
+
const merged = loader.loadWithImports(path.join(dir, 'parent.owl'));
|
|
38
|
+
// Should have A, B declarations and SubClassOf
|
|
39
|
+
assert.ok(merged.getAxiomsOfType('SubClassOf').length >= 1);
|
|
40
|
+
assert.ok(merged.getAxiomsOfType('Declaration').length >= 2);
|
|
41
|
+
assert.ok(merged.imports.length >= 1);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('loadWithImports guards against cyclic imports', () => {
|
|
45
|
+
const dir = tmpDir();
|
|
46
|
+
fs.writeFileSync(path.join(dir, 'a.owl'), `Ontology(<${EX}a> Import(b.owl) Declaration(Class(<${EX}A>)))`);
|
|
47
|
+
fs.writeFileSync(path.join(dir, 'b.owl'), `Ontology(<${EX}b> Import(a.owl) Declaration(Class(<${EX}B>)))`);
|
|
48
|
+
const loader = new OntologyLoader();
|
|
49
|
+
const merged = loader.loadWithImports(path.join(dir, 'a.owl'));
|
|
50
|
+
// Must terminate; both A and B present
|
|
51
|
+
const decls = merged.getAxiomsOfType('Declaration');
|
|
52
|
+
assert.ok(decls.length >= 2);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('loadFromFile auto-detects functional syntax', () => {
|
|
56
|
+
const dir = tmpDir();
|
|
57
|
+
fs.writeFileSync(path.join(dir, 'x.ofn'), `Ontology(<${EX}x> Declaration(Class(<${EX}C>)))`);
|
|
58
|
+
const loader = new OntologyLoader();
|
|
59
|
+
const ont = loader.loadFromFile(path.join(dir, 'x.ofn'));
|
|
60
|
+
assert.equal(ont.format, 'Functional');
|
|
61
|
+
assert.equal(ont.getAxiomsOfType('Declaration').length, 1);
|
|
62
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const test = require('node:test');
|
|
4
|
+
const assert = require('node:assert/strict');
|
|
5
|
+
|
|
6
|
+
const { OWLXMLParser } = require('../../src/io/OWLXMLParser');
|
|
7
|
+
|
|
8
|
+
const EX = 'http://ex.org/';
|
|
9
|
+
|
|
10
|
+
function parse(body) {
|
|
11
|
+
const xml = `<?xml version="1.0"?>
|
|
12
|
+
<Ontology ontologyIRI="${EX}test" xmlns="http://www.w3.org/2002/07/owl#">
|
|
13
|
+
<Prefix name="ex" IRI="${EX}"/>
|
|
14
|
+
${body}
|
|
15
|
+
</Ontology>`;
|
|
16
|
+
return new OWLXMLParser().parse(xml);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
test('Declaration of Class', () => {
|
|
20
|
+
const ont = parse('<Declaration><Class IRI="ex:Person"/></Declaration>');
|
|
21
|
+
assert.equal(ont.getAxiomsOfType('Declaration').length, 1);
|
|
22
|
+
assert.equal(ont.getAxiomsOfType('Declaration')[0].entity.getIRI().toString(), EX + 'Person');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('SubClassOf two classes', () => {
|
|
26
|
+
const ont = parse(`
|
|
27
|
+
<SubClassOf>
|
|
28
|
+
<Class IRI="ex:Child"/>
|
|
29
|
+
<Class IRI="ex:Person"/>
|
|
30
|
+
</SubClassOf>`);
|
|
31
|
+
const subs = ont.getAxiomsOfType('SubClassOf');
|
|
32
|
+
assert.equal(subs.length, 1);
|
|
33
|
+
assert.equal(subs[0].subClass.getIRI().toString(), EX + 'Child');
|
|
34
|
+
assert.equal(subs[0].superClass.getIRI().toString(), EX + 'Person');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('ObjectSomeValuesFrom', () => {
|
|
38
|
+
const ont = parse(`
|
|
39
|
+
<SubClassOf>
|
|
40
|
+
<Class IRI="ex:Parent"/>
|
|
41
|
+
<ObjectSomeValuesFrom>
|
|
42
|
+
<ObjectProperty IRI="ex:hasChild"/>
|
|
43
|
+
<Class IRI="ex:Person"/>
|
|
44
|
+
</ObjectSomeValuesFrom>
|
|
45
|
+
</SubClassOf>`);
|
|
46
|
+
const sup = ont.getAxiomsOfType('SubClassOf')[0].superClass;
|
|
47
|
+
assert.equal(sup.type, 'ObjectSomeValuesFrom');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('ObjectIntersectionOf', () => {
|
|
51
|
+
const ont = parse(`
|
|
52
|
+
<EquivalentClasses>
|
|
53
|
+
<Class IRI="ex:A"/>
|
|
54
|
+
<ObjectIntersectionOf>
|
|
55
|
+
<Class IRI="ex:B"/>
|
|
56
|
+
<Class IRI="ex:C"/>
|
|
57
|
+
</ObjectIntersectionOf>
|
|
58
|
+
</EquivalentClasses>`);
|
|
59
|
+
const eq = ont.getAxiomsOfType('EquivalentClasses')[0];
|
|
60
|
+
assert.equal(eq.classExpressions[1].type, 'ObjectIntersectionOf');
|
|
61
|
+
assert.equal(eq.classExpressions[1].operands.length, 2);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('ClassAssertion + ObjectPropertyAssertion', () => {
|
|
65
|
+
const ont = parse(`
|
|
66
|
+
<ClassAssertion>
|
|
67
|
+
<Class IRI="ex:Person"/>
|
|
68
|
+
<NamedIndividual IRI="ex:alice"/>
|
|
69
|
+
</ClassAssertion>
|
|
70
|
+
<ObjectPropertyAssertion>
|
|
71
|
+
<ObjectProperty IRI="ex:hasParent"/>
|
|
72
|
+
<NamedIndividual IRI="ex:alice"/>
|
|
73
|
+
<NamedIndividual IRI="ex:bob"/>
|
|
74
|
+
</ObjectPropertyAssertion>`);
|
|
75
|
+
assert.equal(ont.getAxiomsOfType('ClassAssertion').length, 1);
|
|
76
|
+
assert.equal(ont.getAxiomsOfType('ObjectPropertyAssertion').length, 1);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('FunctionalObjectProperty', () => {
|
|
80
|
+
const ont = parse('<FunctionalObjectProperty><ObjectProperty IRI="ex:hasSSN"/></FunctionalObjectProperty>');
|
|
81
|
+
assert.equal(ont.getAxiomsOfType('FunctionalObjectProperty').length, 1);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('DataPropertyAssertion with typed literal', () => {
|
|
85
|
+
const ont = parse(`
|
|
86
|
+
<DataPropertyAssertion>
|
|
87
|
+
<DataProperty IRI="ex:age"/>
|
|
88
|
+
<NamedIndividual IRI="ex:alice"/>
|
|
89
|
+
<Literal datatypeIRI="xsd:integer">30</Literal>
|
|
90
|
+
</DataPropertyAssertion>`);
|
|
91
|
+
const a = ont.getAxiomsOfType('DataPropertyAssertion')[0];
|
|
92
|
+
assert.equal(a.literal.lexicalValue, '30');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('Ontology IRI captured', () => {
|
|
96
|
+
const ont = parse('<Declaration><Class IRI="ex:X"/></Declaration>');
|
|
97
|
+
assert.equal(ont.getOntologyID().ontologyIRI.toString(), EX + 'test');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('ObjectMinCardinality', () => {
|
|
101
|
+
const ont = parse(`
|
|
102
|
+
<SubClassOf>
|
|
103
|
+
<Class IRI="ex:Parent"/>
|
|
104
|
+
<ObjectMinCardinality cardinality="2">
|
|
105
|
+
<ObjectProperty IRI="ex:hasChild"/>
|
|
106
|
+
<Class IRI="ex:Person"/>
|
|
107
|
+
</ObjectMinCardinality>
|
|
108
|
+
</SubClassOf>`);
|
|
109
|
+
const sup = ont.getAxiomsOfType('SubClassOf')[0].superClass;
|
|
110
|
+
assert.equal(sup.type, 'ObjectMinQualifiedCardinality');
|
|
111
|
+
assert.equal(sup.cardinality, 2);
|
|
112
|
+
});
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const test = require('node:test');
|
|
4
|
+
const assert = require('node:assert/strict');
|
|
5
|
+
|
|
6
|
+
const { OWL2QLReasoner, OWL2ELReasoner } = require('../../src/inference/OWL2ProfileReasoners');
|
|
7
|
+
const { TripleStore } = require('../../src/inference/TripleStore');
|
|
8
|
+
|
|
9
|
+
const RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
|
|
10
|
+
const RDFS = 'http://www.w3.org/2000/01/rdf-schema#';
|
|
11
|
+
const EX = 'http://ex.org/';
|
|
12
|
+
|
|
13
|
+
function store(...triples) {
|
|
14
|
+
const s = new TripleStore();
|
|
15
|
+
for (const [a, b, c] of triples) s.add(a, b, c);
|
|
16
|
+
return s;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// --- QL ---
|
|
20
|
+
|
|
21
|
+
test('QL: subclass inheritance (cax-sco)', () => {
|
|
22
|
+
const s = store(
|
|
23
|
+
[EX + 'A', RDFS + 'subClassOf', EX + 'B'],
|
|
24
|
+
[EX + 'x', RDF + 'type', EX + 'A']
|
|
25
|
+
);
|
|
26
|
+
const r = new OWL2QLReasoner(s);
|
|
27
|
+
r.materialize();
|
|
28
|
+
assert.ok(r.entails(EX + 'x', RDF + 'type', EX + 'B'));
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('QL: domain typing (prp-dom)', () => {
|
|
32
|
+
const s = store(
|
|
33
|
+
[EX + 'hasParent', RDFS + 'domain', EX + 'Person'],
|
|
34
|
+
[EX + 'alice', EX + 'hasParent', EX + 'bob']
|
|
35
|
+
);
|
|
36
|
+
const r = new OWL2QLReasoner(s);
|
|
37
|
+
r.materialize();
|
|
38
|
+
assert.ok(r.entails(EX + 'alice', RDF + 'type', EX + 'Person'));
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('QL: subPropertyOf propagation (prp-spo1)', () => {
|
|
42
|
+
const s = store(
|
|
43
|
+
[EX + 'hasFather', RDFS + 'subPropertyOf', EX + 'hasParent'],
|
|
44
|
+
[EX + 'alice', EX + 'hasFather', EX + 'bob']
|
|
45
|
+
);
|
|
46
|
+
const r = new OWL2QLReasoner(s);
|
|
47
|
+
r.materialize();
|
|
48
|
+
assert.ok(r.entails(EX + 'alice', EX + 'hasParent', EX + 'bob'));
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('QL: inverseOf (prp-inv1)', () => {
|
|
52
|
+
const s = store(
|
|
53
|
+
[EX + 'hasParent', 'http://www.w3.org/2002/07/owl#inverseOf', EX + 'hasChild'],
|
|
54
|
+
[EX + 'alice', EX + 'hasParent', EX + 'bob']
|
|
55
|
+
);
|
|
56
|
+
const r = new OWL2QLReasoner(s);
|
|
57
|
+
r.materialize();
|
|
58
|
+
assert.ok(r.entails(EX + 'bob', EX + 'hasChild', EX + 'alice'));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// --- EL ---
|
|
62
|
+
|
|
63
|
+
test('EL: intersection membership (cls-int1/cls-int2)', () => {
|
|
64
|
+
// A ⊑ B ⊓ C, x : A → x : B, x : C (via cls-int2 from equivalent/subclass)
|
|
65
|
+
const s = store(
|
|
66
|
+
[EX + 'A', RDFS + 'subClassOf', EX + 'B'],
|
|
67
|
+
[EX + 'x', RDF + 'type', EX + 'A']
|
|
68
|
+
);
|
|
69
|
+
const r = new OWL2ELReasoner(s);
|
|
70
|
+
r.materialize();
|
|
71
|
+
assert.ok(r.entails(EX + 'x', RDF + 'type', EX + 'B'));
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('EL: transitivity (prp-trp)', () => {
|
|
75
|
+
const OWL = 'http://www.w3.org/2002/07/owl#';
|
|
76
|
+
const s = store(
|
|
77
|
+
[EX + 'hasAncestor', RDF + 'type', OWL + 'TransitiveProperty'],
|
|
78
|
+
[EX + 'a', EX + 'hasAncestor', EX + 'b'],
|
|
79
|
+
[EX + 'b', EX + 'hasAncestor', EX + 'c']
|
|
80
|
+
);
|
|
81
|
+
const r = new OWL2ELReasoner(s);
|
|
82
|
+
r.materialize();
|
|
83
|
+
assert.ok(r.entails(EX + 'a', EX + 'hasAncestor', EX + 'c'));
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('EL: property chain (prp-spo2)', () => {
|
|
87
|
+
const OWL = 'http://www.w3.org/2002/07/owl#';
|
|
88
|
+
// hasParent ∘ hasParent ⊑ hasGrandparent via rdf:List chain
|
|
89
|
+
const s = new TripleStore();
|
|
90
|
+
s.add(EX + 'hasGrandparent', OWL + 'propertyChainAxiom', '_:c1');
|
|
91
|
+
s.add('_:c1', RDF + 'first', EX + 'hasParent');
|
|
92
|
+
s.add('_:c1', RDF + 'rest', '_:c2');
|
|
93
|
+
s.add('_:c2', RDF + 'first', EX + 'hasParent');
|
|
94
|
+
s.add('_:c2', RDF + 'rest', RDF + 'nil');
|
|
95
|
+
s.add(EX + 'a', EX + 'hasParent', EX + 'b');
|
|
96
|
+
s.add(EX + 'b', EX + 'hasParent', EX + 'c');
|
|
97
|
+
const r = new OWL2ELReasoner(s);
|
|
98
|
+
r.materialize();
|
|
99
|
+
assert.ok(r.entails(EX + 'a', EX + 'hasGrandparent', EX + 'c'));
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('QL excludes equality rules (no sameAs reflexive blowup)', () => {
|
|
103
|
+
const s = store([EX + 'x', RDF + 'type', EX + 'A']);
|
|
104
|
+
const r = new OWL2QLReasoner(s);
|
|
105
|
+
r.materialize();
|
|
106
|
+
// eq-ref would add x sameAs x; QL does not include it.
|
|
107
|
+
assert.ok(!r.entails(EX + 'x', 'http://www.w3.org/2002/07/owl#sameAs', EX + 'x'));
|
|
108
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const test = require('node:test');
|
|
4
|
+
const assert = require('node:assert/strict');
|
|
5
|
+
|
|
6
|
+
const { checkProfile, isInProfile } = require('../../src/profiles/OWL2Profiles');
|
|
7
|
+
const { OWLOntology, OWLOntologyID } = require('../../src/model/OWLOntology');
|
|
8
|
+
const { OWLClass, OWLObjectProperty } = require('../../src/model/OWLEntity');
|
|
9
|
+
const {
|
|
10
|
+
OWLSubClassOfAxiom, OWLTransitiveObjectPropertyAxiom,
|
|
11
|
+
OWLFunctionalObjectPropertyAxiom
|
|
12
|
+
} = require('../../src/model/OWLAxiom');
|
|
13
|
+
const {
|
|
14
|
+
OWLObjectUnionOf, OWLObjectComplementOf, OWLObjectSomeValuesFrom
|
|
15
|
+
} = require('../../src/model/OWLClassExpression');
|
|
16
|
+
const { IRI } = require('../../src/model/IRI');
|
|
17
|
+
|
|
18
|
+
const EX = 'http://ex.org/';
|
|
19
|
+
const ont0 = () => new OWLOntology(new OWLOntologyID(IRI.create(EX)));
|
|
20
|
+
|
|
21
|
+
test('simple subclass is in all three profiles', () => {
|
|
22
|
+
const ont = ont0();
|
|
23
|
+
ont.addAxiom(new OWLSubClassOfAxiom(new OWLClass(EX + 'A'), new OWLClass(EX + 'B')));
|
|
24
|
+
assert.equal(isInProfile(ont, 'RL'), true);
|
|
25
|
+
assert.equal(isInProfile(ont, 'QL'), true);
|
|
26
|
+
assert.equal(isInProfile(ont, 'EL'), true);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('union in subclass LHS violates RL', () => {
|
|
30
|
+
const ont = ont0();
|
|
31
|
+
ont.addAxiom(new OWLSubClassOfAxiom(
|
|
32
|
+
new OWLObjectUnionOf([new OWLClass(EX + 'A'), new OWLClass(EX + 'B')]),
|
|
33
|
+
new OWLClass(EX + 'C')));
|
|
34
|
+
const v = checkProfile(ont, 'RL');
|
|
35
|
+
assert.ok(v.length >= 1);
|
|
36
|
+
assert.equal(v[0].profile, 'RL');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('complement violates EL', () => {
|
|
40
|
+
const ont = ont0();
|
|
41
|
+
ont.addAxiom(new OWLSubClassOfAxiom(
|
|
42
|
+
new OWLObjectComplementOf(new OWLClass(EX + 'A')),
|
|
43
|
+
new OWLClass(EX + 'B')));
|
|
44
|
+
assert.equal(isInProfile(ont, 'EL'), false);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('transitive property violates QL', () => {
|
|
48
|
+
const ont = ont0();
|
|
49
|
+
ont.addAxiom(new OWLTransitiveObjectPropertyAxiom(new OWLObjectProperty(EX + 'p')));
|
|
50
|
+
assert.equal(isInProfile(ont, 'QL'), false);
|
|
51
|
+
assert.equal(isInProfile(ont, 'RL'), true);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('functional property violates EL', () => {
|
|
55
|
+
const ont = ont0();
|
|
56
|
+
ont.addAxiom(new OWLFunctionalObjectPropertyAxiom(new OWLObjectProperty(EX + 'p')));
|
|
57
|
+
assert.equal(isInProfile(ont, 'EL'), false);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('QL allows ∃P.Thing on LHS', () => {
|
|
61
|
+
const ont = ont0();
|
|
62
|
+
const thing = new OWLClass('http://www.w3.org/2002/07/owl#Thing');
|
|
63
|
+
const svf = new OWLObjectSomeValuesFrom(new OWLObjectProperty(EX + 'p'), thing);
|
|
64
|
+
ont.addAxiom(new OWLSubClassOfAxiom(svf, new OWLClass(EX + 'B')));
|
|
65
|
+
assert.equal(isInProfile(ont, 'QL'), true);
|
|
66
|
+
});
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Test RDFGraphToOntology — TripleStore → OWLOntology mapping (W3C Mapping to
|
|
4
|
+
// RDF Graphs inverse). Covers H5 (nested class expressions), H3 (hasKey),
|
|
5
|
+
// M5 (facet restrictions), L2/L3/L4 (n-ary constructs, negative assertions).
|
|
6
|
+
|
|
7
|
+
const test = require('node:test');
|
|
8
|
+
const assert = require('node:assert/strict');
|
|
9
|
+
|
|
10
|
+
const { TripleStore } = require('../../src/inference/TripleStore');
|
|
11
|
+
const { triplesToOntology } = require('../../src/io/RDFGraphToOntology');
|
|
12
|
+
const { NS } = require('../../src/inference/rdf');
|
|
13
|
+
|
|
14
|
+
const RDF = NS.RDF, RDFS = NS.RDFS, OWL = NS.OWL, XSD = NS.XSD;
|
|
15
|
+
const EX = 'http://ex.org/';
|
|
16
|
+
|
|
17
|
+
function store(...triples) {
|
|
18
|
+
const s = new TripleStore();
|
|
19
|
+
for (const t of triples) s.add(t[0], t[1], t[2]);
|
|
20
|
+
return s;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
test('named class + subClassOf named class', () => {
|
|
24
|
+
const s = store(
|
|
25
|
+
[EX + 'A', RDF + 'type', OWL + 'Class'],
|
|
26
|
+
[EX + 'B', RDF + 'type', OWL + 'Class'],
|
|
27
|
+
[EX + 'A', RDFS + 'subClassOf', EX + 'B']
|
|
28
|
+
);
|
|
29
|
+
const ont = triplesToOntology(s, { ontologyIRI: EX });
|
|
30
|
+
const subs = ont.getAxiomsOfType('SubClassOf');
|
|
31
|
+
assert.equal(subs.length, 1);
|
|
32
|
+
assert.equal(subs[0].subClass.getIRI().toString(), EX + 'A');
|
|
33
|
+
assert.equal(subs[0].superClass.getIRI().toString(), EX + 'B');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('owl:Restriction someValuesFrom nested blank node', () => {
|
|
37
|
+
const s = store(
|
|
38
|
+
[EX + 'A', RDF + 'type', OWL + 'Class'],
|
|
39
|
+
[EX + 'B', RDF + 'type', OWL + 'Class'],
|
|
40
|
+
[EX + 'p', RDF + 'type', OWL + 'ObjectProperty'],
|
|
41
|
+
[EX + 'A', RDFS + 'subClassOf', '_:r0'],
|
|
42
|
+
['_:r0', RDF + 'type', OWL + 'Restriction'],
|
|
43
|
+
['_:r0', OWL + 'onProperty', EX + 'p'],
|
|
44
|
+
['_:r0', OWL + 'someValuesFrom', EX + 'B']
|
|
45
|
+
);
|
|
46
|
+
const ont = triplesToOntology(s);
|
|
47
|
+
const subs = ont.getAxiomsOfType('SubClassOf');
|
|
48
|
+
assert.equal(subs.length, 1);
|
|
49
|
+
const sup = subs[0].superClass;
|
|
50
|
+
assert.equal(sup.type, 'ObjectSomeValuesFrom');
|
|
51
|
+
assert.equal(sup.property.getIRI().toString(), EX + 'p');
|
|
52
|
+
assert.equal(sup.filler.getIRI().toString(), EX + 'B');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('owl:intersectionOf list', () => {
|
|
56
|
+
const s = store(
|
|
57
|
+
[EX + 'A', RDF + 'type', OWL + 'Class'],
|
|
58
|
+
[EX + 'B', RDF + 'type', OWL + 'Class'],
|
|
59
|
+
[EX + 'C', RDF + 'type', OWL + 'Class'],
|
|
60
|
+
[EX + 'A', RDFS + 'subClassOf', '_:x'],
|
|
61
|
+
['_:x', OWL + 'intersectionOf', '_:l'],
|
|
62
|
+
['_:l', RDF + 'first', EX + 'B'],
|
|
63
|
+
['_:l', RDF + 'rest', '_:l2'],
|
|
64
|
+
['_:l2', RDF + 'first', EX + 'C'],
|
|
65
|
+
['_:l2', RDF + 'rest', RDF + 'nil']
|
|
66
|
+
);
|
|
67
|
+
const ont = triplesToOntology(s);
|
|
68
|
+
const sup = ont.getAxiomsOfType('SubClassOf')[0].superClass;
|
|
69
|
+
assert.equal(sup.type, 'ObjectIntersectionOf');
|
|
70
|
+
assert.equal(sup.operands.length, 2);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('owl:hasKey', () => {
|
|
74
|
+
const s = store(
|
|
75
|
+
[EX + 'A', RDF + 'type', OWL + 'Class'],
|
|
76
|
+
[EX + 'k', RDF + 'type', OWL + 'ObjectProperty'],
|
|
77
|
+
[EX + 'A', OWL + 'hasKey', '_:kl'],
|
|
78
|
+
['_:kl', RDF + 'first', EX + 'k'],
|
|
79
|
+
['_:kl', RDF + 'rest', RDF + 'nil']
|
|
80
|
+
);
|
|
81
|
+
const ont = triplesToOntology(s);
|
|
82
|
+
const keys = ont.getAxiomsOfType('HasKey');
|
|
83
|
+
assert.equal(keys.length, 1);
|
|
84
|
+
assert.equal(keys[0].propertyExpressions.length, 1);
|
|
85
|
+
assert.equal(keys[0].propertyExpressions[0].getIRI().toString(), EX + 'k');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('owl:onDatatype + withRestrictions facet', () => {
|
|
89
|
+
const s = store(
|
|
90
|
+
[EX + 'age', RDF + 'type', OWL + 'DatatypeProperty'],
|
|
91
|
+
[EX + 'age', RDFS + 'range', '_:dr'],
|
|
92
|
+
['_:dr', OWL + 'onDatatype', XSD + 'integer'],
|
|
93
|
+
['_:dr', OWL + 'withRestrictions', '_:wrl'],
|
|
94
|
+
['_:wrl', RDF + 'first', '_:fr'],
|
|
95
|
+
['_:wrl', RDF + 'rest', RDF + 'nil'],
|
|
96
|
+
['_:fr', XSD + 'minInclusive', '"0"^^<' + XSD + 'integer' + '>']
|
|
97
|
+
);
|
|
98
|
+
const ont = triplesToOntology(s);
|
|
99
|
+
const ranges = ont.getAxiomsOfType('DataPropertyRange');
|
|
100
|
+
assert.equal(ranges.length, 1);
|
|
101
|
+
assert.equal(ranges[0].range.type, 'DatatypeRestriction');
|
|
102
|
+
assert.equal(ranges[0].range.facetRestrictions.length, 1);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('owl:AllDisjointClasses via owl:members', () => {
|
|
106
|
+
const s = store(
|
|
107
|
+
[EX + 'A', RDF + 'type', OWL + 'Class'],
|
|
108
|
+
[EX + 'B', RDF + 'type', OWL + 'Class'],
|
|
109
|
+
['_:n', RDF + 'type', OWL + 'AllDisjointClasses'],
|
|
110
|
+
['_:n', OWL + 'members', '_:ml'],
|
|
111
|
+
['_:ml', RDF + 'first', EX + 'A'],
|
|
112
|
+
['_:ml', RDF + 'rest', '_:ml2'],
|
|
113
|
+
['_:ml2', RDF + 'first', EX + 'B'],
|
|
114
|
+
['_:ml2', RDF + 'rest', RDF + 'nil']
|
|
115
|
+
);
|
|
116
|
+
const ont = triplesToOntology(s);
|
|
117
|
+
const disj = ont.getAxiomsOfType('DisjointClasses');
|
|
118
|
+
assert.equal(disj.length, 1);
|
|
119
|
+
assert.equal(disj[0].classExpressions.length, 2);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('owl:NegativePropertyAssertion', () => {
|
|
123
|
+
const s = store(
|
|
124
|
+
[EX + 'i', RDF + 'type', OWL + 'NamedIndividual'],
|
|
125
|
+
[EX + 'j', RDF + 'type', OWL + 'NamedIndividual'],
|
|
126
|
+
[EX + 'p', RDF + 'type', OWL + 'ObjectProperty'],
|
|
127
|
+
['_:np', RDF + 'type', OWL + 'NegativePropertyAssertion'],
|
|
128
|
+
['_:np', OWL + 'sourceIndividual', EX + 'i'],
|
|
129
|
+
['_:np', OWL + 'assertionProperty', EX + 'p'],
|
|
130
|
+
['_:np', OWL + 'targetIndividual', EX + 'j']
|
|
131
|
+
);
|
|
132
|
+
const ont = triplesToOntology(s);
|
|
133
|
+
const negs = ont.getAxiomsOfType('NegativeObjectPropertyAssertion');
|
|
134
|
+
assert.equal(negs.length, 1);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('ontology header imports + versionIRI', () => {
|
|
138
|
+
const s = store(
|
|
139
|
+
[EX, RDF + 'type', OWL + 'Ontology'],
|
|
140
|
+
[EX, OWL + 'versionIRI', EX + 'v2'],
|
|
141
|
+
[EX, OWL + 'imports', 'http://other.org/ont'],
|
|
142
|
+
[EX + 'A', RDF + 'type', OWL + 'Class']
|
|
143
|
+
);
|
|
144
|
+
const ont = triplesToOntology(s, { ontologyIRI: EX });
|
|
145
|
+
assert.equal(ont.getOntologyID().ontologyIRI, EX);
|
|
146
|
+
assert.equal(ont.getOntologyID().versionIRI, EX + 'v2');
|
|
147
|
+
assert.deepEqual(ont.imports, ['http://other.org/ont']);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('anonymous individual via blank node', () => {
|
|
151
|
+
const s = store(
|
|
152
|
+
['_:anon1', RDF + 'type', OWL + 'NamedIndividual'],
|
|
153
|
+
['_:anon1', RDF + 'type', EX + 'C']
|
|
154
|
+
);
|
|
155
|
+
const ont = triplesToOntology(s);
|
|
156
|
+
const ca = ont.getAxiomsOfType('ClassAssertion');
|
|
157
|
+
assert.equal(ca.length, 1);
|
|
158
|
+
assert.equal(ca[0].individual.isAnonymous(), true);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('axiom annotation via owl:Axiom reification', () => {
|
|
162
|
+
const s = store(
|
|
163
|
+
[EX + 'A', RDF + 'type', OWL + 'Class'],
|
|
164
|
+
[EX + 'B', RDF + 'type', OWL + 'Class'],
|
|
165
|
+
['_:ax', RDF + 'type', OWL + 'Axiom'],
|
|
166
|
+
['_:ax', OWL + 'annotatedSource', EX + 'A'],
|
|
167
|
+
['_:ax', OWL + 'annotatedProperty', RDFS + 'subClassOf'],
|
|
168
|
+
['_:ax', OWL + 'annotatedTarget', EX + 'B'],
|
|
169
|
+
['_:ax', RDFS + 'comment', '"inferred by rule X"']
|
|
170
|
+
);
|
|
171
|
+
const ont = triplesToOntology(s);
|
|
172
|
+
const subs = ont.getAxiomsOfType('SubClassOf');
|
|
173
|
+
assert.equal(subs.length, 1);
|
|
174
|
+
const anns = subs[0].getAnnotations();
|
|
175
|
+
assert.equal(anns.length, 1);
|
|
176
|
+
assert.equal(anns[0].value.lexicalValue, 'inferred by rule X');
|
|
177
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const test = require('node:test');
|
|
4
|
+
const assert = require('node:assert/strict');
|
|
5
|
+
|
|
6
|
+
const { parseRDFXML } = require('../../src/io/RDFXMLParser');
|
|
7
|
+
|
|
8
|
+
const EX = 'http://ex.org/';
|
|
9
|
+
|
|
10
|
+
test('Punning: Description typed as both Class and ObjectProperty', () => {
|
|
11
|
+
const xml = `<?xml version="1.0"?>
|
|
12
|
+
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
13
|
+
xmlns:owl="http://www.w3.org/2002/07/owl#"
|
|
14
|
+
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
|
|
15
|
+
<rdf:Description rdf:about="${EX}Thing">
|
|
16
|
+
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Class"/>
|
|
17
|
+
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#ObjectProperty"/>
|
|
18
|
+
</rdf:Description>
|
|
19
|
+
</rdf:RDF>`;
|
|
20
|
+
const ont = parseRDFXML(xml, EX + 'ont');
|
|
21
|
+
const decls = ont.getAxiomsOfType('Declaration');
|
|
22
|
+
const kinds = decls.map(d => d.entity.constructor.name).sort();
|
|
23
|
+
assert.ok(kinds.includes('OWLClass'), 'should declare a class');
|
|
24
|
+
assert.ok(kinds.includes('OWLObjectProperty'), 'should declare an object property');
|
|
25
|
+
});
|