@faubulous/mentor-rdf 2.6.0 → 2.6.1

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/dist/index.js CHANGED
@@ -480,6 +480,163 @@ var ResourceRepository = class {
480
480
  }
481
481
  return primary;
482
482
  }
483
+ /**
484
+ * Get the hierarchy relations of a predicate such as `rdfs:subClassOf`, read in a single pass.
485
+ * @param graphUris URIs of the graphs to search, `undefined` for the default graph.
486
+ * @param predicate The hierarchy predicate, e.g. `rdfs:subClassOf` or `rdfs:subPropertyOf`.
487
+ * @param options Optional query parameters.
488
+ * @returns A map from a node, named or blank, to its named parents, excluding the node itself.
489
+ */
490
+ getParentMap(graphUris, predicate, options) {
491
+ const result = /* @__PURE__ */ new Map();
492
+ for (let q of this.store.matchAll(graphUris, null, predicate, null, options?.includeInferred)) {
493
+ if (q.subject.termType != "NamedNode" && q.subject.termType != "BlankNode" || q.object.termType != "NamedNode" || q.subject.value == q.object.value) {
494
+ continue;
495
+ }
496
+ const parents = result.get(q.subject.value);
497
+ if (!parents) {
498
+ result.set(q.subject.value, [q.object.value]);
499
+ } else if (!parents.includes(q.object.value)) {
500
+ parents.push(q.object.value);
501
+ }
502
+ }
503
+ return result;
504
+ }
505
+ /**
506
+ * Get the root nodes of a hierarchy such as the class or the property hierarchy. A member with no
507
+ * visible parent is a root. A parent that is not one of the document's own nodes is referenced:
508
+ * the roots are then the top nodes of its chains of parents, as far as the graphs state them.
509
+ * @param members The nodes whose roots are wanted, e.g. the classes of the document.
510
+ * @param parents The parents of every node, see {@link getParentMap}.
511
+ * @param isOwn Whether a node belongs to the document; a chain that reaches one has no top of its own.
512
+ * @param isVisible Whether a parent is shown in the hierarchy at all.
513
+ * @param canBeRoot Whether a node is listed as a root, be it a member without visible parents or the top of a referenced chain.
514
+ * @returns An iterator of the root nodes.
515
+ */
516
+ *getHierarchyRoots(members, parents, isOwn, isVisible, canBeRoot = () => true) {
517
+ const yielded = /* @__PURE__ */ new Set();
518
+ const getVisibleParents = (iri) => (parents.get(iri) ?? []).filter(isVisible);
519
+ const finished = /* @__PURE__ */ new Set();
520
+ for (let m of members) {
521
+ let hasParent = false;
522
+ for (let p of getVisibleParents(m)) {
523
+ hasParent = true;
524
+ if (!isOwn(p)) {
525
+ for (let r of this._getTopNodes(p, getVisibleParents, isOwn, finished, /* @__PURE__ */ new Set())) {
526
+ if (!yielded.has(r) && canBeRoot(r)) {
527
+ yielded.add(r);
528
+ yield r;
529
+ }
530
+ }
531
+ }
532
+ }
533
+ if (!hasParent && !yielded.has(m) && canBeRoot(m)) {
534
+ yielded.add(m);
535
+ yield m;
536
+ }
537
+ }
538
+ }
539
+ /**
540
+ * Get the top nodes of the chains of parents of a referenced node.
541
+ * @param iri URI of a referenced node.
542
+ * @param getParents A function returning the visible parents of a node.
543
+ * @param isOwn Whether a node belongs to the document; a chain that reaches one has no top of its own.
544
+ * @param finished The referenced nodes whose top nodes were already yielded.
545
+ * @param path The nodes on the current chain, to stop at cycles.
546
+ * @returns An iterator of the top nodes not yielded for another chain before.
547
+ */
548
+ *_getTopNodes(iri, getParents, isOwn, finished, path) {
549
+ if (finished.has(iri)) {
550
+ return;
551
+ }
552
+ path.add(iri);
553
+ let isTop = true;
554
+ for (let p of getParents(iri)) {
555
+ if (isOwn(p) || finished.has(p)) {
556
+ isTop = false;
557
+ } else if (!path.has(p)) {
558
+ isTop = false;
559
+ yield* this._getTopNodes(p, getParents, isOwn, finished, path);
560
+ }
561
+ }
562
+ path.delete(iri);
563
+ finished.add(iri);
564
+ if (isTop) {
565
+ yield iri;
566
+ }
567
+ }
568
+ /**
569
+ * Indicate if a node is only referenced by the document: it is included if referenced terms
570
+ * are included, and it is not defined by another ontology of the document. Annotations or a
571
+ * declaration of a term from another vocabulary do not define it.
572
+ * @param graphUris URIs of the graphs to search, `undefined` for the default graph.
573
+ * @param node A node.
574
+ * @param options Optional query parameters.
575
+ * @param ontologies The other ontologies in the graphs, see {@link getOtherOntologiesOnce}.
576
+ * @returns `true` if the node is a referenced named node, `false` otherwise.
577
+ */
578
+ isReferenced(graphUris, node, options, ontologies) {
579
+ if (!(options?.includeReferenced ?? false) || node.termType != "NamedNode") {
580
+ return false;
581
+ }
582
+ return !ontologies().some((o) => this.isDefinedBy(graphUris, node, o));
583
+ }
584
+ /**
585
+ * Indicate if a node is a referenced term with one of the document's own terms below it in a
586
+ * hierarchy, so that the hierarchy from a referenced root down to the document is not broken.
587
+ * @param graphUris URIs of the graphs to search, `undefined` for the default graph.
588
+ * @param node A node.
589
+ * @param predicate The hierarchy predicate, e.g. `rdfs:subClassOf` or `rdfs:subPropertyOf`.
590
+ * @param options Optional query parameters.
591
+ * @param ontologies The other ontologies in the graphs, see {@link getOtherOntologiesOnce}.
592
+ * @param visited The nodes already visited, to stop at cycles.
593
+ * @returns `true` if a term of the document is a direct or indirect child of the node.
594
+ */
595
+ isReferencedAbove(graphUris, node, predicate, options, ontologies, visited = /* @__PURE__ */ new Set()) {
596
+ if (!this.isReferenced(graphUris, node, options, ontologies)) {
597
+ return false;
598
+ }
599
+ visited.add(node.value);
600
+ for (let q of this.store.matchAll(graphUris, null, predicate, node, options?.includeInferred)) {
601
+ if (visited.has(q.subject.value)) {
602
+ continue;
603
+ }
604
+ if (!this.skip(graphUris, q.subject, options) || this.isReferencedAbove(graphUris, q.subject, predicate, options, ontologies, visited)) {
605
+ return true;
606
+ }
607
+ }
608
+ return false;
609
+ }
610
+ /**
611
+ * Get an accessor for the other ontologies in the graphs that queries them on its first call
612
+ * only, as most queries never need them.
613
+ * @param graphUris URIs of the graphs to search, `undefined` for the default graph.
614
+ * @param options Optional query parameters.
615
+ * @returns A function returning the URIs of the other ontologies, see {@link _getOtherOntologies}.
616
+ */
617
+ getOtherOntologiesOnce(graphUris, options) {
618
+ let ontologies;
619
+ return () => ontologies ?? (ontologies = this._getOtherOntologies(graphUris, options));
620
+ }
621
+ /**
622
+ * Get the ontologies declared in the graphs other than the one the options select with `definedBy`.
623
+ * @param graphUris URIs of the graphs to search, `undefined` for the default graph.
624
+ * @param options Optional query parameters.
625
+ * @returns The URIs of the other ontologies, or none if the options select no ontology.
626
+ */
627
+ _getOtherOntologies(graphUris, options) {
628
+ if (!options?.definedBy) {
629
+ return [];
630
+ }
631
+ const definedBy = Uri.getNormalizedUri(options.definedBy);
632
+ const result = /* @__PURE__ */ new Set();
633
+ for (let q of this.store.matchAll(graphUris, null, rdf.type, owl.Ontology, false)) {
634
+ if (q.subject.termType == "NamedNode" && Uri.getNormalizedUri(q.subject.value) != definedBy) {
635
+ result.add(q.subject.value);
636
+ }
637
+ }
638
+ return [...result];
639
+ }
483
640
  };
