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 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | 93x 1x 53x 31x 22x 17x 1x 16x 3874x 2410x 1464x 1464x 48x 32x 58x 32x 16x 16x 22x 16x 16x 22x 22x 10x 10x 22x 16x 32x 12x 12x 16x 44x 132x 94x 2x 36x 36x | import { BaseQuad, DatasetCore, Literal, Quad, Quad_Graph, Term } from '@rdfjs/types';
import { DataFactory } from 'rdf-data-factory';
import { RdfStore } from 'rdf-stores';
import { LanguageTagUsageStats, Store, XSD, RDF, RDFS } from '@faubulous/mentor-rdf';
/**
* An RDF/JS DataFactory extended with a `dataset()` method (DatasetCoreFactory),
* as required by libraries that consume an RDF/JS environment such as shacl-engine.
*
* `literal()` must tolerate `null` as the language/datatype argument, because
* shacl-engine calls `factory.literal(text, message.language || null)` when there
* is no language tag. The rdf-data-factory implementation treats `null` like
* `undefined` and returns an `xsd:string` literal.
*/
export const rdfDataFactory = Object.assign(new DataFactory(), {
dataset(): DatasetCore {
return RdfStore.createDefault().asDataset();
}
});
/**
* The type of an RDF property derived from its range.
*/
export type PropertyType = 'objectProperty' | 'dataProperty' | 'annotationProperty';
/**
* Classifies a property as a data property or an object property (relation)
* based on its range or datatype IRI. Literal-valued ranges (XSD datatypes,
* rdfs:Literal, rdf:langString) indicate a data property; everything else is
* treated as an object property.
*
* This is the shared classification used by the definition tree icons and the
* completion item kinds so that both surfaces categorize properties identically.
*
* @param rangeIri The IRI of the property's range or datatype, if known.
* @returns The property type derived from the range.
*/
export function getPropertyTypeFromRange(rangeIri?: string): PropertyType {
switch (rangeIri) {
case RDF.langString:
case RDFS.Literal:
case XSD.base64Binary:
case XSD.boolean:
case XSD.byte:
case XSD.date:
case XSD.dateTime:
case XSD.decimal:
case XSD.double:
case XSD.float:
case XSD.int:
case XSD.integer:
case XSD.long:
case XSD.negativeInteger:
case XSD.nonNegativeInteger:
case XSD.nonPositiveInteger:
case XSD.positiveInteger:
case XSD.short:
case XSD.string:
case XSD.unsignedInt:
case XSD.unsignedShort:
case XSD.unsignedLong:
case XSD.unsignedByte: {
return 'dataProperty';
}
default: {
return 'objectProperty';
}
}
}
/**
* Loads RDF content into a store graph, choosing the loader by the source file's
* extension: `.nq` is parsed as N-Quads, everything else (`.ttl`, `.nt`) through
* the Turtle loader, which handles both. Inference is skipped and the target
* graph is replaced. The single source of truth for the extension → loader
* dispatch that shape and template loading rely on.
* @param store The RDF store to load into.
* @param content The serialized RDF content.
* @param graphUri The graph URI to load the triples under.
* @param fileName The source file name or path whose extension selects the loader.
*/
export function loadRdfContent(store: Store, content: string, graphUri: string, fileName: string): void {
if (fileName.endsWith('.nq')) {
store.loadNQuads(content, graphUri, false, true);
} else {
store.loadTurtle(content, graphUri, false, true);
}
}
/**
* Counts the language tag of a literal object in language tag usage statistics. Literals
* without a language tag are counted under the empty tag, other terms are ignored.
* @param stats The statistics to update.
* @param object The object of a triple.
*/
export function addLanguageTagUsage(stats: LanguageTagUsageStats, object: Term): void {
if (object.termType !== 'Literal') {
return;
}
const language = (object as Literal).language;
stats[language] = (stats[language] ?? 0) + 1;
}
/**
* Updates the triples of a graph to the given quads. Only the quads that are not in the graph
* yet are added and only the quads that are no longer given are deleted, so a graph whose
* triples did not change is not modified at all and keeps its version in the store.
* @param store The store that holds the graph.
* @param graph The graph to update. The given quads must be in this graph.
* @param quads The quads the graph should contain.
* @returns `true` if the graph was changed, otherwise `false`.
*/
export function replaceGraphQuads(store: Store, graph: Quad_Graph, quads: Quad[]): boolean {
if (!store.hasGraph(graph)) {
for (const quad of quads) {
store.add(quad);
}
return quads.length > 0;
}
// The quads of the graph by their triple. The entry of a triple that is among the given
// quads is set to `undefined`, so the quads left in the map are the deleted ones.
const graphQuads = new Map<string, Quad | undefined>();
for (const quad of store.matchQuads(null, null, null, graph)) {
graphQuads.set(getTripleKey(quad), quad);
}
let changed = false;
for (const quad of quads) {
const key = getTripleKey(quad);
if (graphQuads.get(key) === undefined && !graphQuads.has(key)) {
store.add(quad);
changed = true;
}
graphQuads.set(key, undefined);
}
for (const quad of graphQuads.values()) {
if (quad) {
store.delete(quad);
changed = true;
}
}
return changed;
}
/**
* Returns a key that identifies the subject, predicate and object of a quad. The terms are
* separated by U+0000, which cannot occur in the keys of subjects and predicates; the object,
* whose literal value may contain it, comes last.
* @param quad A quad.
* @returns A key that is equal for two quads if and only if their triples are equal.
*/
function getTripleKey(quad: BaseQuad): string {
return getTermKey(quad.subject) + '\u0000' + getTermKey(quad.predicate) + '\u0000' + getTermKey(quad.object);
}
/**
* Returns a key that identifies a term.
* @param term A term.
* @returns A key that is equal for two terms if and only if the terms are equal.
*/
function getTermKey(term: Term): string {
switch (term.termType) {
case 'NamedNode': {
// IRIs contain neither spaces nor `"`, so they cannot be mistaken for the keys below.
return term.value;
}
case 'BlankNode': {
return ' ' + term.value;
}
case 'Literal': {
const literal = term as Literal;
// The language tag and the datatype IRI contain neither `^` nor U+0000, so the
// value, which may contain any character, can follow them unambiguously.
return '"' + literal.language + '^' + literal.datatype.value + '\u0000' + literal.value;
}
default: {
// Triple terms and other terms are rare; JSON escapes U+0000 in their values.
return term.termType + JSON.stringify(term.termType === 'Quad' ? getTripleKey(term as BaseQuad) : term.value);
}
}
}
|