@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,356 @@
1
+ 'use strict';
2
+
3
+ // ---------------------------------------------------------------------------
4
+ // io/OWLXMLParser — parse OWL 2 XML Serialization (OWL/XML).
5
+ // Spec: https://www.w3.org/TR/owl2-xml-serialization/
6
+ //
7
+ // <Ontology ontologyIRI="..." xmlns="http://www.w3.org/2002/07/owl#">
8
+ // <Prefix name="ex" IRI="http://ex.org/"/>
9
+ // <Declaration><Class IRI="..."/></Declaration>
10
+ // <SubClassOf><Class IRI="A"/><Class IRI="B"/></SubClassOf>
11
+ // <SubClassOf>
12
+ // <Class IRI="A"/>
13
+ // <ObjectSomeValuesFrom>
14
+ // <ObjectProperty IRI="p"/>
15
+ // <Class IRI="B"/>
16
+ // </ObjectSomeValuesFrom>
17
+ // </SubClassOf>
18
+ // </Ontology>
19
+ // ---------------------------------------------------------------------------
20
+
21
+ const { OWLOntology, OWLOntologyID } = require('../model/OWLOntology');
22
+ const {
23
+ OWLClass, OWLObjectProperty, OWLDataProperty, OWLNamedIndividual,
24
+ OWLAnnotationProperty, OWLDatatype, OWLAnonymousIndividual
25
+ } = require('../model/OWLEntity');
26
+ const AX = require('../model/OWLAxiom');
27
+ const CE = require('../model/OWLClassExpression');
28
+ const { OWLLiteral } = require('../model/OWLLiteral');
29
+ const { IRI } = require('../model/IRI');
30
+
31
+ const XSD = 'http://www.w3.org/2001/XMLSchema#';
32
+
33
+ // --- Minimal XML tokenizer (no deps) -----------------------------------------
34
+
35
+ function tokenizeXML(s) {
36
+ const toks = [];
37
+ let i = 0;
38
+ while (i < s.length) {
39
+ if (s[i] === '<') {
40
+ if (s.startsWith('<!--', i)) {
41
+ const end = s.indexOf('-->', i);
42
+ i = end < 0 ? s.length : end + 3;
43
+ continue;
44
+ }
45
+ if (s.startsWith('<?', i)) {
46
+ const end = s.indexOf('?>', i);
47
+ i = end < 0 ? s.length : end + 2;
48
+ continue;
49
+ }
50
+ if (s.startsWith('<![CDATA[', i)) {
51
+ const end = s.indexOf(']]>', i);
52
+ const text = end < 0 ? s.slice(i + 9) : s.slice(i + 9, end);
53
+ toks.push({ t: 'text', v: text });
54
+ i = end < 0 ? s.length : end + 3;
55
+ continue;
56
+ }
57
+ const end = s.indexOf('>', i);
58
+ if (end < 0) break;
59
+ const inner = s.slice(i + 1, end);
60
+ if (inner.startsWith('/')) {
61
+ toks.push({ t: 'close', v: inner.slice(1).trim() });
62
+ } else if (inner.endsWith('/')) {
63
+ toks.push(parseOpen(inner.slice(0, -1).trim(), true));
64
+ } else {
65
+ toks.push(parseOpen(inner, false));
66
+ }
67
+ i = end + 1;
68
+ } else {
69
+ const end = s.indexOf('<', i);
70
+ const text = end < 0 ? s.slice(i) : s.slice(i, end);
71
+ if (text.trim()) toks.push({ t: 'text', v: text });
72
+ i = end < 0 ? s.length : end;
73
+ }
74
+ }
75
+ return toks;
76
+ }
77
+
78
+ function parseOpen(inner, selfClose) {
79
+ const sp = inner.search(/[\s]/);
80
+ const name = sp < 0 ? inner : inner.slice(0, sp);
81
+ const attrs = {};
82
+ if (sp >= 0) {
83
+ const re = /([\w:.-]+)\s*=\s*"([^"]*)"/g;
84
+ let m;
85
+ while ((m = re.exec(inner)) !== null) attrs[m[1]] = m[2];
86
+ }
87
+ return { t: 'open', v: name, attrs, selfClose };
88
+ }
89
+
90
+ // Build a DOM-ish tree
91
+ function buildTree(toks) {
92
+ const root = { name: '#root', attrs: {}, children: [], text: '' };
93
+ const stack = [root];
94
+ for (const tok of toks) {
95
+ const top = stack[stack.length - 1];
96
+ if (tok.t === 'open') {
97
+ const node = { name: tok.v, attrs: tok.attrs, children: [], text: '' };
98
+ top.children.push(node);
99
+ if (!tok.selfClose) stack.push(node);
100
+ } else if (tok.t === 'close') {
101
+ stack.pop();
102
+ } else if (tok.t === 'text') {
103
+ top.text += tok.v;
104
+ }
105
+ }
106
+ return root;
107
+ }
108
+
109
+ // --- Parser -------------------------------------------------------------------
110
+
111
+ class OWLXMLParser {
112
+ constructor(opts = {}) {
113
+ this.prefixes = Object.assign({
114
+ rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
115
+ rdfs: 'http://www.w3.org/2000/01/rdf-schema#',
116
+ owl: 'http://www.w3.org/2002/07/owl#',
117
+ xsd: XSD
118
+ }, opts.prefixes || {});
119
+ this.base = opts.base || 'http://example.org/';
120
+ }
121
+
122
+ resolveIRI(v) {
123
+ if (!v) return null;
124
+ if (v.startsWith('http://') || v.startsWith('https://') || v.startsWith('urn:')) return v;
125
+ const idx = v.indexOf(':');
126
+ if (idx > 0) {
127
+ const ns = this.prefixes[v.slice(0, idx)];
128
+ if (ns) return ns + v.slice(idx + 1);
129
+ }
130
+ return this.base + v;
131
+ }
132
+
133
+ iriOf(node) {
134
+ return this.resolveIRI(node.attrs.IRI || node.attrs.abbreviatedIRI || node.attrs.nodeID);
135
+ }
136
+
137
+ parse(text, ontologyIRI = null) {
138
+ const tree = buildTree(tokenizeXML(text));
139
+ const ontoNode = tree.children.find(c => c.name === 'Ontology');
140
+ if (!ontoNode) throw new Error('OWL/XML: no <Ontology> root element');
141
+
142
+ // Collect Prefix declarations (also record on the ontology for fidelity)
143
+ for (const ch of ontoNode.children) {
144
+ if (ch.name === 'Prefix') {
145
+ this.prefixes[ch.attrs.name] = ch.attrs.IRI;
146
+ }
147
+ }
148
+
149
+ const oIRI = ontologyIRI || ontoNode.attrs.ontologyIRI || null;
150
+ const ont = new OWLOntology(new OWLOntologyID(oIRI ? IRI.create(oIRI) : null));
151
+ ont.format = 'OWL/XML';
152
+ for (const [pfx, ns] of Object.entries(this.prefixes)) ont.addPrefix(pfx, ns);
153
+
154
+ for (const ch of ontoNode.children) {
155
+ if (ch.name === 'Prefix' || ch.name === 'Import' || ch.name === 'Annotation') {
156
+ if (ch.name === 'Import') {
157
+ ont.imports.push(ch.children.length ? ch.children[0].text.trim() : (ch.text || '').trim());
158
+ }
159
+ continue;
160
+ }
161
+ const ax = this._axiom(ch);
162
+ if (ax) ont.addAxiom(ax);
163
+ }
164
+ return ont;
165
+ }
166
+
167
+ // --- axioms ---------------------------------------------------------------
168
+
169
+ _axiom(node) {
170
+ const kids = node.children;
171
+ switch (node.name) {
172
+ case 'Declaration': {
173
+ const e = this._entity(kids[0]);
174
+ return new AX.OWLDeclarationAxiom(e);
175
+ }
176
+ case 'SubClassOf':
177
+ return new AX.OWLSubClassOfAxiom(this._ce(kids[0]), this._ce(kids[1]));
178
+ case 'EquivalentClasses':
179
+ return new AX.OWLEquivalentClassesAxiom(kids.map(k => this._ce(k)));
180
+ case 'DisjointClasses':
181
+ return new AX.OWLDisjointClassesAxiom(kids.map(k => this._ce(k)));
182
+ case 'DisjointUnion':
183
+ return new AX.OWLDisjointUnionAxiom(this._ce(kids[0]), kids.slice(1).map(k => this._ce(k)));
184
+ case 'SubObjectPropertyOf':
185
+ return new AX.OWLSubObjectPropertyOfAxiom(this._ope(kids[0]), this._ope(kids[1]));
186
+ case 'EquivalentObjectProperties':
187
+ return new AX.OWLEquivalentObjectPropertiesAxiom(kids.map(k => this._ope(k)));
188
+ case 'DisjointObjectProperties':
189
+ return new AX.OWLDisjointObjectPropertiesAxiom(kids.map(k => this._ope(k)));
190
+ case 'InverseObjectProperties':
191
+ return new AX.OWLInverseObjectPropertiesAxiom(this._ope(kids[0]), this._ope(kids[1]));
192
+ case 'ObjectPropertyDomain':
193
+ return new AX.OWLObjectPropertyDomainAxiom(this._ope(kids[0]), this._ce(kids[1]));
194
+ case 'ObjectPropertyRange':
195
+ return new AX.OWLObjectPropertyRangeAxiom(this._ope(kids[0]), this._ce(kids[1]));
196
+ case 'FunctionalObjectProperty':
197
+ return new AX.OWLFunctionalObjectPropertyAxiom(this._ope(kids[0]));
198
+ case 'InverseFunctionalObjectProperty':
199
+ return new AX.OWLInverseFunctionalObjectPropertyAxiom(this._ope(kids[0]));
200
+ case 'ReflexiveObjectProperty':
201
+ return new AX.OWLReflexiveObjectPropertyAxiom(this._ope(kids[0]));
202
+ case 'IrreflexiveObjectProperty':
203
+ return new AX.OWLIrreflexiveObjectPropertyAxiom(this._ope(kids[0]));
204
+ case 'SymmetricObjectProperty':
205
+ return new AX.OWLSymmetricObjectPropertyAxiom(this._ope(kids[0]));
206
+ case 'AsymmetricObjectProperty':
207
+ return new AX.OWLAsymmetricObjectPropertyAxiom(this._ope(kids[0]));
208
+ case 'TransitiveObjectProperty':
209
+ return new AX.OWLTransitiveObjectPropertyAxiom(this._ope(kids[0]));
210
+ case 'SubDataPropertyOf':
211
+ return new AX.OWLSubDataPropertyOfAxiom(this._dpe(kids[0]), this._dpe(kids[1]));
212
+ case 'EquivalentDataProperties':
213
+ return new AX.OWLEquivalentDataPropertiesAxiom(kids.map(k => this._dpe(k)));
214
+ case 'DisjointDataProperties':
215
+ return new AX.OWLDisjointDataPropertiesAxiom(kids.map(k => this._dpe(k)));
216
+ case 'DataPropertyDomain':
217
+ return new AX.OWLDataPropertyDomainAxiom(this._dpe(kids[0]), this._ce(kids[1]));
218
+ case 'DataPropertyRange':
219
+ return new AX.OWLDataPropertyRangeAxiom(this._dpe(kids[0]), this._dr(kids[1]));
220
+ case 'FunctionalDataProperty':
221
+ return new AX.OWLFunctionalDataPropertyAxiom(this._dpe(kids[0]));
222
+ case 'ClassAssertion':
223
+ return new AX.OWLClassAssertionAxiom(this._ind(kids[1]), this._ce(kids[0]));
224
+ case 'ObjectPropertyAssertion':
225
+ return new AX.OWLObjectPropertyAssertionAxiom(this._ind(kids[1]), this._ope(kids[0]), this._ind(kids[2]));
226
+ case 'NegativeObjectPropertyAssertion':
227
+ return new AX.OWLNegativeObjectPropertyAssertionAxiom(this._ind(kids[1]), this._ope(kids[0]), this._ind(kids[2]));
228
+ case 'DataPropertyAssertion':
229
+ return new AX.OWLDataPropertyAssertionAxiom(this._ind(kids[1]), this._dpe(kids[0]), this._lit(kids[2]));
230
+ case 'NegativeDataPropertyAssertion':
231
+ return new AX.OWLNegativeDataPropertyAssertionAxiom(this._ind(kids[1]), this._dpe(kids[0]), this._lit(kids[2]));
232
+ case 'SameIndividual':
233
+ return new AX.OWLSameIndividualAxiom(kids.map(k => this._ind(k)));
234
+ case 'DifferentIndividuals':
235
+ return new AX.OWLDifferentIndividualsAxiom(kids.map(k => this._ind(k)));
236
+ case 'HasKey':
237
+ return new AX.OWLHasKeyAxiom(this._ce(kids[0]), kids.slice(1).map(k => k.name === 'DataProperty' ? this._dpe(k) : this._ope(k)));
238
+ case 'SubAnnotationPropertyOf':
239
+ return new AX.OWLSubAnnotationPropertyOfAxiom(
240
+ new OWLAnnotationProperty(this.iriOf(kids[0])), new OWLAnnotationProperty(this.iriOf(kids[1])));
241
+ case 'AnnotationAssertion':
242
+ return new AX.OWLAnnotationAssertionAxiom(
243
+ new OWLAnnotationProperty(this.iriOf(kids[0])),
244
+ this.iriOf(kids[1]),
245
+ kids[2].name === 'Literal' ? this._lit(kids[2]) : this.iriOf(kids[2]));
246
+ default:
247
+ return null;
248
+ }
249
+ }
250
+
251
+ _entity(node) {
252
+ const iri = this.iriOf(node);
253
+ switch (node.name) {
254
+ case 'Class': return new OWLClass(iri);
255
+ case 'ObjectProperty': return new OWLObjectProperty(iri);
256
+ case 'DataProperty': return new OWLDataProperty(iri);
257
+ case 'NamedIndividual': return new OWLNamedIndividual(iri);
258
+ case 'AnnotationProperty': return new OWLAnnotationProperty(iri);
259
+ case 'Datatype': return new OWLDatatype(iri);
260
+ default: return new OWLClass(iri);
261
+ }
262
+ }
263
+
264
+ // --- class expressions ------------------------------------------------------
265
+
266
+ _ce(node) {
267
+ const kids = node.children;
268
+ switch (node.name) {
269
+ case 'Class': return new OWLClass(this.iriOf(node));
270
+ case 'ObjectIntersectionOf': return new CE.OWLObjectIntersectionOf(kids.map(k => this._ce(k)));
271
+ case 'ObjectUnionOf': return new CE.OWLObjectUnionOf(kids.map(k => this._ce(k)));
272
+ case 'ObjectComplementOf': return new CE.OWLObjectComplementOf(this._ce(kids[0]));
273
+ case 'ObjectOneOf': return new CE.OWLObjectOneOf(kids.map(k => this._ind(k)));
274
+ case 'ObjectSomeValuesFrom': return new CE.OWLObjectSomeValuesFrom(this._ope(kids[0]), this._ce(kids[1]));
275
+ case 'ObjectAllValuesFrom': return new CE.OWLObjectAllValuesFrom(this._ope(kids[0]), this._ce(kids[1]));
276
+ case 'ObjectHasValue': return new CE.OWLObjectHasValue(this._ope(kids[0]), this._ind(kids[1]));
277
+ case 'ObjectHasSelf': return new CE.OWLObjectHasSelf(this._ope(kids[0]));
278
+ case 'ObjectMinCardinality': {
279
+ const n = parseInt(node.attrs.cardinality, 10);
280
+ const T = CE.ClassExpressionType;
281
+ const filler = kids[1] ? this._ce(kids[1]) : null;
282
+ return new CE.OWLObjectCardinalityRestriction(
283
+ filler ? T.OBJECT_MIN_QUALIFIED_CARDINALITY : T.OBJECT_MIN_CARDINALITY,
284
+ n, this._ope(kids[0]), filler);
285
+ }
286
+ case 'ObjectMaxCardinality': {
287
+ const n = parseInt(node.attrs.cardinality, 10);
288
+ const T = CE.ClassExpressionType;
289
+ const filler = kids[1] ? this._ce(kids[1]) : null;
290
+ return new CE.OWLObjectCardinalityRestriction(
291
+ filler ? T.OBJECT_MAX_QUALIFIED_CARDINALITY : T.OBJECT_MAX_CARDINALITY,
292
+ n, this._ope(kids[0]), filler);
293
+ }
294
+ case 'ObjectExactCardinality': {
295
+ const n = parseInt(node.attrs.cardinality, 10);
296
+ const T = CE.ClassExpressionType;
297
+ const filler = kids[1] ? this._ce(kids[1]) : null;
298
+ return new CE.OWLObjectCardinalityRestriction(
299
+ filler ? T.OBJECT_EXACT_QUALIFIED_CARDINALITY : T.OBJECT_EXACT_CARDINALITY,
300
+ n, this._ope(kids[0]), filler);
301
+ }
302
+ case 'DataSomeValuesFrom': return new CE.OWLDataSomeValuesFrom(this._dpe(kids[0]), this._dr(kids[1]));
303
+ case 'DataAllValuesFrom': return new CE.OWLDataAllValuesFrom(this._dpe(kids[0]), this._dr(kids[1]));
304
+ case 'DataHasValue': return new CE.OWLDataHasValue(this._dpe(kids[0]), this._lit(kids[1]));
305
+ default:
306
+ // Fallback: treat as class
307
+ return new OWLClass(this.iriOf(node));
308
+ }
309
+ }
310
+
311
+ _ope(node) {
312
+ if (node.name === 'InverseObjectProperty') {
313
+ // No OWLObjectInverseOf model class yet — unwrap to the base property.
314
+ return this._ope(node.children[0]);
315
+ }
316
+ return new OWLObjectProperty(this.iriOf(node));
317
+ }
318
+
319
+ _dpe(node) {
320
+ return new OWLDataProperty(this.iriOf(node));
321
+ }
322
+
323
+ _dr(node) {
324
+ switch (node.name) {
325
+ case 'Datatype': return new OWLDatatype(this.iriOf(node));
326
+ case 'DatatypeRestriction': {
327
+ const dt = new OWLDatatype(this.iriOf(node.children[0]));
328
+ const facets = node.children.slice(1).map(f => ({
329
+ facet: IRI.create(this.iriOf(f)),
330
+ value: this._lit(f.children[0])
331
+ }));
332
+ return new CE.OWLDatatypeRestriction(dt, facets);
333
+ }
334
+ case 'DataIntersectionOf': return new CE.OWLDataIntersectionOf(node.children.map(k => this._dr(k)));
335
+ case 'DataUnionOf': return new CE.OWLDataUnionOf(node.children.map(k => this._dr(k)));
336
+ case 'DataComplementOf': return new CE.OWLDataComplementOf(this._dr(node.children[0]));
337
+ case 'DataOneOf': return new CE.OWLDataOneOf(node.children.map(k => this._lit(k)));
338
+ default: return new OWLDatatype(this.iriOf(node));
339
+ }
340
+ }
341
+
342
+ _ind(node) {
343
+ if (node.name === 'AnonymousIndividual') {
344
+ return new OWLAnonymousIndividual(node.attrs.nodeID || ('_:' + Math.random().toString(36).slice(2)));
345
+ }
346
+ return new OWLNamedIndividual(this.iriOf(node));
347
+ }
348
+
349
+ _lit(node) {
350
+ const dt = node.attrs.datatypeIRI ? new OWLDatatype(this.resolveIRI(node.attrs.datatypeIRI)) : null;
351
+ const lang = node.attrs['xml:lang'] || null;
352
+ return new OWLLiteral(node.text.trim(), dt, lang);
353
+ }
354
+ }
355
+
356
+ module.exports = { OWLXMLParser };
@@ -0,0 +1,125 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { parseRDFXML } = require('./RDFXMLParser');
6
+ const { FunctionalSyntaxParser } = require('./FunctionalSyntaxParser');
7
+ const { TurtleParser } = require('./TurtleParser');
8
+ const { triplesToOntology } = require('./RDFGraphToOntology');
9
+ const { OWLOntology, OWLOntologyID } = require('../model/OWLOntology');
10
+ const { IRI } = require('../model/IRI');
11
+
12
+ // ---------------------------------------------------------------------------
13
+ // io/OntologyLoader — mirrors org.protege.editor.owl.model.io.OntologyLoader
14
+ // Supports RDF/XML (.owl/.rdf/.xml), OWL Functional Syntax (.ofn/.func/.owl),
15
+ // Turtle (.ttl/.n3). Can follow owl:imports transitively with cycle guard.
16
+ // ---------------------------------------------------------------------------
17
+
18
+ class OntologyLoader {
19
+ /**
20
+ * Detect format from file extension or content sniff.
21
+ * @returns {'RDFXML'|'Functional'|'Turtle'}
22
+ */
23
+ detectFormat(filePath, text) {
24
+ const ext = path.extname(filePath || '').toLowerCase();
25
+ if (ext === '.ttl' || ext === '.n3') return 'Turtle';
26
+ if (ext === '.ofn' || ext === '.func' || ext === '.omn') return 'Functional';
27
+ const head = (text || '').slice(0, 512).trim();
28
+ if (head.startsWith('<')) return 'RDFXML';
29
+ if (/^(Prefix|Ontology|Declaration|SubClassOf|Class)\s*\(/.test(head)) return 'Functional';
30
+ if (/@prefix|@base|^PREFIX\s/i.test(head)) return 'Turtle';
31
+ return 'RDFXML';
32
+ }
33
+
34
+ /**
35
+ * Load an ontology from a local file path.
36
+ * @param {string} filePath
37
+ * @returns {OWLOntology}
38
+ */
39
+ loadFromFile(filePath) {
40
+ const text = fs.readFileSync(filePath, 'utf8');
41
+ const fmt = this.detectFormat(filePath, text);
42
+ let ont;
43
+ if (fmt === 'Turtle') {
44
+ const store = new TurtleParser().parse(text);
45
+ ont = triplesToOntology(store, {});
46
+ } else if (fmt === 'Functional') {
47
+ ont = new FunctionalSyntaxParser().parse(text);
48
+ } else {
49
+ ont = parseRDFXML(text);
50
+ }
51
+ ont.format = fmt;
52
+ ont.source = path.resolve(filePath);
53
+ return ont;
54
+ }
55
+
56
+ /**
57
+ * Load from a string. Format is auto-detected unless given.
58
+ */
59
+ loadFromString(text, ontologyIRI = null, format = null) {
60
+ const fmt = format || this.detectFormat(null, text);
61
+ if (fmt === 'Turtle') {
62
+ const store = new TurtleParser().parse(text);
63
+ return triplesToOntology(store, { ontologyIRI });
64
+ }
65
+ if (fmt === 'Functional') {
66
+ return new FunctionalSyntaxParser().parse(text, ontologyIRI || undefined);
67
+ }
68
+ return parseRDFXML(text, ontologyIRI);
69
+ }
70
+
71
+ /**
72
+ * Load an ontology and follow its owl:imports transitively (H6).
73
+ * Imported ontologies are merged into a single result ontology. A `visited`
74
+ * set prevents infinite recursion on cyclic imports.
75
+ *
76
+ * Import resolution strategy: if the import IRI looks like a relative or
77
+ * absolute local path (file exists), load it; otherwise skip silently
78
+ * (network imports are out of scope for a local library).
79
+ *
80
+ * @param {string} filePath
81
+ * @param {(iri:string, fromFile:string)=>string|null} [resolveImport]
82
+ * Map an import IRI to a local file path, or null to skip. Default: treat
83
+ * import IRI as a path relative to the importing file.
84
+ * @returns {OWLOntology} merged ontology (imports' axioms included)
85
+ */
86
+ loadWithImports(filePath, resolveImport = null) {
87
+ const visited = new Set();
88
+ const merged = new OWLOntology(new OWLOntologyID());
89
+ const resolver = resolveImport || ((iri, fromFile) => {
90
+ if (fs.existsSync(iri)) return iri;
91
+ const rel = path.resolve(path.dirname(fromFile), iri);
92
+ if (fs.existsSync(rel)) return rel;
93
+ return null;
94
+ });
95
+ const walk = (fp) => {
96
+ const abs = path.resolve(fp);
97
+ if (visited.has(abs)) return;
98
+ visited.add(abs);
99
+ let ont;
100
+ try {
101
+ ont = this.loadFromFile(abs);
102
+ } catch {
103
+ return;
104
+ }
105
+ if (!merged.getOntologyID().ontologyIRI && ont.getOntologyID().ontologyIRI) {
106
+ merged.id.ontologyIRI = ont.getOntologyID().ontologyIRI;
107
+ merged.id.versionIRI = ont.getOntologyID().versionIRI;
108
+ }
109
+ for (const ax of ont.getAxioms()) merged.addAxiom(ax);
110
+ for (const ann of ont.getOntologyAnnotations()) merged.addOntologyAnnotation(ann);
111
+ for (const imp of ont.imports || []) {
112
+ const impStr = imp && imp.toString ? imp.toString() : String(imp);
113
+ merged.imports.push(impStr);
114
+ const target = resolver(impStr, abs);
115
+ if (target) walk(target);
116
+ }
117
+ };
118
+ walk(filePath);
119
+ merged.format = 'merged(' + (this.detectFormat(filePath, '')) + ')';
120
+ return merged;
121
+ }
122
+ }
123
+
124
+ module.exports = { OntologyLoader };
125
+