@ulb-darmstadt/shacl-form 1.7.2 → 1.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,9 +28,11 @@ export declare class ShaclPropertyTemplate {
28
28
  shaclAnd: string | undefined;
29
29
  shaclIn: string | undefined;
30
30
  shaclOr: Term[] | undefined;
31
+ shaclXone: Term[] | undefined;
31
32
  languageIn: Term[] | undefined;
32
33
  datatype: NamedNode | undefined;
33
34
  hasValue: Term | undefined;
35
+ qualifiedValueShape: Term | undefined;
34
36
  owlImports: NamedNode[];
35
37
  config: Config;
36
38
  extendedShapes: NamedNode[] | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ulb-darmstadt/shacl-form",
3
- "version": "1.7.2",
3
+ "version": "1.7.4",
4
4
  "description": "SHACL form generator",
5
5
  "main": "dist/form-default.js",
6
6
  "module": "dist/form-default.js",
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/jsonld": "^1.5.15",
36
- "@types/leaflet": "^1.9.18",
36
+ "@types/leaflet": "^1.9.19",
37
37
  "@types/leaflet-editable": "^1.2.6",
38
38
  "@types/leaflet.fullscreen": "^3.0.2",
39
39
  "@types/mapbox__mapbox-gl-draw": "^1.4.9",
@@ -49,12 +49,12 @@
49
49
  },
