All files / rdf/reasoners rdfs-reasoner.ts

100% Statements 73/73
100% Branches 56/56
100% Functions 11/11
100% Lines 72/72

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172            11x           24x   24x   24x   24x     24x       45490x               13483x   32007x         14735x             73x     31011x   73x 2643x         73x 73x 73x 73x       472034x     472034x   45490x 32007x     45490x 55x       472034x 472034x       77149x   77149x       472034x 472034x 472034x   472034x 212688x     259346x   59176x   4981x 54195x 13759x 40436x 2967x   59176x     24555x   24555x 24215x 340x 75x 265x 19x 19x 246x 5x 5x 241x 9x 9x   24555x       5199x 3388x   5199x           18710x   18710x       472034x 472034x 472034x   472034x 212688x     259346x   59176x   8768x     59176x       5199x   5199x        
import * as rdfjs from "@rdfjs/types";
import { owl, rdf, rdfs, skos, sh } from "../../ontologies";
import { SkosReasoner } from "./skos-reasoner";
import { GraphUriGenerator, DefaultInferenceGraphHandler } from "./reasoner";
import { dataFactory } from "../data-factory";
 
const { quad } = dataFactory;
 
/**
 * A simple RDFS reasoner that expands the graph with inferred triples.
 */
export class RdfsReasoner extends SkosReasoner {
    protected ontologies: Set<string> = new Set();
 
    protected classes: Set<string> = new Set();
 
    protected properties: Set<string> = new Set();
 
    protected individuals: Set<rdfjs.Quad_Subject> = new Set();
 
    constructor(targetUriGenerator: GraphUriGenerator = new DefaultInferenceGraphHandler()) {
        super(targetUriGenerator);
    }
 
    protected isIgnoredNode(term: rdfjs.Quad_Subject | rdfjs.Quad_Object): boolean {
        switch (term.value) {
            case skos.Concept.value:
            case skos.ConceptScheme.value:
            case skos.Collection.value:
            case skos.OrderedCollection.value:
            case sh.Shape.value:
            case sh.NodeShape.value:
            case sh.PropertyShape.value:
                return true;
            default:
                return false;
        }
    }
 
    protected isClass(id: string): boolean {
        return super.isClass(id) ||
            this.classes.has(id) ||
            this.properties.has(id) ||
            this.ontologies.has(id);
    }
 
    protected afterInference() {
        super.afterInference();
 
        // After all axioms have been inferred, add the inferred individuals to the graph.
        const individuals = [...this.individuals].filter(x => !this.isClass(x.value));
 
        for (let individual of individuals) {
            this.store.add(quad(individual, rdf.type, owl.NamedIndividual, this.targetGraph));
        }
    }
 
    protected override resetState(): void {
        this.ontologies.clear();
        this.classes.clear();
        this.properties.clear();
        this.individuals.clear();
    }
 
    override applyInference(quad: rdfjs.Quad) {
        super.applyInference(quad);
 
        // Treat all named nodes with rdf:type definitions as potential individuals.
        if (quad.subject.termType == "NamedNode" && quad.predicate.equals(rdf.type)) {
            // Only consider individuals that are not of a type that is ignored such as skos:Concept.
            if (!this.isIgnoredNode(quad.object)) {
                this.individuals.add(quad.subject);
            }
 
            if (quad.object.value == owl.Ontology.value) {
                this.ontologies.add(quad.subject.value);
            }
        }
 
        this.inferClassAxioms(quad as rdfjs.Quad);
        this.inferPropertyAxioms(quad as rdfjs.Quad);
    }
 
    protected assertClass(subject: rdfjs.Quad_Subject) {
        this.store.add(quad(subject, rdf.type, rdfs.Class, this.targetGraph));
 
        this.classes.add(subject.value);
    }
 
    protected inferClassAxioms(q: rdfjs.Quad) {
        let s = q.subject;
        let p = q.predicate;
        let o = q.object.termType != "Literal" ? q.object : undefined;
 
        if (!o) {
            return;
        }
 
        switch (p.value) {
            case rdf.type.value: {
                if (o.equals(rdfs.Class)) {
                    // No need to infer the class type, as it is already asserted.
                    this.classes.add(s.value);
                } else if (o.equals(owl.Class)) {
                    this.assertClass(s);
                } else if (!this.isW3CNode(o)) {
                    this.assertClass(o);
                }
                return;
            }
            case rdfs.subClassOf.value: {
                this.assertClass(s);
 
                if (!this.isW3CNode(o)) {
                    this.assertClass(o);
                } else if (o.equals(rdfs.Resource)) {
                    this.store.add(quad(rdfs.Resource, rdf.type, rdfs.Class, this.targetGraph));
                } else if (o.equals(rdfs.Class)) {
                    this.store.add(quad(rdfs.Class, rdf.type, rdfs.Class, this.targetGraph));
                    this.store.add(quad(rdfs.Class, rdfs.subClassOf, rdfs.Resource, this.targetGraph));
                } else if (o.equals(rdfs.Datatype)) {
                    this.store.add(quad(rdfs.Datatype, rdf.type, rdfs.Class, this.targetGraph));
                    this.store.add(quad(rdfs.Datatype, rdfs.subClassOf, rdfs.Class, this.targetGraph));
                } else if (o.equals(owl.Class)) {
                    this.store.add(quad(owl.Class, rdf.type, rdfs.Class, this.targetGraph));
                    this.store.add(quad(owl.Class, rdfs.subClassOf, rdfs.Class, this.targetGraph));
                }
                return;
            }
            case rdfs.range.value:
            case rdfs.domain.value: {
                if (!this.isW3CNode(o)) {
                    this.assertClass(o);
                }
                return;
            }
        }
    }
 
    protected assertProperty(subject: rdfjs.Quad_Subject) {
        this.store.add(quad(subject, rdf.type, rdf.Property, this.targetGraph));
 
        this.properties.add(subject.value);
    }
 
    protected inferPropertyAxioms(quad: rdfjs.Quad) {
        let s = quad.subject;
        let p = quad.predicate;
        let o = quad.object.termType != "Literal" ? quad.object : undefined;
 
        if (!o) {
            return;
        }
 
        switch (p.value) {
            case rdf.type.value: {
                if (o.equals(rdf.Property)) {
                    // No need to infer the property type, as it is already asserted.
                    this.properties.add(s.value);
                }
 
                return;
            }
            case rdfs.range.value:
            case rdfs.domain.value: {
                this.assertProperty(s);
 
                return;
            }
        }
    }
}