@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.
Files changed (92) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/LICENSE +48 -0
  3. package/README.md +287 -0
  4. package/docs/API.md +654 -0
  5. package/docs/design/OWL2-/347/274/272/345/217/243/345/210/206/346/236/220.md +140 -0
  6. package/docs/owl2-rl/OWL2-RL-/350/247/204/345/210/231/347/273/206/345/210/231.md +168 -0
  7. package/docs/owl2-rl/owl2-profiles-spec-zh.html +1914 -0
  8. package/docs/owl2-rl/owl2-profiles-spec.html +1916 -0
  9. package/docs/owl2-rl/rl_rules.json +392 -0
  10. package/main.js +46 -0
  11. package/package.json +76 -0
  12. package/sample/README.md +74 -0
  13. package/sample/case1-ecommerce-risk.js +170 -0
  14. package/sample/case10-ecommerce-customer-service.js +454 -0
  15. package/sample/case2-family-swrl.js +147 -0
  16. package/sample/case3-pharma-safety.js +132 -0
  17. package/sample/case4-knowledge-publishing.js +111 -0
  18. package/sample/case5-obda-profiles.js +153 -0
  19. package/sample/case6-medical-ontology.js +152 -0
  20. package/sample/case7-manufacturing-ppr.js +155 -0
  21. package/sample/case8-real-medical-bfo-ogms.js +353 -0
  22. package/sample/case9-real-manufacturing-iao.js +360 -0
  23. package/sample/ontologies/bfo.owl +1715 -0
  24. package/sample/ontologies/iao.owl +8532 -0
  25. package/sample/ontologies/ogms.owl +4320 -0
  26. package/sample/ontologies/ro-core.owl +1010 -0
  27. package/scripts/verify-api.js +53 -0
  28. package/src/index.js +176 -0
  29. package/src/inference/OWL2ProfileReasoners.js +67 -0
  30. package/src/inference/OWL2RLReasoner.js +65 -0
  31. package/src/inference/ReasonerQueries.js +111 -0
  32. package/src/inference/SWRLReasoner.js +409 -0
  33. package/src/inference/TripleStore.js +82 -0
  34. package/src/inference/rdf.js +83 -0
  35. package/src/inference/rules/owl2el.js +33 -0
  36. package/src/inference/rules/owl2ql.js +29 -0
  37. package/src/inference/rules/owl2rl.js +823 -0
  38. package/src/io/FunctionalSyntaxParser.js +399 -0
  39. package/src/io/FunctionalSyntaxWriter.js +180 -0
  40. package/src/io/ManchesterSyntaxParser.js +398 -0
  41. package/src/io/OWLXMLParser.js +356 -0
  42. package/src/io/OntologyLoader.js +125 -0
  43. package/src/io/RDFGraphToOntology.js +702 -0
  44. package/src/io/RDFXMLParser.js +319 -0
  45. package/src/io/RDFXMLWriter.js +196 -0
  46. package/src/io/SWRLParser.js +150 -0
  47. package/src/io/TurtleParser.js +363 -0
  48. package/src/io/TurtleWriter.js +393 -0
  49. package/src/model/IRI.js +62 -0
  50. package/src/model/OWLAxiom.js +507 -0
  51. package/src/model/OWLClassExpression.js +242 -0
  52. package/src/model/OWLEntity.js +142 -0
  53. package/src/model/OWLLiteral.js +30 -0
  54. package/src/model/OWLModelManager.js +106 -0
  55. package/src/model/OWLOntology.js +119 -0
  56. package/src/model/SWRL.js +152 -0
  57. package/src/model/event/OWLModelManagerEvent.js +33 -0
  58. package/src/model/hierarchy/HierarchyProvider.js +214 -0
  59. package/src/profiles/OWL2Profiles.js +185 -0
  60. package/src/server/public/app.js +97 -0
  61. package/src/server/public/index.html +49 -0
  62. package/src/server/webServer.js +138 -0
  63. package/src/validation/GlobalRestrictionsValidator.js +172 -0
  64. package/test/core.test.js +111 -0
  65. package/test/exports-map.test.js +165 -0
  66. package/test/owl2/axioms.test.js +160 -0
  67. package/test/owl2/classExpressions.test.js +99 -0
  68. package/test/owl2/functional-syntax-writer.test.js +65 -0
  69. package/test/owl2/functional.test.js +113 -0
  70. package/test/owl2/global-restrictions.test.js +76 -0
  71. package/test/owl2/longtail.test.js +74 -0
  72. package/test/owl2/manchester-syntax.test.js +100 -0
  73. package/test/owl2/ontology-loader-imports.test.js +62 -0
  74. package/test/owl2/owlxml-parser.test.js +112 -0
  75. package/test/owl2/profile-reasoners.test.js +108 -0
  76. package/test/owl2/profiles.test.js +66 -0
  77. package/test/owl2/rdf-graph-to-ontology.test.js +177 -0
  78. package/test/owl2/rdfxml-punning.test.js +25 -0
  79. package/test/owl2/rdfxml-writer.test.js +55 -0
  80. package/test/owl2/rdfxml.test.js +91 -0
  81. package/test/owl2/reasoner-queries.test.js +58 -0
  82. package/test/owl2/sample-e2e.test.js +195 -0
  83. package/test/owl2/swrl-parser.test.js +60 -0
  84. package/test/owl2/swrl.test.js +131 -0
  85. package/test/owl2/turtle-writer.test.js +84 -0
  86. package/test/owl2/turtle.test.js +124 -0
  87. package/test/owl2rl/cax.test.js +57 -0
  88. package/test/owl2rl/cls.test.js +210 -0
  89. package/test/owl2rl/dt.test.js +72 -0
  90. package/test/owl2rl/eq.test.js +95 -0
  91. package/test/owl2rl/prp.test.js +198 -0
  92. package/test/owl2rl/scm.test.js +203 -0
