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            12x           23x   23x   23x   23x     23x       44995x               13483x   31512x         14527x             72x     30557x   72x 2613x         72x 72x 72x 72x       466372x     466372x   44995x 31512x     44995x 54x       466372x 466372x       75586x   75586x       466372x 466372x 466372x   466372x 211283x     255089x   58147x   5005x 53142x 13249x 39893x 2939x   58147x     24476x   24476x 24111x 365x 82x 283x 21x 21x 262x 6x 6x 256x 10x 10x   24476x       5129x 3286x   5129x           18112x   18112x       466372x 466372x 466372x   466372x 211283x     255089x   58147x   8808x     58147x       5129x   5129x        
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;
            }
        }
    }
}