From 81a409f564757d0b4a5053fcd63719a692e88a1e Mon Sep 17 00:00:00 2001 From: Stephan Tittel Date: Thu, 12 Oct 2023 09:30:57 +0200 Subject: [PATCH] enhanced shacl or resolution for literals --- src/constraints.ts | 43 +++++++++++++++++++++++++++++++++++++++++-- src/editors.ts | 2 +- src/property.ts | 23 +++-------------------- 3 files changed, 45 insertions(+), 23 deletions(-) diff --git a/src/constraints.ts b/src/constraints.ts index 9d0eba3..e848fea 100644 --- a/src/constraints.ts +++ b/src/constraints.ts @@ -1,10 +1,11 @@ -import { BlankNode, NamedNode, Quad } from 'n3' +import { BlankNode, Literal, NamedNode, Quad } from 'n3' import { Term } from '@rdfjs/types' import { ShaclNode } from "./node" import { ShaclProperty, createPropertyInstance } from "./property" import { Config } from './config' -import { SHAPES_GRAPH } from './constants' +import { PREFIX_SHACL, RDF_PREDICATE_TYPE, SHAPES_GRAPH } from './constants' import { findLabel, removePrefixes } from './util' +import { ShaclPropertyTemplate } from './property-template' export function createShaclOrConstraint(options: Term[], context: ShaclNode | ShaclProperty, config: Config): HTMLElement { @@ -62,3 +63,41 @@ export function createShaclOrConstraint(options: Term[], context: ShaclNode | Sh } return constraintElement } + +export function resolveShaclOrConstraint(template: ShaclPropertyTemplate, value: Term): ShaclPropertyTemplate { + if (!template.shaclOr) { + console.warn('can\'t resolve sh:or because template has no options', template) + return template + } + if (value instanceof Literal) { + // value is a literal, try to match given value datatype + const valueType = value.datatype + for (const option of template.shaclOr) { + const shaclOrDatatypes = template.config.shapesGraph.getObjects(option, `${PREFIX_SHACL}datatype`, SHAPES_GRAPH) + if (shaclOrDatatypes.length && shaclOrDatatypes[0].equals(valueType)) { + template = template.clone() + template.datatype = shaclOrDatatypes[0] as NamedNode + return template + } + } + console.warn('couldn\'t resolve sh:or datatype for literal', value) + } else { + // value is a NamedNode or BlankNode + // find rdf:type of given value. if more than one available, choose first one for now + let types = template.config.dataGraph.getObjects(value, RDF_PREDICATE_TYPE, null) + if (types.length > 0) { + const type = types[0] as NamedNode + template = template.clone() + // try to find node shape that has requested target class + const nodeShapes = template.config.shapesGraph.getSubjects(`${PREFIX_SHACL}targetClass`, type, SHAPES_GRAPH) + if (nodeShapes.length > 0) { + template.node = nodeShapes[0] as NamedNode + // remove label since this is a node type property now + template.label = '' + } else { + template.class = type + } + } + } + return template +} \ No newline at end of file diff --git a/src/editors.ts b/src/editors.ts index e6a200b..60c728a 100644 --- a/src/editors.ts +++ b/src/editors.ts @@ -229,7 +229,7 @@ export function editorFactory(template: ShaclPropertyTemplate, value?: Term): HT } } - // check if it a langstring + // check if it is a langstring if (template.datatype?.value === `${PREFIX_RDF}langString` || template.languageIn?.length) { return createLangStringEditor(template, value) } diff --git a/src/property.ts b/src/property.ts index 9f36eec..53a6f48 100644 --- a/src/property.ts +++ b/src/property.ts @@ -2,8 +2,8 @@ import { BlankNode, DataFactory, NamedNode, Store } from 'n3' import { Term } from '@rdfjs/types' import { ShaclNode } from './node' import { focusFirstInputElement } from './util' -import { RDF_PREDICATE_TYPE, PREFIX_SHACL, SHAPES_GRAPH } from './constants' -import { createShaclOrConstraint } from './constraints' +import { SHAPES_GRAPH } from './constants' +import { createShaclOrConstraint, resolveShaclOrConstraint } from './constraints' import { Config } from './config' import { ShaclPropertyTemplate } from './property-template' import { Editor, editorFactory, toRDF } from './editors' @@ -55,28 +55,11 @@ export class ShaclProperty extends HTMLElement { let instance: HTMLElement if (this.template.shaclOr?.length) { if (value) { - let template = this.template - // find rdf:type of given value. if more than one available, choose first one for now - let types = this.template.config.dataGraph.getObjects(value, RDF_PREDICATE_TYPE, null) - if (types.length > 0) { - const type = types[0] as NamedNode - template = template.clone() - // try to find node shape that has requested target class - const nodeShapes = template.config.shapesGraph.getSubjects(`${PREFIX_SHACL}targetClass`, type, SHAPES_GRAPH) - if (nodeShapes.length > 0) { - template.node = nodeShapes[0] as NamedNode - // remove label since this is a node type property now - template.label = '' - } else { - template.class = type - } - } - instance = createPropertyInstance(template, value, true) + instance = createPropertyInstance(resolveShaclOrConstraint(this.template, value), value, true) } else { instance = createShaclOrConstraint(this.template.shaclOr, this, this.template.config) appendRemoveButton(instance, '') } - } else { instance = createPropertyInstance(this.template, value) }