@@ -0,0 +1,399 @@
1
+ 'use strict';
2
+
3
+ // ---------------------------------------------------------------------------
4
+ // FunctionalSyntaxParser — parses the OWL 2 Functional-Style syntax.
5
+ // Handles the full axiom set: Declaration, SubClassOf, EquivalentClasses,
6
+ // DisjointClasses, DisjointUnion, SubObjectPropertyOf, SubObjectPropertyChain,
7
+ // EquivalentObjectProperties, DisjointObjectProperties, ObjectPropertyDomain,
8
+ // ObjectPropertyRange, InverseObjectProperties, Functional/InverseFunctional/
9
+ // Reflexive/Irreflexive/Symmetric/Asymmetric/Transitive ObjectProperty,
10
+ // SubDataPropertyOf, EquivalentDataProperties, DisjointDataProperties,
11
+ // DataPropertyDomain, DataPropertyRange, FunctionalDataProperty,
12
+ // DatatypeDefinition, HasKey, SameIndividual, DifferentIndividuals,
13
+ // ClassAssertion, ObjectPropertyAssertion, NegativeObjectPropertyAssertion,
14
+ // DataPropertyAssertion, NegativeDataPropertyAssertion,
15
+ // AnnotationAssertion, SubAnnotationPropertyOf, AnnotationPropertyDomain/Range,
16
+ // plus all class expressions (Object*/Data*) and data ranges.
17
+ // ---------------------------------------------------------------------------
18
+
19
+ const { OWLOntology, OWLOntologyID } = require('../model/OWLOntology');
20
+ const { OWLClass, OWLObjectProperty, OWLDataProperty, OWLNamedIndividual, OWLDatatype, OWLAnnotationProperty } = require('../model/OWLEntity');
21
+ const { IRI } = require('../model/IRI');
22
+ const { OWLLiteral } = require('../model/OWLLiteral');
23
+ const CE = require('../model/OWLClassExpression');
24
+ const AX = require('../model/OWLAxiom');
25
+
26
+ const KNOWN_PREFIXES = {
27
+ rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
28
+ rdfs: 'http://www.w3.org/2000/01/rdf-schema#',
29
+ owl: 'http://www.w3.org/2002/07/owl#',
30
+ xsd: 'http://www.w3.org/2001/XMLSchema#'
31
+ };
32
+
33
+ class FunctionalSyntaxParser {
34
+ constructor() {
35
+ this.prefixes = { ...KNOWN_PREFIXES };
36
+ }
37
+
38
+ /** Parse functional-syntax text, return OWLOntology. */
39
+ parse(text, ontologyIRI = 'http://example.org/ontology') {
40
+ const tokens = this._tokenize(text);
41
+ this._pos = 0;
42
+ this._tokens = tokens;
43
+ const onto = new OWLOntology(new OWLOntologyID(IRI.create(ontologyIRI)));
44
+ // Optionally: handle Prefix(...) declarations and Ontology(...) wrapper.
45
+ while (this._pos < tokens.length) {
46
+ this._statement(onto);
47
+ }
48
+ return onto;
49
+ }
50
+
51
+ _tokenize(text) {
52
+ const out = [];
53
+ let i = 0;
54
+ const n = text.length;
55
+ while (i < n) {
56
+ const ch = text[i];
57
+ if (/\s/.test(ch)) { i++; continue; }
58
+ if (ch === '#') { while (i < n && text[i] !== '\n') i++; continue; }
59
+ if (ch === '(') { out.push({ t: '(' }); i++; continue; }
60
+ if (ch === ')') { out.push({ t: ')' }); i++; continue; }
61
+ if (ch === '"') {
62
+ let j = i + 1;
63
+ let v = '';
64
+ while (j < n && text[j] !== '"') {
65
+ if (text[j] === '\\' && j + 1 < n) { v += text[j + 1]; j += 2; }
66
+ else { v += text[j]; j++; }
67
+ }
68
+ out.push({ t: 'STRING', v });
69
+ i = j + 1;
70
+ continue;
71
+ }
72
+ if (ch === '<') {
73
+ const j = text.indexOf('>', i);
74
+ out.push({ t: 'IRI', v: text.slice(i + 1, j) });
75
+ i = j + 1;
76
+ continue;
77
+ }
78
+ if (ch === '@') {
79
+ let j = i + 1;
80
+ while (j < n && /[A-Za-z-]/.test(text[j])) j++;
81
+ out.push({ t: 'LANG', v: text.slice(i + 1, j) });
82
+ i = j;
83
+ continue;
84
+ }
85
+ if (ch === '^' && text[i + 1] === '^') { out.push({ t: '^^' }); i += 2; continue; }
86
+ if (ch === '=') { out.push({ t: '=' }); i++; continue; }
87
+ // number
88
+ if (/[0-9+-]/.test(ch)) {
89
+ let j = i + 1;
90
+ while (j < n && /[0-9.eE+-]/.test(text[j])) j++;
91
+ out.push({ t: 'NUMBER', v: text.slice(i, j) });
92
+ i = j;
93
+ continue;
94
+ }
95
+ // name (identifier or prefixed)
96
+ let j = i;
97
+ while (j < n && /[A-Za-z0-9_:\-.]/.test(text[j])) j++;
98
+ if (j > i) { out.push({ t: 'NAME', v: text.slice(i, j) }); i = j; continue; }
99
+ throw new Error('Unexpected char: ' + ch + ' at ' + i);
100
+ }
101
+ return out;
102
+ }
103
+
104
+ _peek() { return this._tokens[this._pos]; }
105
+ _next() { return this._tokens[this._pos++]; }
106
+ _expect(t) {
107
+ const tok = this._next();
108
+ if (!tok || tok.t !== t) throw new Error(`Expected ${t}, got ${tok && tok.t}`);
109
+ return tok;
110
+ }
111
+
112
+ _iriFrom(tok) {
113
+ if (tok.t === 'IRI') return IRI.create(tok.v);
114
+ if (tok.t === 'NAME') {
115
+ const s = tok.v;
116
+ const idx = s.indexOf(':');
117
+ if (idx > 0) {
118
+ const prefix = s.slice(0, idx);
119
+ const local = s.slice(idx + 1);
120
+ if (this.prefixes[prefix]) return IRI.create(this.prefixes[prefix] + local);
121
+ }
122
+ return IRI.create(s);
123
+ }
124
+ throw new Error('Expected IRI, got ' + JSON.stringify(tok));
125
+ }
126
+
127
+ _statement(onto) {
128
+ const tok = this._peek();
129
+ if (tok.t === 'NAME' && tok.v === 'Prefix') {
130
+ this._next(); this._expect('(');
131
+ const pn = this._expect('NAME');
132
+ this._expect('='); // may not tokenize — handle below
133
+ const iriTok = this._expect('IRI');
134
+ const pfx = pn.v.replace(/:$/, '');
135
+ this.prefixes[pfx] = iriTok.v;
136
+ if (onto && onto.addPrefix) onto.addPrefix(pfx, iriTok.v); // prefix fidelity (L5)
137
+ this._expect(')');
138
+ return;
139
+ }
140
+ if (tok.t === 'NAME' && tok.v === 'Ontology') {
141
+ // Ontology( [iri] [versionIRI] Import(...)* Annotation(...)* axiom* )
142
+ this._next(); this._expect('(');
143
+ // Optional ontology IRI
144
+ if (this._peek().t === 'IRI' || (this._peek().t === 'NAME' && this._tokens[this._pos + 1] && this._tokens[this._pos + 1].t !== '(')) {
145
+ onto.id.ontologyIRI = this._iriFrom(this._next());
146
+ // Optional version IRI
147
+ if (this._peek() && this._peek().t === 'IRI') {
148
+ onto.id.versionIRI = this._iriFrom(this._next());
149
+ }
150
+ }
151
+ while (this._peek() && this._peek().t !== ')') {
152
+ const t = this._peek();
153
+ if (t.t === 'NAME' && t.v === 'Import') {
154
+ this._next(); this._expect('(');
155
+ onto.imports.push(this._iriFrom(this._next()));
156
+ this._expect(')');
157
+ continue;
158
+ }
159
+ if (t.t === 'NAME' && t.v === 'Annotation') {
160
+ // Ontology-level annotation
161
+ this._next(); this._expect('(');
162
+ const propTok = this._next();
163
+ const prop = this._iriFrom(propTok);
164
+ const valTok = this._next();
165
+ let value;
166
+ if (valTok.t === 'STRING') {
167
+ const { OWLLiteral } = require('../model/OWLLiteral');
168
+ value = new OWLLiteral(valTok.v);
169
+ if (this._peek() && this._peek().t === '^^') {
170
+ this._next();
171
+ const { OWLDatatype } = require('../model/OWLEntity');
172
+ value.datatype = new OWLDatatype(this._iriFrom(this._next()));
173
+ }
174
+ } else {
175
+ value = this._iriFrom(valTok);
176
+ }
177
+ const { OWLAnnotationAssertionAxiom } = require('../model/OWLAxiom');
178
+ const { OWLAnnotationProperty } = require('../model/OWLEntity');
179
+ onto.addOntologyAnnotation(new OWLAnnotationAssertionAxiom(
180
+ onto.id.ontologyIRI, new OWLAnnotationProperty(prop), value));
181
+ this._expect(')');
182
+ continue;
183
+ }
184
+ this._axiom(onto);
185
+ }
186
+ this._expect(')');
187
+ return;
188
+ }
189
+ this._axiom(onto);
190
+ }
191
+
192
+ _axiom(onto) {
193
+ const name = this._expect('NAME').v;
194
+ this._expect('(');
195
+ const args = [];
196
+ while (this._peek() && this._peek().t !== ')') {
197
+ args.push(this._term());
198
+ }
199
+ this._expect(')');
200
+ this._dispatch(onto, name, args);
201
+ }
202
+
203
+ _term() {
204
+ const tok = this._peek();
205
+ if (tok.t === '(') {
206
+ this._next();
207
+ const head = this._expect('NAME').v;
208
+ const items = [];
209
+ while (this._peek() && this._peek().t !== ')') items.push(this._term());
210
+ this._expect(')');
211
+ return { kind: 'expr', name: head, items };
212
+ }
213
+ // NAME followed by '(' — e.g. Class(ex:Person) inside Declaration
214
+ if (tok.t === 'NAME' && this._tokens[this._pos + 1] && this._tokens[this._pos + 1].t === '(') {
215
+ const head = this._next().v;
216
+ this._expect('(');
217
+ const items = [];
218
+ while (this._peek() && this._peek().t !== ')') items.push(this._term());
219
+ this._expect(')');
220
+ return { kind: 'expr', name: head, items };
221
+ }
222
+ if (tok.t === 'STRING') {
223
+ this._next();
224
+ let lit = { lex: tok.v, dt: null, lang: null };
225
+ const next = this._peek();
226
+ if (next && next.t === '^^') {
227
+ this._next();
228
+ const dtTok = this._next();
229
+ lit.dt = this._iriFrom(dtTok).toString();
230
+ } else if (next && next.t === 'LANG') {
231
+ this._next();
232
+ lit.lang = next.v;
233
+ }
234
+ return { kind: 'literal', value: lit };
235
+ }
236
+ if (tok.t === 'NUMBER') {
237
+ this._next();
238
+ return { kind: 'number', value: tok.v };
239
+ }
240
+ // IRI or name
241
+ return { kind: 'iri', value: this._iriFrom(this._next()) };
242
+ }
243
+
244
+ _dispatch(onto, name, args) {
245
+ const P = (tok) => {
246
+ if (tok.kind === 'iri') {
247
+ const iri = tok.value;
248
+ // heuristic: ObjectProperty if name in prop position; we let caller wrap.
249
+ return iri;
250
+ }
251
+ return tok;
252
+ };
253
+
254
+ const entity = (tok, kind) => {
255
+ const iri = tok.kind === 'iri' ? tok.value : (() => { throw new Error('expected iri'); })();
256
+ switch (kind) {
257
+ case 'class': return new OWLClass(iri);
258
+ case 'op': return new OWLObjectProperty(iri);
259
+ case 'dp': return new OWLDataProperty(iri);
260
+ case 'ni': return new OWLNamedIndividual(iri);
261
+ case 'dt': return new OWLDatatype(iri);
262
+ case 'ap': return new OWLAnnotationProperty(iri);
263
+ default: throw new Error('unknown entity kind ' + kind);
264
+ }
265
+ };
266
+
267
+ const literal = (tok) => {
268
+ if (tok.kind !== 'literal' && tok.kind !== 'number') throw new Error('expected literal');
269
+ if (tok.kind === 'number') {
270
+ const v = tok.value;
271
+ const dt = /^-?\d+$/.test(v) ? 'http://www.w3.org/2001/XMLSchema#integer'
272
+ : /^-?\d+\.\d+$/.test(v) ? 'http://www.w3.org/2001/XMLSchema#decimal'
273
+ : 'http://www.w3.org/2001/XMLSchema#double';
274
+ return new OWLLiteral(v, new OWLDatatype(IRI.create(dt)));
275
+ }
276
+ return new OWLLiteral(tok.value.lex, tok.value.dt ? new OWLDatatype(IRI.create(tok.value.dt)) : null, tok.value.lang);
277
+ };
278
+
279
+ const classExpr = (tok) => {
280
+ if (tok.kind === 'iri') return new OWLClass(tok.value);
281
+ const n = tok.name;
282
+ const it = tok.items;
283
+ switch (n) {
284
+ case 'ObjectIntersectionOf': return new CE.OWLObjectIntersectionOf(it.map(classExpr));
285
+ case 'ObjectUnionOf': return new CE.OWLObjectUnionOf(it.map(classExpr));
286
+ case 'ObjectComplementOf': return new CE.OWLObjectComplementOf(classExpr(it[0]));
287
+ case 'ObjectSomeValuesFrom': return new CE.OWLObjectSomeValuesFrom(entity(it[0], 'op'), classExpr(it[1]));
288
+ case 'ObjectAllValuesFrom': return new CE.OWLObjectAllValuesFrom(entity(it[0], 'op'), classExpr(it[1]));
289
+ case 'ObjectHasValue': return new CE.OWLObjectHasValue(entity(it[0], 'op'), entity(it[1], 'ni'));
290
+ case 'ObjectOneOf': return new CE.OWLObjectOneOf(it.map(i => entity(i, 'ni')));
291
+ case 'ObjectHasSelf': return new CE.OWLObjectHasSelf(entity(it[0], 'op'));
292
+ case 'ObjectMinCardinality': return new CE.OWLObjectCardinalityRestriction(CE.ClassExpressionType.OBJECT_MIN_CARDINALITY, parseInt(it[0].value, 10), entity(it[1], 'op'), it[2] ? classExpr(it[2]) : null);
293
+ case 'ObjectMaxCardinality': return new CE.OWLObjectCardinalityRestriction(CE.ClassExpressionType.OBJECT_MAX_CARDINALITY, parseInt(it[0].value, 10), entity(it[1], 'op'), it[2] ? classExpr(it[2]) : null);
294
+ case 'ObjectExactCardinality': return new CE.OWLObjectCardinalityRestriction(CE.ClassExpressionType.OBJECT_EXACT_CARDINALITY, parseInt(it[0].value, 10), entity(it[1], 'op'), it[2] ? classExpr(it[2]) : null);
295
+ case 'DataSomeValuesFrom': return new CE.OWLDataSomeValuesFrom(entity(it[0], 'dp'), dataRange(it[1]));
296
+ case 'DataAllValuesFrom': return new CE.OWLDataAllValuesFrom(entity(it[0], 'dp'), dataRange(it[1]));
297
+ case 'DataHasValue': return new CE.OWLDataHasValue(entity(it[0], 'dp'), literal(it[1]));
298
+ case 'DataMinCardinality': return new CE.OWLDataCardinalityRestriction(CE.ClassExpressionType.DATA_MIN_CARDINALITY, parseInt(it[0].value, 10), entity(it[1], 'dp'), it[2] ? dataRange(it[2]) : null);
299
+ case 'DataMaxCardinality': return new CE.OWLDataCardinalityRestriction(CE.ClassExpressionType.DATA_MAX_CARDINALITY, parseInt(it[0].value, 10), entity(it[1], 'dp'), it[2] ? dataRange(it[2]) : null);
300
+ case 'DataExactCardinality': return new CE.OWLDataCardinalityRestriction(CE.ClassExpressionType.DATA_EXACT_CARDINALITY, parseInt(it[0].value, 10), entity(it[1], 'dp'), it[2] ? dataRange(it[2]) : null);
301
+ default: throw new Error('Unknown class expression: ' + n);
302
+ }
303
+ };
304
+
305
+ const dataRange = (tok) => {
306
+ if (tok.kind === 'iri') return new OWLDatatype(tok.value);
307
+ const n = tok.name;
308
+ const it = tok.items;
309
+ switch (n) {
310
+ case 'DataIntersectionOf': return new CE.OWLDataIntersectionOf(it.map(dataRange));
311
+ case 'DataUnionOf': return new CE.OWLDataUnionOf(it.map(dataRange));
312
+ case 'DataComplementOf': return new CE.OWLDataComplementOf(dataRange(it[0]));
313
+ case 'DataOneOf': return new CE.OWLDataOneOf(it.map(literal));
314
+ case 'DatatypeRestriction': {
315
+ const dt = new OWLDatatype(tok.items[0].value);
316
+ const facets = it.slice(1).map(f => ({ facet: f.items ? f.items[0].value : f.value, value: literal(f.items ? f.items[1] : f) }));
317
+ return new CE.OWLDatatypeRestriction(dt, facets);
318
+ }
319
+ default: throw new Error('Unknown data range: ' + n);
320
+ }
321
+ };
322
+
323
+ const add = (ax) => onto.addAxiom(ax);
324
+
325
+ switch (name) {
326
+ case 'Declaration': {
327
+ const entTok = args[0];
328
+ if (entTok.kind === 'expr') {
329
+ const kind = entTok.name;
330
+ const iriTok = entTok.items[0];
331
+ const map = { Class: 'class', ObjectProperty: 'op', DataProperty: 'dp', NamedIndividual: 'ni', Datatype: 'dt', AnnotationProperty: 'ap' };
332
+ if (map[kind]) add(new AX.OWLDeclarationAxiom(entity(iriTok, map[kind])));
333
+ }
334
+ return;
335
+ }
336
+ case 'SubClassOf': add(new AX.OWLSubClassOfAxiom(classExpr(args[0]), classExpr(args[1]))); return;
337
+ case 'EquivalentClasses': add(new AX.OWLEquivalentClassesAxiom(args.map(classExpr))); return;
338
+ case 'DisjointClasses': add(new AX.OWLDisjointClassesAxiom(args.map(classExpr))); return;
339
+ case 'DisjointUnion': add(new AX.OWLDisjointUnionAxiom(entity(args[0], 'class'), args.slice(1).map(classExpr))); return;
340
+ case 'SubObjectPropertyOf': {
341
+ if (args[0] && args[0].kind === 'expr' && args[0].name === 'ObjectPropertyChain') {
342
+ const chain = args[0].items.map(t => entity(t, 'op'));
343
+ add(new AX.OWLSubPropertyChainOfAxiom(chain, entity(args[args.length - 1], 'op')));
344
+ } else {
345
+ add(new AX.OWLSubObjectPropertyOfAxiom(entity(args[0], 'op'), entity(args[1], 'op')));
346
+ }
347
+ return;
348
+ }
349
+ case 'SubObjectPropertyChain': {
350
+ const chain = args[0].kind === 'expr' && args[0].name === 'ObjectPropertyChain'
351
+ ? args[0].items.map(t => entity(t, 'op'))
352
+ : args.slice(0, -1).map(t => entity(t, 'op'));
353
+ add(new AX.OWLSubPropertyChainOfAxiom(chain, entity(args[args.length - 1], 'op')));
354
+ return;
355
+ }
356
+ case 'EquivalentObjectProperties': add(new AX.OWLEquivalentObjectPropertiesAxiom(args.map(t => entity(t, 'op')))); return;
357
+ case 'DisjointObjectProperties': add(new AX.OWLDisjointObjectPropertiesAxiom(args.map(t => entity(t, 'op')))); return;
358
+ case 'ObjectPropertyDomain': add(new AX.OWLObjectPropertyDomainAxiom(entity(args[0], 'op'), classExpr(args[1]))); return;
359
+ case 'ObjectPropertyRange': add(new AX.OWLObjectPropertyRangeAxiom(entity(args[0], 'op'), classExpr(args[1]))); return;
360
+ case 'InverseObjectProperties': add(new AX.OWLInverseObjectPropertiesAxiom(entity(args[0], 'op'), entity(args[1], 'op'))); return;
361
+ case 'FunctionalObjectProperty': add(new AX.OWLFunctionalObjectPropertyAxiom(entity(args[0], 'op'))); return;
362
+ case 'InverseFunctionalObjectProperty': add(new AX.OWLInverseFunctionalObjectPropertyAxiom(entity(args[0], 'op'))); return;
363
+ case 'ReflexiveObjectProperty': add(new AX.OWLReflexiveObjectPropertyAxiom(entity(args[0], 'op'))); return;
364
+ case 'IrreflexiveObjectProperty': add(new AX.OWLIrreflexiveObjectPropertyAxiom(entity(args[0], 'op'))); return;
365
+ case 'SymmetricObjectProperty': add(new AX.OWLSymmetricObjectPropertyAxiom(entity(args[0], 'op'))); return;
366
+ case 'AsymmetricObjectProperty': add(new AX.OWLAsymmetricObjectPropertyAxiom(entity(args[0], 'op'))); return;
367
+ case 'TransitiveObjectProperty': add(new AX.OWLTransitiveObjectPropertyAxiom(entity(args[0], 'op'))); return;
368
+ case 'SubDataPropertyOf': add(new AX.OWLSubDataPropertyOfAxiom(entity(args[0], 'dp'), entity(args[1], 'dp'))); return;
369
+ case 'EquivalentDataProperties': add(new AX.OWLEquivalentDataPropertiesAxiom(args.map(t => entity(t, 'dp')))); return;
370
+ case 'DisjointDataProperties': add(new AX.OWLDisjointDataPropertiesAxiom(args.map(t => entity(t, 'dp')))); return;
371
+ case 'DataPropertyDomain': add(new AX.OWLDataPropertyDomainAxiom(entity(args[0], 'dp'), classExpr(args[1]))); return;
372
+ case 'DataPropertyRange': add(new AX.OWLDataPropertyRangeAxiom(entity(args[0], 'dp'), dataRange(args[1]))); return;
373
+ case 'FunctionalDataProperty': add(new AX.OWLFunctionalDataPropertyAxiom(entity(args[0], 'dp'))); return;
374
+ case 'DatatypeDefinition': add(new AX.OWLDatatypeDefinitionAxiom(entity(args[0], 'dt'), dataRange(args[1]))); return;
375
+ case 'HasKey': add(new AX.OWLHasKeyAxiom(classExpr(args[0]), args.slice(1).map(t => entity(t, 'op')))); return;
376
+ case 'SameIndividual': add(new AX.OWLSameIndividualAxiom(args.map(t => entity(t, 'ni')))); return;
377
+ case 'DifferentIndividuals': add(new AX.OWLDifferentIndividualsAxiom(args.map(t => entity(t, 'ni')))); return;
378
+ case 'ClassAssertion': add(new AX.OWLClassAssertionAxiom(entity(args[1], 'ni'), classExpr(args[0]))); return;
379
+ case 'ObjectPropertyAssertion': add(new AX.OWLObjectPropertyAssertionAxiom(entity(args[1], 'ni'), entity(args[0], 'op'), entity(args[2], 'ni'))); return;
380
+ case 'NegativeObjectPropertyAssertion': add(new AX.OWLNegativeObjectPropertyAssertionAxiom(entity(args[1], 'ni'), entity(args[0], 'op'), entity(args[2], 'ni'))); return;
381
+ case 'DataPropertyAssertion': add(new AX.OWLDataPropertyAssertionAxiom(entity(args[1], 'ni'), entity(args[0], 'dp'), literal(args[2]))); return;
382
+ case 'NegativeDataPropertyAssertion': add(new AX.OWLNegativeDataPropertyAssertionAxiom(entity(args[1], 'ni'), entity(args[0], 'dp'), literal(args[2]))); return;
383
+ case 'AnnotationAssertion': {
384
+ const prop = entity(args[0], 'ap');
385
+ const subj = args[1].kind === 'iri' ? args[1].value : null;
386
+ const val = args[2].kind === 'iri' ? args[2].value : literal(args[2]);
387
+ add(new AX.OWLAnnotationAssertionAxiom(subj, prop, val));
388
+ return;
389
+ }
390
+ case 'SubAnnotationPropertyOf': add(new AX.OWLSubAnnotationPropertyOfAxiom(entity(args[0], 'ap'), entity(args[1], 'ap'))); return;
391
+ case 'AnnotationPropertyDomain': add(new AX.OWLAnnotationPropertyDomainAxiom(entity(args[0], 'ap'), args[1].value)); return;
392
+ case 'AnnotationPropertyRange': add(new AX.OWLAnnotationPropertyRangeAxiom(entity(args[0], 'ap'), args[1].value)); return;
393
+ default:
394
+ throw new Error('Unsupported axiom: ' + name);
395
+ }
396
+ }
397
+ }
398
+
399
+ module.exports = { FunctionalSyntaxParser };
@@ -0,0 +1,180 @@
1
+ 'use strict';
2
+
3
+ // ---------------------------------------------------------------------------
4
+ // io/FunctionalSyntaxWriter — serialize an OWLOntology to OWL 2 Functional
5
+ // Syntax (W3C §9). Round-trip capable counterpart to FunctionalSyntaxParser.
6
+ // ---------------------------------------------------------------------------
7
+
8
+ const { ClassExpressionType: T } = require('../model/OWLClassExpression');
9
+ const { AxiomType: A } = require('../model/OWLAxiom');
10
+
11
+ function iriOf(x) {
12
+ const s = x && x.getIRI ? x.getIRI().toString() : String(x);
13
+ return `<${s}>`;
14
+ }
15
+
16
+ function classExpr(e) {
17
+ if (!e) return '';
18
+ if (e.getIRI) return iriOf(e); // OWLClass / entity
19
+ switch (e.type) {
20
+ case T.OBJECT_INTERSECTION_OF:
21
+ return `ObjectIntersectionOf(${e.operands.map(classExpr).join(' ')})`;
22
+ case T.OBJECT_UNION_OF:
23
+ return `ObjectUnionOf(${e.operands.map(classExpr).join(' ')})`;
24
+ case T.OBJECT_COMPLEMENT_OF:
25
+ return `ObjectComplementOf(${classExpr(e.operand)})`;
26
+ case T.OBJECT_SOME_VALUES_FROM:
27
+ return `ObjectSomeValuesFrom(${iriOf(e.property)} ${classExpr(e.filler)})`;
28
+ case T.OBJECT_ALL_VALUES_FROM:
29
+ return `ObjectAllValuesFrom(${iriOf(e.property)} ${classExpr(e.filler)})`;
30
+ case T.OBJECT_HAS_VALUE:
31
+ return `ObjectHasValue(${iriOf(e.property)} ${iriOf(e.value)})`;
32
+ case T.OBJECT_ONE_OF:
33
+ return `ObjectOneOf(${e.operands.map(iriOf).join(' ')})`;
34
+ case T.OBJECT_HAS_SELF:
35
+ return `ObjectHasSelf(${iriOf(e.property)})`;
36
+ case T.OBJECT_MIN_CARDINALITY:
37
+ return `ObjectMinCardinality(${e.cardinality} ${iriOf(e.property)})`;
38
+ case T.OBJECT_MAX_CARDINALITY:
39
+ return `ObjectMaxCardinality(${e.cardinality} ${iriOf(e.property)})`;
40
+ case T.OBJECT_EXACT_CARDINALITY:
41
+ return `ObjectExactCardinality(${e.cardinality} ${iriOf(e.property)})`;
42
+ case T.OBJECT_MIN_QUALIFIED_CARDINALITY:
43
+ return `ObjectMinCardinality(${e.cardinality} ${iriOf(e.property)} ${classExpr(e.filler)})`;
44
+ case T.OBJECT_MAX_QUALIFIED_CARDINALITY:
45
+ return `ObjectMaxCardinality(${e.cardinality} ${iriOf(e.property)} ${classExpr(e.filler)})`;
46
+ case T.OBJECT_EXACT_QUALIFIED_CARDINALITY:
47
+ return `ObjectExactCardinality(${e.cardinality} ${iriOf(e.property)} ${classExpr(e.filler)})`;
48
+ case T.DATA_SOME_VALUES_FROM:
49
+ return `DataSomeValuesFrom(${iriOf(e.property)} ${dataRange(e.filler)})`;
50
+ case T.DATA_ALL_VALUES_FROM:
51
+ return `DataAllValuesFrom(${iriOf(e.property)} ${dataRange(e.filler)})`;
52
+ case T.DATA_HAS_VALUE:
53
+ return `DataHasValue(${iriOf(e.property)} ${literal(e.value)})`;
54
+ case T.DATA_MIN_CARDINALITY:
55
+ return `DataMinCardinality(${e.cardinality} ${iriOf(e.property)})`;
56
+ case T.DATA_MAX_CARDINALITY:
57
+ return `DataMaxCardinality(${e.cardinality} ${iriOf(e.property)})`;
58
+ case T.DATA_EXACT_CARDINALITY:
59
+ return `DataExactCardinality(${e.cardinality} ${iriOf(e.property)})`;
60
+ default:
61
+ return iriOf(e);
62
+ }
63
+ }
64
+
65
+ function dataRange(dr) {
66
+ if (!dr) return '';
67
+ if (dr.getIRI) return iriOf(dr); // OWLDatatype
68
+ switch (dr.type) {
69
+ case T.DATA_INTERSECTION_OF:
70
+ return `DataIntersectionOf(${dr.operands.map(dataRange).join(' ')})`;
71
+ case T.DATA_UNION_OF:
72
+ return `DataUnionOf(${dr.operands.map(dataRange).join(' ')})`;
73
+ case T.DATA_COMPLEMENT_OF:
74
+ return `DataComplementOf(${dataRange(dr.operand)})`;
75
+ case T.DATA_ONE_OF:
76
+ return `DataOneOf(${dr.operands.map(literal).join(' ')})`;
77
+ case T.DATATYPE_RESTRICTION: {
78
+ const frs = dr.facetRestrictions.map(f => `${iriOf(f.facet)} ${literal(f.value)}`).join(' ');
79
+ return `DatatypeRestriction(${iriOf(dr.datatype)} ${frs})`;
80
+ }
81
+ default:
82
+ return iriOf(dr);
83
+ }
84
+ }
85
+
86
+ function literal(l) {
87
+ let s = `"${l.lexicalValue}"`;
88
+ if (l.lang) s += `@${l.lang}`;
89
+ else if (l.datatype) s += `^^${iriOf(l.datatype)}`;
90
+ return s;
91
+ }
92
+
93
+ function ind(i) {
94
+ return i.isAnonymous && i.isAnonymous() ? i.toString() : iriOf(i);
95
+ }
96
+
97
+ function axiom(ax) {
98
+ const t = ax.getAxiomType();
99
+ switch (t) {
100
+ case A.DECLARATION: {
101
+ const e = ax.entity;
102
+ const kind = e.isOWLClass() ? 'Class'
103
+ : e.isOWLObjectProperty() ? 'ObjectProperty'
104
+ : e.isOWLDataProperty() ? 'DataProperty'
105
+ : e.isOWLAnnotationProperty() ? 'AnnotationProperty'
106
+ : 'NamedIndividual';
107
+ return `Declaration(${kind}(${iriOf(e)}))`;
108
+ }
109
+ case A.SUBCLASS_OF: return `SubClassOf(${classExpr(ax.subClass)} ${classExpr(ax.superClass)})`;
110
+ case A.EQUIVALENT_CLASSES: return `EquivalentClasses(${ax.classExpressions.map(classExpr).join(' ')})`;
111
+ case A.DISJOINT_CLASSES: return `DisjointClasses(${ax.classExpressions.map(classExpr).join(' ')})`;
112
+ case A.DISJOINT_UNION: return `DisjointUnion(${classExpr(ax.owlClass)} ${ax.classExpressions.map(classExpr).join(' ')})`;
113
+ case A.SUB_OBJECT_PROPERTY_OF: return `SubObjectPropertyOf(${iriOf(ax.subProperty)} ${iriOf(ax.superProperty)})`;
114
+ case A.SUB_DATA_PROPERTY_OF: return `SubDataPropertyOf(${iriOf(ax.subProperty)} ${iriOf(ax.superProperty)})`;
115
+ case A.EQUIVALENT_OBJECT_PROPERTIES: return `EquivalentObjectProperties(${ax.properties.map(iriOf).join(' ')})`;
116
+ case A.DISJOINT_OBJECT_PROPERTIES: return `DisjointObjectProperties(${ax.properties.map(iriOf).join(' ')})`;
117
+ case A.EQUIVALENT_DATA_PROPERTIES: return `EquivalentDataProperties(${ax.properties.map(iriOf).join(' ')})`;
118
+ case A.DISJOINT_DATA_PROPERTIES: return `DisjointDataProperties(${ax.properties.map(iriOf).join(' ')})`;
119
+ case A.INVERSE_OBJECT_PROPERTIES: return `InverseObjectProperties(${iriOf(ax.property1)} ${iriOf(ax.property2)})`;
120
+ case A.OBJECT_PROPERTY_DOMAIN: return `ObjectPropertyDomain(${iriOf(ax.property)} ${classExpr(ax.domain)})`;
121
+ case A.OBJECT_PROPERTY_RANGE: return `ObjectPropertyRange(${iriOf(ax.property)} ${classExpr(ax.range)})`;
122
+ case A.DATA_PROPERTY_DOMAIN: return `DataPropertyDomain(${iriOf(ax.property)} ${classExpr(ax.domain)})`;
123
+ case A.DATA_PROPERTY_RANGE: return `DataPropertyRange(${iriOf(ax.property)} ${dataRange(ax.range)})`;
124
+ case A.FUNCTIONAL_OBJECT_PROPERTY: return `FunctionalObjectProperty(${iriOf(ax.property)})`;
125
+ case A.INVERSE_FUNCTIONAL_OBJECT_PROPERTY: return `InverseFunctionalObjectProperty(${iriOf(ax.property)})`;
126
+ case A.TRANSITIVE_OBJECT_PROPERTY: return `TransitiveObjectProperty(${iriOf(ax.property)})`;
127
+ case A.SYMMETRIC_OBJECT_PROPERTY: return `SymmetricObjectProperty(${iriOf(ax.property)})`;
128
+ case A.ASYMMETRIC_OBJECT_PROPERTY: return `AsymmetricObjectProperty(${iriOf(ax.property)})`;
129
+ case A.REFLEXIVE_OBJECT_PROPERTY: return `ReflexiveObjectProperty(${iriOf(ax.property)})`;
130
+ case A.IRREFLEXIVE_OBJECT_PROPERTY: return `IrreflexiveObjectProperty(${iriOf(ax.property)})`;
131
+ case A.FUNCTIONAL_DATA_PROPERTY: return `FunctionalDataProperty(${iriOf(ax.property)})`;
132
+ case A.SUB_PROPERTY_CHAIN_OF: return `SubObjectPropertyOf(ObjectPropertyChain(${ax.propertyChain.map(iriOf).join(' ')}) ${iriOf(ax.superProperty)})`;
133
+ case A.CLASS_ASSERTION: return `ClassAssertion(${classExpr(ax.classExpression)} ${ind(ax.individual)})`;
134
+ case A.OBJECT_PROPERTY_ASSERTION: return `ObjectPropertyAssertion(${iriOf(ax.property)} ${ind(ax.subject)} ${ind(ax.object)})`;
135
+ case A.DATA_PROPERTY_ASSERTION: return `DataPropertyAssertion(${iriOf(ax.property)} ${ind(ax.subject)} ${literal(ax.literal)})`;
136
+ case A.NEGATIVE_OBJECT_PROPERTY_ASSERTION: return `NegativeObjectPropertyAssertion(${iriOf(ax.property)} ${ind(ax.subject)} ${ind(ax.object)})`;
137
+ case A.NEGATIVE_DATA_PROPERTY_ASSERTION: return `NegativeDataPropertyAssertion(${iriOf(ax.property)} ${ind(ax.subject)} ${literal(ax.literal)})`;
138
+ case A.SAME_INDIVIDUAL: return `SameIndividual(${ax.individuals.map(ind).join(' ')})`;
139
+ case A.DIFFERENT_INDIVIDUALS: return `DifferentIndividuals(${ax.individuals.map(ind).join(' ')})`;
140
+ case A.HAS_KEY: return `HasKey(${classExpr(ax.classExpression)} (${ax.propertyExpressions.map(iriOf).join(' ')}))`;
141
+ case A.ANNOTATION_ASSERTION: {
142
+ const v = ax.value && ax.value.lexicalValue !== undefined ? literal(ax.value) : iriOf(ax.value);
143
+ return `AnnotationAssertion(${iriOf(ax.property)} ${ax.subject ? iriOf(ax.subject) : ''} ${v})`;
144
+ }
145
+ case A.DATATYPE_DEFINITION: return `DatatypeDefinition(${iriOf(ax.datatype)} ${dataRange(ax.dataRange)})`;
146
+ case A.SUB_ANNOTATION_PROPERTY_OF: return `SubAnnotationPropertyOf(${iriOf(ax.subProperty)} ${iriOf(ax.superProperty)})`;
147
+ case A.ANNOTATION_PROPERTY_DOMAIN: return `AnnotationPropertyDomain(${iriOf(ax.property)} ${iriOf(ax.domain)})`;
148
+ case A.ANNOTATION_PROPERTY_RANGE: return `AnnotationPropertyRange(${iriOf(ax.property)} ${iriOf(ax.range)})`;
149
+ default: return null;
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Serialize an OWLOntology to OWL 2 Functional Syntax.
155
+ * @param {OWLOntology} ont
156
+ * @returns {string}
157
+ */
158
+ function writeFunctionalSyntax(ont) {
159
+ const lines = [];
160
+ const id = ont.getOntologyID();
161
+ const head = [];
162
+ if (id.ontologyIRI) {
163
+ head.push(iriOf(id.ontologyIRI));
164
+ if (id.versionIRI) head.push(iriOf(id.versionIRI));
165
+ }
166
+ for (const imp of ont.imports || []) {
167
+ head.push(`Import(${iriOf(imp)})`);
168
+ }
169
+ for (const ann of ont.getOntologyAnnotations()) {
170
+ const v = ann.value && ann.value.lexicalValue !== undefined ? literal(ann.value) : iriOf(ann.value);
171
+ head.push(`Annotation(${iriOf(ann.property)} ${v})`);
172
+ }
173
+ const axioms = ont.getAxioms().map(axiom).filter(Boolean);
174
+ lines.push(`Ontology(${head.join(' ')}`);
175
+ for (const a of axioms) lines.push(' ' + a);
176
+ lines.push(')');
177
+ return lines.join('\n');
178
+ }
179
+
180
+ module.exports = { writeFunctionalSyntax, classExpr, dataRange, literal };