484
641
 
485
642
  // src/rdf/concept-repository.ts
@@ -1403,8 +1560,9 @@ var ClassRepository = class extends ConceptRepository {
1403
1560
  */
1404
1561
  hasSubClasses(graphUris, subjectUri, options) {
1405
1562
  const o = namedNode2(subjectUri);
1563
+ const ontologies = this.getOtherOntologiesOnce(graphUris, options);
1406
1564
  for (let q of this.store.matchAll(graphUris, null, rdfs.subClassOf, o)) {
1407
- if (!this.skip(graphUris, q.subject, options)) {
1565
+ if (!this.skip(graphUris, q.subject, options) || this.isReferencedAbove(graphUris, q.subject, rdfs.subClassOf, options, ontologies)) {
1408
1566
  return true;
1409
1567
  }
1410
1568
  }
@@ -1419,8 +1577,12 @@ var ClassRepository = class extends ConceptRepository {
1419
1577
  if (subjectUri) {
1420
1578
  const yielded = /* @__PURE__ */ new Set();
1421
1579
  const o = namedNode2(subjectUri);
1580
+ const ontologies = this.getOtherOntologiesOnce(graphUris, options);
1422
1581
  for (let q of this.store.matchAll(graphUris, null, rdfs.subClassOf, o, options?.includeInferred)) {
1423
- if (!this.skip(graphUris, q.subject, options) && !yielded.has(q.subject.value)) {
1582
+ if (yielded.has(q.subject.value)) {
1583
+ continue;
1584
+ }
1585
+ if (!this.skip(graphUris, q.subject, options) || this.isReferencedAbove(graphUris, q.subject, rdfs.subClassOf, options, ontologies)) {
1424
1586
  yielded.add(q.subject.value);
1425
1587
  yield q.subject.value;
1426
1588
  }
@@ -1601,28 +1763,23 @@ var ClassRepository = class extends ConceptRepository {
1601
1763
  * @returns An iterator of root classes in the repository.
1602
1764
  */
1603
1765
  *getRootClasses(graphUris, options) {
1604
- const yielded = /* @__PURE__ */ new Set();
1605
1766
  const classes = new Set(this.getClasses(graphUris, options));
1606
- for (let c of classes) {
1607
- let hasSuperClass = false;
1608
- for (let q of this.store.matchAll(graphUris, namedNode2(c), rdfs.subClassOf, null, options?.includeInferred)) {
1609
- const includeReferenced = options?.includeReferenced ?? false;
1610
- const skip = this.skip(graphUris, q.object, options);
1611
- if (q.object.termType != "NamedNode" || skip && (!includeReferenced || includeReferenced && this.hasSubject(graphUris, q.object.value))) {
1612
- continue;
1613
- }
1614
- hasSuperClass = q.object.value != q.subject.value;
1615
- if (!classes.has(q.object.value) && !yielded.has(q.object.value)) {
1616
- yielded.add(q.object.value);
1617
- yield q.object.value;
1618
- }
1619
- break;
1767
+ const ontologies = this.getOtherOntologiesOnce(graphUris, options);
1768
+ const superClasses = this.getParentMap(graphUris, rdfs.subClassOf, options);
1769
+ const visible = /* @__PURE__ */ new Map();
1770
+ const isVisible = (iri) => {
1771
+ if (classes.has(iri)) {
1772
+ return true;
1620
1773
  }
1621
- if (!hasSuperClass && !yielded.has(c)) {
1622
- yielded.add(c);
1623
- yield c;
1774
+ let result = visible.get(iri);
1775
+ if (result === void 0) {
1776
+ const node = namedNode2(iri);
1777
+ result = !this.skip(graphUris, node, options) || this.isReferenced(graphUris, node, options, ontologies);
1778
+ visible.set(iri, result);
1624
1779
  }
1625
- }
1780
+ return result;
1781
+ };
1782
+ yield* this.getHierarchyRoots(classes, superClasses, (iri) => classes.has(iri), isVisible);
1626
1783
  }
1627
1784
  /**
1628
1785
  * Indicate if there is an equivalent class of a given class.
@@ -1888,38 +2045,41 @@ var PropertyRepository = class extends ClassRepository {
1888
2045
  * @returns An iterator of properties of the given type.
1889
2046
  */
1890
2047
  *getRootPropertiesOfType(graphUris, typeUri, options) {
1891
- const yielded = /* @__PURE__ */ new Set();
1892
- const type = namedNode3(typeUri);
1893
- for (let q of this.store.matchAll(graphUris, null, rdf.type, type, options?.includeInferred)) {
1894
- if (this.skip(graphUris, q.subject, options)) {
1895
- continue;
1896
- }
1897
- let hasSuperProperty = false;
1898
- let referencedSuperProperty = null;
1899
- for (let q2 of this.store.matchAll(graphUris, q.subject, rdfs.subPropertyOf, null, options?.includeInferred)) {
1900
- if (options?.includeReferenced && !this.hasSubject(graphUris, q2.object.value)) {
1901
- referencedSuperProperty = q2.object.value;
1902
- } else if (this.skip(graphUris, q2.object, options)) {
1903
- continue;
2048
+ const members = /* @__PURE__ */ new Map();
2049
+ for (let q of this.store.matchAll(graphUris, null, rdf.type, namedNode3(typeUri), options?.includeInferred)) {
2050
+ if (!this.skip(graphUris, q.subject, options)) {
2051
+ members.set(q.subject.value, q.subject);
2052
+ }
2053
+ }
2054
+ const ontologies = this.getOtherOntologiesOnce(graphUris, options);
2055
+ const superProperties = this.getParentMap(graphUris, rdfs.subPropertyOf, options);
2056
+ const referenced = /* @__PURE__ */ new Map();
2057
+ const own = /* @__PURE__ */ new Map();
2058
+ const isReferenced = (iri) => {
2059
+ let result = referenced.get(iri);
2060
+ if (result === void 0) {
2061
+ const node = namedNode3(iri);
2062
+ if (!(options?.includeReferenced ?? false)) {
2063
+ result = false;
2064
+ } else if (options?.definedBy) {
2065
+ result = this.skip(graphUris, node, options) && this.isReferenced(graphUris, node, options, ontologies);
2066
+ } else {
2067
+ result = !this.hasSubject(graphUris, iri);
1904
2068
  }
1905
- hasSuperProperty = q2.object.value != q2.subject.value;
1906
- break;
2069
+ referenced.set(iri, result);
1907
2070
  }
1908
- if (referencedSuperProperty && !yielded.has(referencedSuperProperty)) {
1909
- yielded.add(referencedSuperProperty);
1910
- yield referencedSuperProperty;
1911
- }
1912
- if (hasSuperProperty) {
1913
- continue;
1914
- }
1915
- if (typeUri === RDF.Property && this._hasSpecificType(graphUris, q.subject)) {
1916
- continue;
1917
- }
1918
- if (!yielded.has(q.subject.value)) {
1919
- yielded.add(q.subject.value);
1920
- yield q.subject.value;
2071
+ return result;
2072
+ };
2073
+ const isOwn = (iri) => {
2074
+ let result = own.get(iri);
2075
+ if (result === void 0) {
2076
+ result = !isReferenced(iri) && !this.skip(graphUris, namedNode3(iri), options);
2077
+ own.set(iri, result);
1921
2078
  }
1922
- }
2079
+ return result;
2080
+ };
2081
+ const canBeRoot = (iri) => typeUri !== RDF.Property || !this._hasSpecificType(graphUris, members.get(iri) ?? namedNode3(iri));
2082
+ yield* this.getHierarchyRoots(members.keys(), superProperties, isOwn, (iri) => isOwn(iri) || isReferenced(iri), canBeRoot);
1923
2083
  }
1924
2084
  /**
1925
2085
  * Indicate whether a property is stated to be of a type that is more specific than rdf:Property.
@@ -1999,8 +2159,9 @@ var PropertyRepository = class extends ClassRepository {
1999
2159
  */
2000
2160
  hasSubProperties(graphUris, subjectUri, options) {
2001
2161
  const o = namedNode3(subjectUri);
2162
+ const ontologies = this.getOtherOntologiesOnce(graphUris, options);
2002
2163
  for (let q of this.store.matchAll(graphUris, null, rdfs.subPropertyOf, o, options?.includeInferred)) {
2003
- if (!this.skip(graphUris, q.subject, options)) {
2164
+ if (!this.skip(graphUris, q.subject, options) || this.isReferencedAbove(graphUris, q.subject, rdfs.subPropertyOf, options, ontologies)) {
2004
2165
  return true;
2005
2166
  }
2006
2167
  }
@@ -2016,8 +2177,9 @@ var PropertyRepository = class extends ClassRepository {
2016
2177
  if (subjectUri) {
2017
2178
  const yielded = /* @__PURE__ */ new Set();
2018
2179
  const o = namedNode3(subjectUri);
2180
+ const ontologies = this.getOtherOntologiesOnce(graphUris, options);
2019
2181
  for (let q of this.store.matchAll(graphUris, null, rdfs.subPropertyOf, o, options?.includeInferred)) {
2020
- if (this.skip(graphUris, q.subject, options)) {
2182
+ if (this.skip(graphUris, q.subject, options) && !this.isReferencedAbove(graphUris, q.subject, rdfs.subPropertyOf, options, ontologies)) {
2021
2183
  continue;
2022
2184
  }
2023
2185
  if (!yielded.has(q.subject.value)) {
@@ -2035,28 +2197,23 @@ var PropertyRepository = class extends ClassRepository {
2035
2197
  * @returns An iterator of root properties in the repository.
2036
2198
  */
2037
2199
  *getRootProperties(graphUris, options) {
2038
- const yielded = /* @__PURE__ */ new Set();
2039
2200
  const properties = new Set(this.getProperties(graphUris, options));
2040
- for (let p of properties) {
2041
- let hasSuperProperty = false;
2042
- for (let q of this.store.matchAll(graphUris, namedNode3(p), rdfs.subPropertyOf, null, options?.includeInferred)) {
2043
- const includeReferenced = options?.includeReferenced ?? false;
2044
- const skip = this.skip(graphUris, q.object, options);
2045
- if (q.object.termType != "NamedNode" || skip && (!includeReferenced || includeReferenced && this.hasSubject(graphUris, q.object.value))) {
2046
- continue;
2047
- }
2048
- hasSuperProperty = q.object.value != q.subject.value;
2049
- if (!properties.has(q.object.value) && !yielded.has(q.object.value)) {
2050
- yielded.add(q.object.value);
2051
- yield q.object.value;
2052
- }
2053
- break;
2201
+ const ontologies = this.getOtherOntologiesOnce(graphUris, options);
2202
+ const superProperties = this.getParentMap(graphUris, rdfs.subPropertyOf, options);
2203
+ const visible = /* @__PURE__ */ new Map();
2204
+ const isVisible = (iri) => {
2205
+ if (properties.has(iri)) {
2206
+ return true;
2054
2207
  }
2055
- if (!hasSuperProperty && !yielded.has(p)) {
2056
- yielded.add(p);
2057
- yield p;
2208
+ let result = visible.get(iri);
2209
+ if (result === void 0) {
2210
+ const node = namedNode3(iri);
2211
+ result = !this.skip(graphUris, node, options) || this.isReferenced(graphUris, node, options, ontologies);
2212
+ visible.set(iri, result);
2058
2213
  }
2059
- }
2214
+ return result;
2215
+ };
2216
+ yield* this.getHierarchyRoots(properties, superProperties, (iri) => properties.has(iri), isVisible);
2060
2217
  }
2061
2218
  /**
2062
2219
  * Indicate if there is an equivalent property of a given property.