50
50
  "dependencies": {
51
51
  "@mapbox/mapbox-gl-draw": "^1.5.0",
52
- "bootstrap": "^5.3.6",
52
+ "bootstrap": "^5.3.7",
53
53
  "jsonld": "^8.3.3",
54
54
  "leaflet": "^1.9.4",
55
55
  "leaflet-editable": "^1.3.1",
56
56
  "leaflet.fullscreen": "^4.0.0",
57
- "mapbox-gl": "^3.12.0",
57
+ "mapbox-gl": "^3.13.0",
58
58
  "mdui": "^2.1.4",
59
59
  "n3": "^1.26.0",
60
60
  "shacl-engine": "^1.0.2",
package/src/constants.ts CHANGED
@@ -20,6 +20,7 @@ export const OWL_PREDICATE_IMPORTS = DataFactory.namedNode(PREFIX_OWL + 'imports
20
20
  export const OWL_OBJECT_NAMED_INDIVIDUAL = DataFactory.namedNode(PREFIX_OWL + 'NamedIndividual')
21
21
  export const SHACL_OBJECT_NODE_SHAPE = DataFactory.namedNode(PREFIX_SHACL + 'NodeShape')
22
22
  export const SHACL_OBJECT_IRI = DataFactory.namedNode(PREFIX_SHACL + 'IRI')
23
+ export const SHACL_PREDICATE_PROPERTY = DataFactory.namedNode(PREFIX_SHACL + 'property')
23
24
  export const SHACL_PREDICATE_CLASS = DataFactory.namedNode(PREFIX_SHACL + 'class')
24
25
  export const SHACL_PREDICATE_TARGET_CLASS = DataFactory.namedNode(PREFIX_SHACL + 'targetClass')
25
26
  export const SHACL_PREDICATE_NODE_KIND = DataFactory.namedNode(PREFIX_SHACL + 'nodeKind')
@@ -3,7 +3,7 @@ import { Term } from '@rdfjs/types'
3
3
  import { ShaclNode } from "./node"
4
4
  import { ShaclProperty, createPropertyInstance } from "./property"
5
5
  import { Config } from './config'
6
- import { PREFIX_SHACL, RDF_PREDICATE_TYPE, SHACL_PREDICATE_CLASS, SHACL_PREDICATE_TARGET_CLASS, SHACL_PREDICATE_NODE_KIND, SHACL_OBJECT_IRI } from './constants'
6
+ import { PREFIX_SHACL, RDF_PREDICATE_TYPE, SHACL_PREDICATE_CLASS, SHACL_PREDICATE_TARGET_CLASS, SHACL_PREDICATE_NODE_KIND, SHACL_OBJECT_IRI, SHACL_PREDICATE_PROPERTY } from './constants'
7
7
  import { findLabel, removePrefixes } from './util'
8
8
  import { ShaclPropertyTemplate } from './property-template'
9
9
  import { Editor, InputListEntry } from './theme'
@@ -17,18 +17,45 @@ export function createShaclOrConstraint(options: Term[], context: ShaclNode | Sh
17
17
  optionElements.push({ label: '--- please choose ---', value: '' })
18
18
 
19
19
  if (context instanceof ShaclNode) {
20
- const properties: ShaclProperty[] = []
21
- // expect options to be shacl properties
20
+ const properties: ShaclProperty[][] = []
21
+ // options can be shacl properties or blank nodes referring to (list of) properties
22
+ let optionsAreReferencedProperties = false
23
+ if (options.length) {
24
+ optionsAreReferencedProperties = config.shapesGraph.getObjects(options[0] , SHACL_PREDICATE_PROPERTY, null).length > 0
25
+ }
22
26
  for (let i = 0; i < options.length; i++) {
23
- const property = new ShaclProperty(options[i] as NamedNode | BlankNode, context, config)
24
- properties.push(property)
25
- optionElements.push({ label: property.template.label, value: i.toString() })
27
+ if (optionsAreReferencedProperties) {
28
+ const quads = config.shapesGraph.getObjects(options[i] , SHACL_PREDICATE_PROPERTY, null)
29
+ // option can be single property or list of properties
30
+ const list: ShaclProperty[] = []
31
+ let combinedText = ''
32
+ for (const subject of quads) {
33
+ const property = new ShaclProperty(subject as NamedNode | BlankNode, context, config)
34
+ list.push(property)
35
+ combinedText += (combinedText.length > 1 ? ' / ' : '') + property.template.label
36
+ }
37
+ properties.push(list)
38
+ optionElements.push({ label: combinedText, value: i.toString() })
39
+ } else {
40
+ const property = new ShaclProperty(options[i] as NamedNode | BlankNode, context, config)
41
+ properties.push([property])
42
+ optionElements.push({ label: property.template.label, value: i.toString() })
43
+ }
26
44
  }
27
45
  const editor = config.theme.createListEditor('Please choose', null, false, optionElements)
28
46
  const select = editor.querySelector('.editor') as Editor
29
47
  select.onchange = () => {
30
48
  if (select.value) {
31
- constraintElement.replaceWith(properties[parseInt(select.value)])
49
+ const selectedOptions = properties[parseInt(select.value)]
50
+ let lastAddedProperty: ShaclProperty
51
+ if (selectedOptions.length) {
52
+ lastAddedProperty = selectedOptions[0]
53
+ constraintElement.replaceWith(selectedOptions[0])
54
+ }
55
+ for (let i = 1; i < selectedOptions.length; i++) {
56
+ lastAddedProperty!.after(selectedOptions[1])
57
+ lastAddedProperty = selectedOptions[1]
58
+ }
32
59
  }
33
60
  }
34
61
  constraintElement.appendChild(editor)
@@ -54,53 +81,73 @@ export function createShaclOrConstraint(options: Term[], context: ShaclNode | Sh
54
81
  return constraintElement
55
82
  }
56
83
 
57
- export function resolveShaclOrConstraint(template: ShaclPropertyTemplate, value: Term): ShaclPropertyTemplate {
58
- if (!template.shaclOr) {
59
- console.warn('can\'t resolve sh:or because template has no options', template)
60
- return template
61
- }
84
+ export function resolveShaclOrConstraintOnProperty(subjects: Term[], value: Term, config: Config): Quad[] {
62
85
  if (value instanceof Literal) {
63
- // value is a literal, try to resolve sh:or by matching on given value datatype
86
+ // value is a literal, try to resolve sh:or/sh:xone by matching on given value datatype
64
87
  const valueType = value.datatype
65
- for (const subject of template.shaclOr) {
66
- const options = template.config.shapesGraph.getQuads(subject, null, null, null)
88
+ for (const subject of subjects) {
89
+ const options = config.shapesGraph.getQuads(subject, null, null, null)
67
90
  for (const quad of options) {
68
91
  if (quad.predicate.value === `${PREFIX_SHACL}datatype` && quad.object.equals(valueType)) {
69
- return template.clone().merge(options)
92
+ return options
70
93
  }
71
94
  }
72
95
  }
73
96
  } else {
74
- // value is a NamedNode or BlankNode, try to resolve sh:or by matching rdf:type of given value with sh:node or sh:class in data graph or shapes graph
75
- let types = template.config.dataGraph.getObjects(value, RDF_PREDICATE_TYPE, null)
76
- types.push(...template.config.shapesGraph.getObjects(value, RDF_PREDICATE_TYPE, null))
77
- for (const subject of template.shaclOr) {
78
- const options = template.config.shapesGraph.getQuads(subject, null, null, null)
97
+ // value is a NamedNode or BlankNode, try to resolve sh:or/sh:xone by matching rdf:type of given value with sh:node or sh:class in data graph or shapes graph
98
+ const types = config.dataGraph.getObjects(value, RDF_PREDICATE_TYPE, null)
99
+ types.push(...config.shapesGraph.getObjects(value, RDF_PREDICATE_TYPE, null))
100
+ for (const subject of subjects) {
101
+ const options = config.shapesGraph.getQuads(subject, null, null, null)
79
102
  for (const quad of options) {
80
103
  if (types.length > 0) {
81
- // try to find matching sh:node in sh:or values
104
+ // try to find matching sh:node in sh:or/sh:xone values
82
105
  if (quad.predicate.value === `${PREFIX_SHACL}node`) {
83
106
  for (const type of types) {
84
- if (template.config.shapesGraph.getQuads(quad.object, SHACL_PREDICATE_TARGET_CLASS, type, null).length > 0) {
85
- return template.clone().merge(options)
107
+ if (config.shapesGraph.getQuads(quad.object, SHACL_PREDICATE_TARGET_CLASS, type, null).length > 0) {
108
+ return options
86
109
  }
87
110
  }
88
111
  }
89
- // try to find matching sh:class in sh:or values
112
+ // try to find matching sh:class in sh:or/sh:xone values
90
113
  if (quad.predicate.equals(SHACL_PREDICATE_CLASS)) {
91
114
  for (const type of types) {
92
115
  if (quad.object.equals(type)) {
93
- return template.clone().merge(options)
116
+ return options
94
117
  }
95
118
  }
96
119
  }
97
120
  } else if (quad.predicate.equals(SHACL_PREDICATE_NODE_KIND) && quad.object.equals(SHACL_OBJECT_IRI)) {
98
121
  // if sh:nodeKind is sh:IRI, just use that
99
- return template.clone().merge(options)
122
+ return options
123
+ }
124
+ }
125
+ }
126
+ }
127
+ console.error('couldn\'t resolve sh:or/sh:xone on property for value', value)
128
+ return []
129
+ }
130
+
131
+ export function resolveShaclOrConstraintOnNode(subjects: Term[], value: Term, config: Config): Term[] {
132
+ for (const subject of subjects) {
133
+ let subjectMatches = true
134
+ const propertySubjects = config.shapesGraph.getObjects(subject, SHACL_PREDICATE_PROPERTY, null)
135
+ for (const propertySubject of propertySubjects) {
136
+ const paths = config.shapesGraph.getObjects(propertySubject, `${PREFIX_SHACL}path`, null)
137
+ for (const path of paths) {
138
+ const values = config.dataGraph.getObjects(value, path, null)
139
+ values.push(...config.shapesGraph.getObjects(value, path, null))
140
+ if (!values.length) {
141
+ subjectMatches = false
142
+ break
100
143
  }
101
144
  }
102
145
  }
146
+ if (subjectMatches) {
147
+ return propertySubjects
148
+ }
103
149
  }
104
- console.error('couldn\'t resolve sh:or for value', value)
105
- return template
150
+
151
+ console.error('couldn\'t resolve sh:or/sh:xone on node for value', value)
152
+ return []
106
153
  }
package/src/node.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { BlankNode, DataFactory, NamedNode, Store } from 'n3'
2
2
  import { Term } from '@rdfjs/types'
3
- import { PREFIX_SHACL, RDF_PREDICATE_TYPE, OWL_PREDICATE_IMPORTS } from './constants'
3
+ import { PREFIX_SHACL, RDF_PREDICATE_TYPE, OWL_PREDICATE_IMPORTS, SHACL_PREDICATE_PROPERTY } from './constants'
4
4
  import { ShaclProperty } from './property'
5
5
  import { createShaclGroup } from './group'
6
6
  import { v4 as uuidv4 } from 'uuid'
7
- import { createShaclOrConstraint } from './constraints'
7
+ import { createShaclOrConstraint, resolveShaclOrConstraintOnNode } from './constraints'
8
8
  import { Config } from './config'
9
9
 
10
10
  export class ShaclNode extends HTMLElement {
@@ -76,33 +76,8 @@ export class ShaclNode extends HTMLElement {
76
76
 
77
77
  for (const quad of quads) {
78
78
  switch (quad.predicate.id) {
79
- case `${PREFIX_SHACL}property`:
80
- let parentElement: HTMLElement = this
81
- // check if property belongs to a group
82
- const groupRef = config.shapesGraph.getQuads(quad.object as Term, `${PREFIX_SHACL}group`, null, null)
83
- if (groupRef.length > 0) {
84
- const groupSubject = groupRef[0].object.value
85
- if (config.groups.indexOf(groupSubject) > -1) {
86
- // check if group element already exists, otherwise create it
87
- let group = this.querySelector(`:scope > .shacl-group[data-subject='${groupSubject}']`) as HTMLElement
88
- if (!group) {
89
- group = createShaclGroup(groupSubject, config)
90
- this.appendChild(group)
91
- }
92
- parentElement = group
93
- } else {
94
- console.warn('ignoring unknown group reference', groupRef[0], 'existing groups:', config.groups)
95
- }
96
- }
97
- // delay creating/appending the property until we finished parsing the node.
98
- // This is needed to have possible owlImports parsed before creating the property.
99
- setTimeout(() => {
100
- const property = new ShaclProperty(quad.object as NamedNode | BlankNode, this, config, valueSubject)
101
- // do not add empty properties (i.e. properties with no instances). This can be the case e.g. in viewer mode when there is no data for the respective property.
102
- if (property.childElementCount > 0) {
103
- parentElement.appendChild(property)
104
- }
105
- })
79
+ case SHACL_PREDICATE_PROPERTY.id:
80
+ this.addPropertyInstance(quad.object, config, valueSubject)
106
81
  break;
107
82
  case `${PREFIX_SHACL}and`:
108
83
  // inheritance via sh:and
@@ -127,13 +102,10 @@ export class ShaclNode extends HTMLElement {
127
102
  this.owlImports.push(quad.object as NamedNode)
128
103
  break;
129
104
  case `${PREFIX_SHACL}or`:
130
- list = config.lists[quad.object.value]
131
- if (list?.length) {
132
- this.appendChild(createShaclOrConstraint(list, this, config))
133
- }
134
- else {
135
- console.error('list not found:', quad.object.value, 'existing lists:', config.lists)
136
- }
105
+ this.tryResolve(quad.object, valueSubject, config)
106
+ break;
107
+ case `${PREFIX_SHACL}xone`:
108
+ this.tryResolve(quad.object, valueSubject, config)
137
109
  break;
138
110
  }
139
111
  }
@@ -162,6 +134,57 @@ export class ShaclNode extends HTMLElement {
162
134
  }
163
135
  return subject
164
136
  }
137
+
138
+ addPropertyInstance(shaclSubject: Term, config: Config, valueSubject: NamedNode | BlankNode | undefined) {
139
+ let parentElement: HTMLElement = this
140
+ // check if property belongs to a group
141
+ const groupRef = config.shapesGraph.getQuads(shaclSubject as Term, `${PREFIX_SHACL}group`, null, null)
142
+ if (groupRef.length > 0) {
143
+ const groupSubject = groupRef[0].object.value
144
+ if (config.groups.indexOf(groupSubject) > -1) {
145
+ // check if group element already exists, otherwise create it
146
+ let group = this.querySelector(`:scope > .shacl-group[data-subject='${groupSubject}']`) as HTMLElement
147
+ if (!group) {
148
+ group = createShaclGroup(groupSubject, config)
149
+ this.appendChild(group)
150
+ }
151
+ parentElement = group
152
+ } else {
153
+ console.warn('ignoring unknown group reference', groupRef[0], 'existing groups:', config.groups)
154
+ }
155
+ }
156
+ // delay creating/appending the property until we finished parsing the node.
157
+ // This is needed to have possible owlImports parsed before creating the property.
158
+ setTimeout(() => {
159
+ const property = new ShaclProperty(shaclSubject as NamedNode | BlankNode, this, config, valueSubject)
160
+ // do not add empty properties (i.e. properties with no instances). This can be the case e.g. in viewer mode when there is no data for the respective property.
161
+ if (property.childElementCount > 0) {
162
+ parentElement.appendChild(property)
163
+ }
164
+ })
165
+ }
166
+
167
+ tryResolve(subject: Term, valueSubject: NamedNode | BlankNode | undefined, config: Config) {
168
+ const list = config.lists[subject.value]
169
+ if (list?.length) {
170
+ let resolved = false
171
+ if (valueSubject) {
172
+ const resolvedPropertySubjects = resolveShaclOrConstraintOnNode(list, valueSubject, config)
173
+ if (resolvedPropertySubjects.length) {
174
+ for (const propertySubject of resolvedPropertySubjects) {
175
+ this.addPropertyInstance(propertySubject, config, valueSubject)
176
+ }
177
+ resolved = true
178
+ }
179
+ }
180
+ if (!resolved) {
181
+ this.appendChild(createShaclOrConstraint(list, this, config))
182
+ }
183
+ }
184
+ else {
185
+ console.error('list for sh:or/sh:xone not found:', subject, 'existing lists:', config.lists)
186
+ }
187
+ }
165
188
  }
166
189
 
167
190
  window.customElements.define('shacl-node', ShaclNode)
@@ -31,8 +31,11 @@ const mappers: Record<string, (template: ShaclPropertyTemplate, term: Term) => v
31
31
  [`${PREFIX_SHACL}languageIn`]: (template, term) => { template.languageIn = template.config.lists[term.value]; template.datatype = DataFactory.namedNode(PREFIX_RDF + 'langString') },
32
32
  [`${PREFIX_SHACL}defaultValue`]: (template, term) => { template.defaultValue = term },
33
33
  [`${PREFIX_SHACL}hasValue`]: (template, term) => { template.hasValue = term },
34
- [OWL_PREDICATE_IMPORTS.id]: (template, term) => { template.owlImports.push(term as NamedNode) },
35
- [SHACL_PREDICATE_CLASS.id]: (template, term) => {
34
+ [`${PREFIX_SHACL}qualifiedValueShape`]: (template, term) => { template.qualifiedValueShape = term },
35
+ [`${PREFIX_SHACL}qualifiedMinCount`]: (template, term) => { template.minCount = parseInt(term.value) },
36
+ [`${PREFIX_SHACL}qualifiedMaxCount`]: (template, term) => { template.maxCount = parseInt(term.value) },
37
+ [OWL_PREDICATE_IMPORTS.id]: (template, term) => { template.owlImports.push(term as NamedNode) },
38
+ [SHACL_PREDICATE_CLASS.id]: (template, term) => {
36
39
  template.class = term as NamedNode
37
40
  // try to find node shape that has requested target class
38
41
  const nodeShapes = template.config.shapesGraph.getSubjects(SHACL_PREDICATE_TARGET_CLASS, term, null)
@@ -45,7 +48,15 @@ const mappers: Record<string, (template: ShaclPropertyTemplate, term: Term) => v
45
48
  if (list?.length) {
46
49
  template.shaclOr = list
47
50
  } else {
48
- console.error('list not found:', term.value, 'existing lists:', template.config.lists)
51
+ console.error('list for sh:or not found:', term.value, 'existing lists:', template.config.lists)
52
+ }
53
+ },
54
+ [`${PREFIX_SHACL}xone`]: (template, term) => {
55
+ const list = template.config.lists[term.value]
56
+ if (list?.length) {
57
+ template.shaclXone = list
58
+ } else {
59
+ console.error('list for sh:xone not found:', term.value, 'existing lists:', template.config.lists)
49
60
  }
50
61
  }
51
62
  }
@@ -76,9 +87,11 @@ export class ShaclPropertyTemplate {
76
87
  shaclAnd: string | undefined
77
88
  shaclIn: string | undefined
78
89
  shaclOr: Term[] | undefined
90
+ shaclXone: Term[] | undefined
79
91
  languageIn: Term[] | undefined
80
92
  datatype: NamedNode | undefined
81
93
  hasValue: Term | undefined
94
+ qualifiedValueShape: Term | undefined
82
95
  owlImports: NamedNode[] = []
83
96
 
84
97
  config: Config
@@ -88,6 +101,9 @@ export class ShaclPropertyTemplate {
88
101
  this.parent = parent
89
102
  this.config = config
90
103
  this.merge(quads)
104
+ if (this.qualifiedValueShape) {
105
+ this.merge(config.shapesGraph.getQuads(this.qualifiedValueShape, null, null, null))
106
+ }
91
107
  }
92
108
 
93
109
  merge(quads: Quad[]): ShaclPropertyTemplate {
@@ -127,6 +143,9 @@ export class ShaclPropertyTemplate {
127
143
  if (this.shaclOr) {
128
144
  copy.shaclOr = [ ...this.shaclOr ]
129
145
  }
146
+ if (this.shaclXone) {
147
+ copy.shaclXone = [ ...this.shaclXone ]
148
+ }
130
149
  copy.merge = this.merge.bind(copy)
131
150
  copy.clone = this.clone.bind(copy)
132
151
  return copy
package/src/property.ts CHANGED
@@ -2,12 +2,13 @@ import { BlankNode, DataFactory, NamedNode, Store } from 'n3'
2
2
  import { Term } from '@rdfjs/types'
3
3
  import { ShaclNode } from './node'
4
4
  import { focusFirstInputElement } from './util'
5
- import { createShaclOrConstraint, resolveShaclOrConstraint } from './constraints'
5
+ import { createShaclOrConstraint, resolveShaclOrConstraintOnProperty } from './constraints'
6
6
  import { Config } from './config'
7
7
  import { ShaclPropertyTemplate } from './property-template'
8
8
  import { Editor, fieldFactory } from './theme'
9
9
  import { toRDF } from './serialize'
10
10
  import { findPlugin } from './plugin'
11
+ import { RDF_PREDICATE_TYPE, SHACL_PREDICATE_TARGET_CLASS } from './constants'
11
12
 
12
13
  export class ShaclProperty extends HTMLElement {
13
14
  template: ShaclPropertyTemplate
@@ -46,6 +47,22 @@ export class ShaclProperty extends HTMLElement {
46
47
  const values = valueSubject ? config.dataGraph.getQuads(valueSubject, this.template.path, null, null) : []
47
48
  let valuesContainHasValue = false
48
49
  for (const value of values) {
50
+ // ignore values that do not conform to this property.
51
+ // this might be the case when there are multiple properties with the same sh:path in a NodeShape.
52
+ if (this.template.node) {
53
+ const targetClasses = config.shapesGraph.getObjects(this.template.node, SHACL_PREDICATE_TARGET_CLASS, null)
54
+ if (targetClasses.length > 0) {
55
+ let hasTargetClass = false
56
+ for (let i = 0; i < targetClasses.length && !hasTargetClass; i++) {
57
+ if (config.dataGraph.getQuads(value.object, RDF_PREDICATE_TYPE, targetClasses[i], null).length > 0) {
58
+ hasTargetClass = true
59
+ }
60
+ }
61
+ if (!hasTargetClass) {
62
+ continue
63
+ }
64
+ }
65
+ }
49
66
  this.addPropertyInstance(value.object)
50
67
  if (this.template.hasValue && value.object.equals(this.template.hasValue)) {
51
68
  valuesContainHasValue = true
@@ -83,22 +100,29 @@ export class ShaclProperty extends HTMLElement {
83
100
 
84
101
  addPropertyInstance(value?: Term): HTMLElement {
85
102
  let instance: HTMLElement
86
- if (this.template.shaclOr?.length) {
103
+ if (this.template.shaclOr?.length || this.template.shaclXone?.length) {
104
+ const options = this.template.shaclOr?.length ? this.template.shaclOr : this.template.shaclXone as Term[]
105
+ let resolved = false
87
106
  if (value) {
88
- instance = createPropertyInstance(resolveShaclOrConstraint(this.template, value), value, true)
89
- } else {
90
- instance = createShaclOrConstraint(this.template.shaclOr, this, this.template.config)
107
+ const resolvedOptions = resolveShaclOrConstraintOnProperty(options, value, this.template.config)
108
+ if (resolvedOptions.length) {
109
+ instance = createPropertyInstance(this.template.clone().merge(resolvedOptions), value, true)
110
+ resolved = true
111
+ }
112
+ }
113
+ if (!resolved) {
114
+ instance = createShaclOrConstraint(options, this, this.template.config)
91
115
  appendRemoveButton(instance, '')
92
116
  }
93
117
  } else {
94
118
  instance = createPropertyInstance(this.template, value)
95
119
  }
96
120
  if (this.template.config.editMode) {
97
- this.insertBefore(instance, this.addButton!)
121
+ this.insertBefore(instance!, this.addButton!)
98
122
  } else {
99
- this.appendChild(instance)
123
+ this.appendChild(instance!)
100
124
  }
101
- return instance
125
+ return instance!
102
126
  }
103
127
 
104
128
  updateControls() {
package/src/util.ts CHANGED
@@ -85,22 +85,32 @@ function findClassInstancesFromOwlImports(clazz: NamedNode, context: ShaclNode |
85
85
  }
86
86
 
87
87
  export function findInstancesOf(clazz: NamedNode, template: ShaclPropertyTemplate, indent = 0): InputListEntry[] {
88
- // find instances in the shapes graph
89
- const instances: Term[] = template.config.shapesGraph.getSubjects(RDF_PREDICATE_TYPE, clazz, SHAPES_GRAPH)
90
- // find instances in the data graph
91
- instances.push(...template.config.dataGraph.getSubjects(RDF_PREDICATE_TYPE, clazz, null))
92
- // find instances in imported taxonomies
93
- findClassInstancesFromOwlImports(clazz, template, template.config.shapesGraph, instances)
94
-
95
- const entries = createInputListEntries(instances, template.config.shapesGraph, template.config.languages, indent)
96
- for (const subClass of template.config.shapesGraph.getSubjects(RDFS_PREDICATE_SUBCLASS_OF, clazz, null)) {
97
- entries.push(...findInstancesOf(subClass as NamedNode, template, indent + 1))
88
+ let instances: Term[]
89
+ // if template has sh:in, then just use that as class instances
90
+ if (template.shaclIn) {
91
+ const list = template.config.lists[template.shaclIn]
92
+ instances = list?.length ? list : []
93
+ } else {
94
+ // find instances in the shapes graph
95
+ instances = template.config.shapesGraph.getSubjects(RDF_PREDICATE_TYPE, clazz, SHAPES_GRAPH)
96
+ // find instances in the data graph
97
+ instances.push(...template.config.dataGraph.getSubjects(RDF_PREDICATE_TYPE, clazz, null))
98
+ // find instances in imported taxonomies
99
+ findClassInstancesFromOwlImports(clazz, template, template.config.shapesGraph, instances)
98
100
  }
99
- if (template.config.shapesGraph.getQuads(clazz, RDF_PREDICATE_TYPE, OWL_OBJECT_NAMED_INDIVIDUAL, null).length > 0) {
100
- entries.push(...createInputListEntries([ clazz ], template.config.shapesGraph, template.config.languages, indent))
101
- for (const subClass of template.config.shapesGraph.getSubjects(SKOS_PREDICATE_BROADER, clazz, null)) {
101
+
102
+ const entries = createInputListEntries(instances, template.config.shapesGraph, template.config.languages, indent)
103
+ // build inheritance tree only if sh:in is not defined
104
+ if (template.shaclIn === undefined) {
105
+ for (const subClass of template.config.shapesGraph.getSubjects(RDFS_PREDICATE_SUBCLASS_OF, clazz, null)) {
102
106
  entries.push(...findInstancesOf(subClass as NamedNode, template, indent + 1))
103
107
  }
108
+ if (template.config.shapesGraph.getQuads(clazz, RDF_PREDICATE_TYPE, OWL_OBJECT_NAMED_INDIVIDUAL, null).length > 0) {
109
+ entries.push(...createInputListEntries([ clazz ], template.config.shapesGraph, template.config.languages, indent))
110
+ for (const subClass of template.config.shapesGraph.getSubjects(SKOS_PREDICATE_BROADER, clazz, null)) {
111
+ entries.push(...findInstancesOf(subClass as NamedNode, template, indent + 1))
112
+ }
113
+ }
104
114
  }
105
115
  return entries
106
116
  }