UNPKG

@ulb-darmstadt/shacl-form

Version:
154 lines (147 loc) 7.9 kB
import { Literal, NamedNode, Quad, DataFactory } from 'n3' import { Term } from '@rdfjs/types' import { OWL_PREDICATE_IMPORTS, PREFIX_DASH, PREFIX_OA, PREFIX_RDF, PREFIX_SHACL, SHACL_PREDICATE_CLASS, SHACL_PREDICATE_TARGET_CLASS } from './constants' import { Config } from './config' import { findLabel, prioritizeByLanguage, removePrefixes } from './util' import { ShaclNode } from './node' const mappers: Record<string, (template: ShaclPropertyTemplate, term: Term) => void> = { [`${PREFIX_SHACL}name`]: (template, term) => { const literal = term as Literal; template.name = prioritizeByLanguage(template.config.languages, template.name, literal) }, [`${PREFIX_SHACL}description`]: (template, term) => { const literal = term as Literal; template.description = prioritizeByLanguage(template.config.languages, template.description, literal) }, [`${PREFIX_SHACL}path`]: (template, term) => { template.path = term.value }, [`${PREFIX_SHACL}node`]: (template, term) => { template.node = term as NamedNode }, [`${PREFIX_SHACL}datatype`]: (template, term) => { template.datatype = term as NamedNode }, [`${PREFIX_SHACL}nodeKind`]: (template, term) => { template.nodeKind = term as NamedNode }, [`${PREFIX_SHACL}minCount`]: (template, term) => { template.minCount = parseInt(term.value) }, [`${PREFIX_SHACL}maxCount`]: (template, term) => { template.maxCount = parseInt(term.value) }, [`${PREFIX_SHACL}minLength`]: (template, term) => { template.minLength = parseInt(term.value) }, [`${PREFIX_SHACL}maxLength`]: (template, term) => { template.maxLength = parseInt(term.value) }, [`${PREFIX_SHACL}minInclusive`]: (template, term) => { template.minInclusive = parseInt(term.value) }, [`${PREFIX_SHACL}maxInclusive`]: (template, term) => { template.maxInclusive = parseInt(term.value) }, [`${PREFIX_SHACL}minExclusive`]: (template, term) => { template.minExclusive = parseInt(term.value) }, [`${PREFIX_SHACL}maxExclusive`]: (template, term) => { template.maxExclusive = parseInt(term.value) }, [`${PREFIX_SHACL}pattern`]: (template, term) => { template.pattern = term.value }, [`${PREFIX_SHACL}order`]: (template, term) => { template.order = parseInt(term.value) }, [`${PREFIX_DASH}singleLine`]: (template, term) => { template.singleLine = term.value === 'true' }, [`${PREFIX_DASH}readonly`]: (template, term) => { template.readonly = term.value === 'true' }, [`${PREFIX_OA}styleClass`]: (template, term) => { template.cssClass = term.value }, [`${PREFIX_SHACL}and`]: (template, term) => { template.shaclAnd = term.value }, [`${PREFIX_SHACL}in`]: (template, term) => { template.shaclIn = term.value }, // sh:datatype might be undefined, but sh:languageIn defined. this is undesired. the spec says, that strings without a lang tag are not valid if sh:languageIn is set. but the shacl validator accepts these as valid. to prevent this, we just set the datatype here to 'langString'. [`${PREFIX_SHACL}languageIn`]: (template, term) => { template.languageIn = template.config.lists[term.value]; template.datatype = DataFactory.namedNode(PREFIX_RDF + 'langString') }, [`${PREFIX_SHACL}defaultValue`]: (template, term) => { template.defaultValue = term }, [`${PREFIX_SHACL}hasValue`]: (template, term) => { template.hasValue = term }, [`${PREFIX_SHACL}qualifiedValueShape`]: (template, term) => { template.qualifiedValueShape = term }, [`${PREFIX_SHACL}qualifiedMinCount`]: (template, term) => { template.minCount = parseInt(term.value) }, [`${PREFIX_SHACL}qualifiedMaxCount`]: (template, term) => { template.maxCount = parseInt(term.value) }, [OWL_PREDICATE_IMPORTS.id]: (template, term) => { template.owlImports.push(term as NamedNode) }, [SHACL_PREDICATE_CLASS.id]: (template, term) => { template.class = term as NamedNode // try to find node shape that has requested target class const nodeShapes = template.config.store.getSubjects(SHACL_PREDICATE_TARGET_CLASS, term, null) if (nodeShapes.length > 0) { template.node = nodeShapes[0] as NamedNode } }, [`${PREFIX_SHACL}or`]: (template, term) => { const list = template.config.lists[term.value] if (list?.length) { template.shaclOr = list } else { console.error('list for sh:or not found:', term.value, 'existing lists:', template.config.lists) } }, [`${PREFIX_SHACL}xone`]: (template, term) => { const list = template.config.lists[term.value] if (list?.length) { template.shaclXone = list } else { console.error('list for sh:xone not found:', term.value, 'existing lists:', template.config.lists) } } } export class ShaclPropertyTemplate { parent: ShaclNode label = '' name: Literal | undefined description: Literal | undefined path: string | undefined node: NamedNode | undefined class: NamedNode | undefined minCount: number | undefined maxCount: number | undefined minLength: number | undefined maxLength: number | undefined minInclusive: number | undefined maxInclusive: number | undefined minExclusive: number | undefined maxExclusive: number | undefined singleLine: boolean | undefined readonly: boolean | undefined cssClass: string | undefined defaultValue: Term | undefined pattern: string | undefined order: number | undefined nodeKind: NamedNode | undefined shaclAnd: string | undefined shaclIn: string | undefined shaclOr: Term[] | undefined shaclXone: Term[] | undefined languageIn: Term[] | undefined datatype: NamedNode | undefined hasValue: Term | undefined qualifiedValueShape: Term | undefined owlImports: NamedNode[] = [] config: Config extendedShapes: NamedNode[] = [] constructor(quads: Quad[], parent: ShaclNode, config: Config) { this.parent = parent this.config = config this.merge(quads) if (this.qualifiedValueShape) { this.merge(config.store.getQuads(this.qualifiedValueShape, null, null, null)) } } merge(quads: Quad[]): ShaclPropertyTemplate { for (const quad of quads) { mappers[quad.predicate.id]?.call(this, this, quad.object) } // provide best fitting label for UI this.label = this.name?.value || findLabel(quads, this.config.languages) if (!this.label && !this.shaclAnd) { this.label = this.path ? removePrefixes(this.path, this.config.prefixes) : 'unknown' } // resolve extended shapes if (this.node || this.shaclAnd) { if (this.node) { this.extendedShapes.push(this.node) } if (this.shaclAnd) { const list = this.config.lists[this.shaclAnd] if (list?.length) { for (const node of list) { this.extendedShapes.push(node as NamedNode) } } } } return this } clone(): ShaclPropertyTemplate { const copy = Object.assign({}, this) // arrays are not cloned but referenced, so create them manually copy.extendedShapes = [ ...this.extendedShapes ] copy.owlImports = [ ...this.owlImports ] if (this.languageIn) { copy.languageIn = [ ...this.languageIn ] } if (this.shaclOr) { copy.shaclOr = [ ...this.shaclOr ] } if (this.shaclXone) { copy.shaclXone = [ ...this.shaclXone ] } copy.merge = this.merge.bind(copy) copy.clone = this.clone.bind(copy) return copy } }