@entryscape/rdforms 10.14.0 → 10.15.0
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.
- package/README.md +27 -19
- package/dist/rdforms.bmd.js +1 -1
- package/dist/rdforms.bootstrap.js +1 -1
- package/dist/rdforms.jquery.js +1 -1
- package/dist/rdforms.node.js +4027 -0
- package/dist/rdforms.react.js +19 -19
- package/package.json +7 -5
- package/src/template/OntologyStore.js +1 -1
- package/src/utils.js +1 -1
- package/src/view/jquery/text.js +1 -1
|
@@ -0,0 +1,4027 @@
|
|
|
1
|
+
/******/ (() => { // webpackBootstrap
|
|
2
|
+
/******/ "use strict";
|
|
3
|
+
/******/ var __webpack_modules__ = ({
|
|
4
|
+
|
|
5
|
+
/***/ 229:
|
|
6
|
+
/***/ ((module) => {
|
|
7
|
+
|
|
8
|
+
module.exports = require("node-fetch");
|
|
9
|
+
|
|
10
|
+
/***/ })
|
|
11
|
+
|
|
12
|
+
/******/ });
|
|
13
|
+
/************************************************************************/
|
|
14
|
+
/******/ // The module cache
|
|
15
|
+
/******/ var __webpack_module_cache__ = {};
|
|
16
|
+
/******/
|
|
17
|
+
/******/ // The require function
|
|
18
|
+
/******/ function __webpack_require__(moduleId) {
|
|
19
|
+
/******/ // Check if module is in cache
|
|
20
|
+
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
|
21
|
+
/******/ if (cachedModule !== undefined) {
|
|
22
|
+
/******/ return cachedModule.exports;
|
|
23
|
+
/******/ }
|
|
24
|
+
/******/ // Create a new module (and put it into the cache)
|
|
25
|
+
/******/ var module = __webpack_module_cache__[moduleId] = {
|
|
26
|
+
/******/ // no module.id needed
|
|
27
|
+
/******/ // no module.loaded needed
|
|
28
|
+
/******/ exports: {}
|
|
29
|
+
/******/ };
|
|
30
|
+
/******/
|
|
31
|
+
/******/ // Execute the module function
|
|
32
|
+
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
|
33
|
+
/******/
|
|
34
|
+
/******/ // Return the exports of the module
|
|
35
|
+
/******/ return module.exports;
|
|
36
|
+
/******/ }
|
|
37
|
+
/******/
|
|
38
|
+
/************************************************************************/
|
|
39
|
+
/******/ /* webpack/runtime/compat get default export */
|
|
40
|
+
/******/ (() => {
|
|
41
|
+
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
|
42
|
+
/******/ __webpack_require__.n = (module) => {
|
|
43
|
+
/******/ var getter = module && module.__esModule ?
|
|
44
|
+
/******/ () => (module['default']) :
|
|
45
|
+
/******/ () => (module);
|
|
46
|
+
/******/ __webpack_require__.d(getter, { a: getter });
|
|
47
|
+
/******/ return getter;
|
|
48
|
+
/******/ };
|
|
49
|
+
/******/ })();
|
|
50
|
+
/******/
|
|
51
|
+
/******/ /* webpack/runtime/define property getters */
|
|
52
|
+
/******/ (() => {
|
|
53
|
+
/******/ // define getter functions for harmony exports
|
|
54
|
+
/******/ __webpack_require__.d = (exports, definition) => {
|
|
55
|
+
/******/ for(var key in definition) {
|
|
56
|
+
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
|
|
57
|
+
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
|
58
|
+
/******/ }
|
|
59
|
+
/******/ }
|
|
60
|
+
/******/ };
|
|
61
|
+
/******/ })();
|
|
62
|
+
/******/
|
|
63
|
+
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
|
64
|
+
/******/ (() => {
|
|
65
|
+
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
|
|
66
|
+
/******/ })();
|
|
67
|
+
/******/
|
|
68
|
+
/******/ /* webpack/runtime/make namespace object */
|
|
69
|
+
/******/ (() => {
|
|
70
|
+
/******/ // define __esModule on exports
|
|
71
|
+
/******/ __webpack_require__.r = (exports) => {
|
|
72
|
+
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
|
73
|
+
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
74
|
+
/******/ }
|
|
75
|
+
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
|
76
|
+
/******/ };
|
|
77
|
+
/******/ })();
|
|
78
|
+
/******/
|
|
79
|
+
/************************************************************************/
|
|
80
|
+
var __webpack_exports__ = {};
|
|
81
|
+
// ESM COMPAT FLAG
|
|
82
|
+
__webpack_require__.r(__webpack_exports__);
|
|
83
|
+
|
|
84
|
+
// EXPORTS
|
|
85
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
86
|
+
ItemStore: () => (/* reexport */ ItemStore),
|
|
87
|
+
bundleLoader: () => (/* reexport */ bundleLoader),
|
|
88
|
+
engine: () => (/* reexport */ engine),
|
|
89
|
+
system: () => (/* reexport */ system),
|
|
90
|
+
utils: () => (/* reexport */ utils),
|
|
91
|
+
validate: () => (/* reexport */ validate)
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
;// external "@entryscape/rdfjson"
|
|
95
|
+
const rdfjson_namespaceObject = require("@entryscape/rdfjson");
|
|
96
|
+
;// external "moment"
|
|
97
|
+
const external_moment_namespaceObject = require("moment");
|
|
98
|
+
var external_moment_default = /*#__PURE__*/__webpack_require__.n(external_moment_namespaceObject);
|
|
99
|
+
;// external "lodash"
|
|
100
|
+
const external_lodash_namespaceObject = require("lodash");
|
|
101
|
+
;// ./src/model/system.js
|
|
102
|
+
/* eslint-disable no-unused-vars */
|
|
103
|
+
|
|
104
|
+
const generateUIDNotMoreThan1million = () =>
|
|
105
|
+
// eslint-disable-next-line no-restricted-properties,no-bitwise
|
|
106
|
+
`0000${(Math.random() * Math.pow(36, 4) << 0).toString(36)}`.slice(-4);
|
|
107
|
+
const createURI = (item, parentBinding) => {
|
|
108
|
+
const parentURI = parentBinding.getChildrenRootUri();
|
|
109
|
+
const hash = parentURI.lastIndexOf('#');
|
|
110
|
+
const newURIBase = hash === -1 ? `${parentURI}#` : parentURI.substring(0, hash + 1);
|
|
111
|
+
const graph = parentBinding.getGraph()._graph;
|
|
112
|
+
while (true) {
|
|
113
|
+
const newURI = newURIBase + generateUIDNotMoreThan1million();
|
|
114
|
+
if (graph[newURI] == null) {
|
|
115
|
+
return newURI;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const getFallbackChoice = (item, value, seeAlso, graph) => {
|
|
120
|
+
if (item.getNodetype() === 'URI' || item.getNodetype() === 'RESOURCE') {
|
|
121
|
+
let lmap = utils.getLocalizedMap(graph, value, item.getURIValueLabelProperties());
|
|
122
|
+
if (!lmap) {
|
|
123
|
+
const lastHash = value.lastIndexOf('#');
|
|
124
|
+
const lastSlash = value.lastIndexOf('/');
|
|
125
|
+
if (lastHash > 0 || lastSlash > 0) {
|
|
126
|
+
lmap = {
|
|
127
|
+
'': decodeURIComponent(value.substring(1 + (lastHash > lastSlash ? lastHash : lastSlash)))
|
|
128
|
+
};
|
|
129
|
+
} else {
|
|
130
|
+
lmap = {
|
|
131
|
+
'': value
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
value,
|
|
137
|
+
label: lmap
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
value,
|
|
142
|
+
label: value
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* This method is a default implementation, feel free to override with specific construction of matched choices.
|
|
148
|
+
* Returns a choice object containing a value and a label.
|
|
149
|
+
* Override this function to provide specific loading of a choice.
|
|
150
|
+
* If you need to do this asynchonously provide a "load" method on the returned choice object.
|
|
151
|
+
* To indicate that a matched value is not acceptable anymore,
|
|
152
|
+
* set the flag mismatch to true in the returned choice object.
|
|
153
|
+
*
|
|
154
|
+
* @param item the RDForms template item matched against.
|
|
155
|
+
* @param value the value to match
|
|
156
|
+
* @param seeAlso if provided the value is a URI and a rdfs:seeAlso property has been found in the graph
|
|
157
|
+
* @param graph the RDF graph where the value was matched
|
|
158
|
+
* @returns {Object} an object containing a value, a label (object with language codes as attributes),
|
|
159
|
+
* an optional load callback method and an optional mismatch flag.
|
|
160
|
+
* @see openChoiceSelector
|
|
161
|
+
*/
|
|
162
|
+
const getChoice = (item, value, seeAlso, graph) => getFallbackChoice(item, value, seeAlso, graph);
|
|
163
|
+
const labelProperties = ['http://www.w3.org/2000/01/rdf-schema#label', 'http://purl.org/dc/terms/title', 'http://purl.org/dc/elements/1.1/title', 'http://www.w3.org/2004/02/skos/core#prefLabel', 'http://xmlns.com/foaf/0.1/name', 'http://xmlns.com/foaf/name'];
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* This method is a fake implementation for launching a dialog for choosing system choices.
|
|
167
|
+
* The method MUST be overridden if the template you use depends on system choices.
|
|
168
|
+
* (System choices in a RDForm template choice items means that there are neither inline choices
|
|
169
|
+
* or an ontology URL given in combination with provided cached choices for the given ontology URL).
|
|
170
|
+
*
|
|
171
|
+
* @param {rdforms.model.Binding} binding the binding where the choice will be given
|
|
172
|
+
* @param {Function} callback a method to call with a choice object when the user has selected an appropriate choice.
|
|
173
|
+
*/
|
|
174
|
+
const openChoiceSelector = (binding, callback) => {
|
|
175
|
+
alert('This alert is a placeholder for a search dialog that should be provided as part of the integration of ' + 'RDForms into a wider system.\nSimply override the methods "getChoices" and "openChoiceSelector" in the ' + 'system module.');
|
|
176
|
+
callback({
|
|
177
|
+
value: 'http://example.com/choice1',
|
|
178
|
+
label: {
|
|
179
|
+
en: 'First choice',
|
|
180
|
+
sv: 'Första valet'
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/** The implementor is expected to provide an application specific override
|
|
186
|
+
* For example:
|
|
187
|
+
* system.attachExternalLinkBehaviour = (node, binding) => node.setAttribute("target", "_blank");
|
|
188
|
+
*/
|
|
189
|
+
const attachExternalLinkBehaviour = () => false;
|
|
190
|
+
|
|
191
|
+
/** The implementor is expected to provide an application specific override
|
|
192
|
+
* For example:
|
|
193
|
+
* system.attachLinkBehaviour = (node, binding) => node.setAttribute("target", "_blank");
|
|
194
|
+
*/
|
|
195
|
+
const attachLinkBehaviour = (node, binding) => false;
|
|
196
|
+
const hasDnDSupport = binding => false;
|
|
197
|
+
const addDnD = (binding, node, onDrop) => ({});
|
|
198
|
+
/* harmony default export */ const system = ({
|
|
199
|
+
hasDnDSupport,
|
|
200
|
+
addDnD,
|
|
201
|
+
attachExternalLinkBehaviour,
|
|
202
|
+
attachLinkBehaviour,
|
|
203
|
+
openChoiceSelector,
|
|
204
|
+
getChoice,
|
|
205
|
+
getFallbackChoice,
|
|
206
|
+
labelProperties,
|
|
207
|
+
createURI
|
|
208
|
+
});
|
|
209
|
+
;// ./src/utils.js
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
const getLocalizedValue = (hash, locale) => {
|
|
214
|
+
const _locale = locale || external_moment_default().locale();
|
|
215
|
+
if (hash == null) {
|
|
216
|
+
return {
|
|
217
|
+
precision: 'none'
|
|
218
|
+
};
|
|
219
|
+
} else if (typeof hash === 'string') {
|
|
220
|
+
return {
|
|
221
|
+
value: hash,
|
|
222
|
+
precision: 'nolang',
|
|
223
|
+
lang: ''
|
|
224
|
+
};
|
|
225
|
+
} else if (hash.hasOwnProperty(_locale)) {
|
|
226
|
+
return {
|
|
227
|
+
value: hash[_locale],
|
|
228
|
+
precision: 'exact',
|
|
229
|
+
lang: _locale
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
const pos = _locale.indexOf('_');
|
|
233
|
+
if (pos > -1 && hash.hasOwnProperty(_locale.substr(0, 2))) {
|
|
234
|
+
return {
|
|
235
|
+
value: hash[_locale.substr(0, 2)],
|
|
236
|
+
precision: 'coarsen',
|
|
237
|
+
lang: _locale.substr(0, 2)
|
|
238
|
+
};
|
|
239
|
+
} else if (hash.hasOwnProperty('en')) {
|
|
240
|
+
return {
|
|
241
|
+
value: hash.en,
|
|
242
|
+
precision: 'default',
|
|
243
|
+
lang: 'en'
|
|
244
|
+
};
|
|
245
|
+
} else if (hash.hasOwnProperty('')) {
|
|
246
|
+
return {
|
|
247
|
+
value: hash[''],
|
|
248
|
+
precision: 'nolang',
|
|
249
|
+
lang: ''
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
const allLangs = Object.keys(hash);
|
|
253
|
+
if (allLangs.length > 0) {
|
|
254
|
+
return {
|
|
255
|
+
value: hash[allLangs[0]],
|
|
256
|
+
precision: 'any',
|
|
257
|
+
lang: allLangs[0]
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
precision: 'none'
|
|
262
|
+
};
|
|
263
|
+
};
|
|
264
|
+
const f = (graph, subject, prop) => {
|
|
265
|
+
const stmts = graph.find(subject, prop);
|
|
266
|
+
if (stmts.length > 0) {
|
|
267
|
+
const obj = {};
|
|
268
|
+
for (let s = 0; s < stmts.length; s++) {
|
|
269
|
+
obj[stmts[s].getLanguage() || ''] = stmts[s].getValue();
|
|
270
|
+
}
|
|
271
|
+
return obj;
|
|
272
|
+
}
|
|
273
|
+
return undefined;
|
|
274
|
+
};
|
|
275
|
+
const getLocalizedMap = (graphOrBinding, subject, propArr) => {
|
|
276
|
+
let graph;
|
|
277
|
+
let _subject = subject;
|
|
278
|
+
let _propArr = propArr;
|
|
279
|
+
if (graphOrBinding.getItem) {
|
|
280
|
+
// graphOrBinding is a Binding
|
|
281
|
+
graph = graphOrBinding.getGraph();
|
|
282
|
+
_subject = graphOrBinding.getValue();
|
|
283
|
+
_propArr = graphOrBinding.getItem().getURIValueLabelProperties();
|
|
284
|
+
} else {
|
|
285
|
+
graph = graphOrBinding;
|
|
286
|
+
}
|
|
287
|
+
if (_propArr == null || _propArr.length === 0) {
|
|
288
|
+
_propArr = system.labelProperties;
|
|
289
|
+
}
|
|
290
|
+
for (let i = 0; i < _propArr.length; i++) {
|
|
291
|
+
const props = _propArr[i];
|
|
292
|
+
if (Array.isArray(props)) {
|
|
293
|
+
const valueArr = [];
|
|
294
|
+
for (let j = 0; j < props.length; j++) {
|
|
295
|
+
const value = f(graph, _subject, props[j]);
|
|
296
|
+
if (value) {
|
|
297
|
+
valueArr.push(getLocalizedValue(value).value);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (valueArr.length > 0) {
|
|
301
|
+
return {
|
|
302
|
+
'': valueArr.join(' ')
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
} else {
|
|
306
|
+
const value = f(graph, _subject, props);
|
|
307
|
+
if (value) {
|
|
308
|
+
return value;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return undefined;
|
|
313
|
+
};
|
|
314
|
+
const cloneArrayWithLabels = (objects, noSort) => {
|
|
315
|
+
const itemsArray = [];
|
|
316
|
+
for (let i = 0; i < objects.length; i++) {
|
|
317
|
+
const o = objects[i];
|
|
318
|
+
const currentLabel = getLocalizedValue(o.label);
|
|
319
|
+
const obj = {
|
|
320
|
+
value: o.value,
|
|
321
|
+
label: currentLabel.value || o.value || ''
|
|
322
|
+
};
|
|
323
|
+
if (o.top === true) {
|
|
324
|
+
obj.top = true;
|
|
325
|
+
}
|
|
326
|
+
if (o.children != null) {
|
|
327
|
+
obj.children = (0,external_lodash_namespaceObject.cloneDeep)(o.children);
|
|
328
|
+
}
|
|
329
|
+
if (o.selectable === false) {
|
|
330
|
+
obj.selectable = false;
|
|
331
|
+
} else {
|
|
332
|
+
obj.selectable = true;
|
|
333
|
+
}
|
|
334
|
+
itemsArray.push(obj);
|
|
335
|
+
}
|
|
336
|
+
if (noSort !== true) {
|
|
337
|
+
itemsArray.sort((o1, o2) => o1.label > o2.label ? 1 : -1);
|
|
338
|
+
}
|
|
339
|
+
return itemsArray;
|
|
340
|
+
};
|
|
341
|
+
const extractGist = (str, template) => {
|
|
342
|
+
let _template = template;
|
|
343
|
+
if (_template) {
|
|
344
|
+
if (_template.indexOf('$1') === -1) {
|
|
345
|
+
_template += '$1';
|
|
346
|
+
}
|
|
347
|
+
const r = `${_template}`.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1').replace('\\$1', '(.*)');
|
|
348
|
+
const e = new RegExp(r).exec(str);
|
|
349
|
+
if (e != null) {
|
|
350
|
+
return e[1];
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return str;
|
|
354
|
+
};
|
|
355
|
+
const findFirstValue = (engine, graph, uri, template) => {
|
|
356
|
+
const fvb = engine.findFirstValueBinding(engine.match(graph, uri, template), false);
|
|
357
|
+
if (!fvb) {
|
|
358
|
+
return undefined;
|
|
359
|
+
}
|
|
360
|
+
if (fvb.getChoice) {
|
|
361
|
+
return getLocalizedValue(fvb.getChoice().label).value;
|
|
362
|
+
}
|
|
363
|
+
return fvb.getGist();
|
|
364
|
+
};
|
|
365
|
+
const generateUUID = () => {
|
|
366
|
+
// Public Domain/MIT
|
|
367
|
+
let d = new Date().getTime();
|
|
368
|
+
if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
|
|
369
|
+
d += performance.now(); // use high-precision timer if available
|
|
370
|
+
}
|
|
371
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
|
|
372
|
+
// eslint-disable-next-line no-mixed-operators,no-bitwise
|
|
373
|
+
const r = (d + Math.random() * 16) % 16 | 0;
|
|
374
|
+
d = Math.floor(d / 16);
|
|
375
|
+
// eslint-disable-next-line no-bitwise,no-mixed-operators
|
|
376
|
+
return (c === 'x' ? r : r & 0x3 | 0x8).toString(16);
|
|
377
|
+
});
|
|
378
|
+
};
|
|
379
|
+
/* harmony default export */ const utils = ({
|
|
380
|
+
getLocalizedValue,
|
|
381
|
+
getLocalizedMap,
|
|
382
|
+
cloneArrayWithLabels,
|
|
383
|
+
extractGist,
|
|
384
|
+
findFirstValue,
|
|
385
|
+
generateUUID
|
|
386
|
+
});
|
|
387
|
+
;// ./src/template/Item.js
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
let itemCount = 0;
|
|
391
|
+
const setObjAttr = (obj, attr, value) => {
|
|
392
|
+
if (value === null || typeof value === 'undefined' || value === '' || Array.isArray(value) && value.length === 0) {
|
|
393
|
+
delete obj[attr];
|
|
394
|
+
} else {
|
|
395
|
+
obj[attr] = value;
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
class Item {
|
|
399
|
+
/**
|
|
400
|
+
* Base functionality of Text, Group and Choice item classes.
|
|
401
|
+
*/
|
|
402
|
+
constructor({
|
|
403
|
+
source = {},
|
|
404
|
+
bundle,
|
|
405
|
+
itemStore
|
|
406
|
+
}) {
|
|
407
|
+
this._itemStore = itemStore;
|
|
408
|
+
this._source = source;
|
|
409
|
+
this._bundle = bundle;
|
|
410
|
+
itemCount += 1;
|
|
411
|
+
this._internalId = itemCount;
|
|
412
|
+
this._styles = ['heading', 'invisible', 'invisibleGroup', 'stars', 'commentOn', 'multiline', 'horizontalRadioButtons', 'verticalRadioButtons', 'horizontalCheckBoxes', 'verticalCheckBoxes', 'nonEditable', 'expandable', 'compact', 'nonCompact', 'dropDown', 'table', 'firstcolumnfixedtable', 'tree', 'externalLink', 'internalLink', 'noLink', 'image', 'label', 'tooltip', 'strictmatch', 'relaxedDatatypeMatch', 'viewAllTranslations', 'filterTranslations', 'email', 'atLeastOneChild', 'atMostOneChild', 'exactlyOneChild', 'noLabelInPresent', 'autoInitDate', 'autoUpdateDate', 'autoUUID', 'autoValue', 'showURI', 'showLink', 'showValue', 'showDescriptionInPresent', 'showDescriptionInEdit', 'textAsChoice', 'preserveOrderOfChoices', 'linkWithLabel', 'deprecated', 'inline', 'truncate', 'noTruncate', 'card', 'cardInPresent', 'cardInEdit'];
|
|
413
|
+
this._getLocalizedValue = utils.getLocalizedValue;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
//= ==================================================
|
|
417
|
+
// Public API
|
|
418
|
+
//= ==================================================
|
|
419
|
+
getId() {
|
|
420
|
+
const s = this.getSource(true);
|
|
421
|
+
return s.id || s['@id'];
|
|
422
|
+
}
|
|
423
|
+
setId(id) {
|
|
424
|
+
setObjAttr(this.getSource(true), 'id', id);
|
|
425
|
+
delete s['@id'];
|
|
426
|
+
}
|
|
427
|
+
getType(original) {
|
|
428
|
+
const s = this.getSource(original);
|
|
429
|
+
return s.type || s['@type'];
|
|
430
|
+
}
|
|
431
|
+
setType(typeStr) {
|
|
432
|
+
setObjAttr(this.getSource(true), 'type', typeStr);
|
|
433
|
+
delete s['@type'];
|
|
434
|
+
this.refreshExtends();
|
|
435
|
+
}
|
|
436
|
+
getExtends() {
|
|
437
|
+
return this.getSource(true).extends || '';
|
|
438
|
+
}
|
|
439
|
+
refreshExtends() {
|
|
440
|
+
if (this.isExtention) {
|
|
441
|
+
this.setExtends(this.getExtends());
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
setExtends(extendsStr) {
|
|
445
|
+
const s = this.getSource(true);
|
|
446
|
+
const ei = this._itemStore.getItem(extendsStr);
|
|
447
|
+
if (ei == null) {
|
|
448
|
+
this._source = s;
|
|
449
|
+
} else {
|
|
450
|
+
this._source = this._itemStore.createExtendedSource(ei.getSource(), s);
|
|
451
|
+
}
|
|
452
|
+
if (extendsStr === '' || extendsStr == null) {
|
|
453
|
+
delete s.extends;
|
|
454
|
+
} else {
|
|
455
|
+
s.extends = extendsStr;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
isExtention() {
|
|
459
|
+
return this.getExtends() != null;
|
|
460
|
+
}
|
|
461
|
+
_getText(attr, returnDetails, original) {
|
|
462
|
+
const s = this.getSource(original);
|
|
463
|
+
return returnDetails ? utils.getLocalizedValue(s[attr]) : utils.getLocalizedValue(s[attr]).value;
|
|
464
|
+
}
|
|
465
|
+
_setText(attr, value, lang) {
|
|
466
|
+
const s = this.getSource(true);
|
|
467
|
+
s[attr] = this._setLangHash(s[attr], value, lang);
|
|
468
|
+
this.refreshExtends();
|
|
469
|
+
}
|
|
470
|
+
_setTextMap(attr, map) {
|
|
471
|
+
setObjAttr(this.getSource(true), attr, map);
|
|
472
|
+
this.refreshExtends();
|
|
473
|
+
}
|
|
474
|
+
getLabel(returnDetails, original) {
|
|
475
|
+
return this._getText('label', returnDetails, original);
|
|
476
|
+
}
|
|
477
|
+
setLabel(value, lang) {
|
|
478
|
+
this._setText('label', value, lang);
|
|
479
|
+
}
|
|
480
|
+
getLabelMap(original) {
|
|
481
|
+
return this.getSource(original).label;
|
|
482
|
+
}
|
|
483
|
+
setLabelMap(map) {
|
|
484
|
+
this._setTextMap('label', map);
|
|
485
|
+
}
|
|
486
|
+
getEditLabel(returnDetails, original) {
|
|
487
|
+
return this._getText('editlabel', returnDetails, original);
|
|
488
|
+
}
|
|
489
|
+
setEditLabel(value, lang) {
|
|
490
|
+
this._setText('editlabel', value, lang);
|
|
491
|
+
}
|
|
492
|
+
getEditLabelMap(original) {
|
|
493
|
+
return this.getSource(original).editlabel;
|
|
494
|
+
}
|
|
495
|
+
setEditLabelMap(map) {
|
|
496
|
+
this._setTextMap('editlabel', map);
|
|
497
|
+
}
|
|
498
|
+
getDescription(returnDetails, original) {
|
|
499
|
+
return this._getText('description', returnDetails, original);
|
|
500
|
+
}
|
|
501
|
+
setDescription(value, lang) {
|
|
502
|
+
this._setText('description', value, lang);
|
|
503
|
+
}
|
|
504
|
+
getDescriptionMap(original) {
|
|
505
|
+
return this.getSource(original).description;
|
|
506
|
+
}
|
|
507
|
+
setDescriptionMap(map) {
|
|
508
|
+
this._setTextMap('description', map);
|
|
509
|
+
}
|
|
510
|
+
getEditDescription(returnDetails, original) {
|
|
511
|
+
return this._getText('editdescription', returnDetails, original);
|
|
512
|
+
}
|
|
513
|
+
setEditDescription(value, lang) {
|
|
514
|
+
this._setText('editdescription', value, lang);
|
|
515
|
+
}
|
|
516
|
+
getEditDescriptionMap(original) {
|
|
517
|
+
return this.getSource(original).editdescription;
|
|
518
|
+
}
|
|
519
|
+
setEditDescriptionMap(map) {
|
|
520
|
+
this._setTextMap('editdescription', map);
|
|
521
|
+
}
|
|
522
|
+
getHelp(returnDetails, original) {
|
|
523
|
+
return this._getText('help', returnDetails, original);
|
|
524
|
+
}
|
|
525
|
+
setHelp(value, lang) {
|
|
526
|
+
this._setText('help', value, lang);
|
|
527
|
+
}
|
|
528
|
+
getHelpMap(original) {
|
|
529
|
+
return this.getSource(original).help;
|
|
530
|
+
}
|
|
531
|
+
setHelpMap(map) {
|
|
532
|
+
this._setTextMap('help', map);
|
|
533
|
+
}
|
|
534
|
+
getPlaceholder(returnDetails, original) {
|
|
535
|
+
return this._getText('placeholder', returnDetails, original);
|
|
536
|
+
}
|
|
537
|
+
setPlaceholder(value, lang) {
|
|
538
|
+
this._setText('placeholder', value, lang);
|
|
539
|
+
}
|
|
540
|
+
getPlaceholderMap(original) {
|
|
541
|
+
return this.getSource(original).placeholder;
|
|
542
|
+
}
|
|
543
|
+
setPlaceholderMap(map) {
|
|
544
|
+
this._setTextMap('placeholder', map);
|
|
545
|
+
}
|
|
546
|
+
getPurpose(returnDetails, original) {
|
|
547
|
+
return this._getText('purpose', returnDetails, original);
|
|
548
|
+
}
|
|
549
|
+
setPurpose(value, lang) {
|
|
550
|
+
this._setText('purpose', value, lang);
|
|
551
|
+
}
|
|
552
|
+
getPurposeMap(original) {
|
|
553
|
+
return this.getSource(original).purpose;
|
|
554
|
+
}
|
|
555
|
+
setPurposeMap(map) {
|
|
556
|
+
this._setTextMap('purpose', map);
|
|
557
|
+
}
|
|
558
|
+
getSpecification(returnDetails, original) {
|
|
559
|
+
return this._getText('specification', returnDetails, original);
|
|
560
|
+
}
|
|
561
|
+
setSpecification(value, lang) {
|
|
562
|
+
this._setText('specification', value, lang);
|
|
563
|
+
}
|
|
564
|
+
getSpecificationMap(original) {
|
|
565
|
+
return this.getSource(original).specification;
|
|
566
|
+
}
|
|
567
|
+
setSpecificationMap(map) {
|
|
568
|
+
this._setTextMap('specification', map);
|
|
569
|
+
}
|
|
570
|
+
getText(attr, returnDetails, original) {
|
|
571
|
+
const s = this.getSource(original);
|
|
572
|
+
const t = s.text || {};
|
|
573
|
+
return returnDetails ? utils.getLocalizedValue(t[attr]) : utils.getLocalizedValue(t[attr]).value;
|
|
574
|
+
}
|
|
575
|
+
setText(attr, value, lang) {
|
|
576
|
+
const s = this.getSource(true);
|
|
577
|
+
const t = s.text = s.text || {};
|
|
578
|
+
t[attr] = this._setLangHash(t[attr], value, lang);
|
|
579
|
+
this.refreshExtends();
|
|
580
|
+
}
|
|
581
|
+
setTextMap(attr, map) {
|
|
582
|
+
const s = this.getSource(true);
|
|
583
|
+
const t = s.text = s.text || {};
|
|
584
|
+
setObjAttr(t, attr, map);
|
|
585
|
+
this.refreshExtends();
|
|
586
|
+
}
|
|
587
|
+
getEnhanced(attribute) {
|
|
588
|
+
const s = this.getSource(true);
|
|
589
|
+
if (typeof s.enhanced === 'boolean') {
|
|
590
|
+
return s.enhanced;
|
|
591
|
+
}
|
|
592
|
+
return s.enhanced && s.enhanced[attribute] || false;
|
|
593
|
+
}
|
|
594
|
+
setEnhanced(attribute, enhanced) {
|
|
595
|
+
const s = this.getSource(true);
|
|
596
|
+
if (typeof attribute === 'boolean') {
|
|
597
|
+
if (attribute === true) {
|
|
598
|
+
s.enhanced = true;
|
|
599
|
+
} else {
|
|
600
|
+
delete s.enhanced;
|
|
601
|
+
}
|
|
602
|
+
} else {
|
|
603
|
+
s.enhanced = typeof s.enhanced === 'boolean' ? {} : s.enhanced || {};
|
|
604
|
+
if (enhanced) {
|
|
605
|
+
s.enhanced[attribute] = true;
|
|
606
|
+
} else {
|
|
607
|
+
delete s.enhanced[attribute];
|
|
608
|
+
if (Object.keys(s).length === 0) {
|
|
609
|
+
delete s.enhanced;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
// Simple way to refresh this._source which is a cache including potential enhancements
|
|
614
|
+
this.setExtends(this.getExtends());
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* @return {String|null} as a URI, may be null for Groups, never null for Text or choice
|
|
619
|
+
* item types.
|
|
620
|
+
*/
|
|
621
|
+
getProperty(original) {
|
|
622
|
+
let p = null;
|
|
623
|
+
const source = this.getSource(original);
|
|
624
|
+
if (source) {
|
|
625
|
+
p = source.property;
|
|
626
|
+
if (p != null && p !== '') {
|
|
627
|
+
p = rdfjson_namespaceObject.namespaces.expand(p);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return p;
|
|
631
|
+
}
|
|
632
|
+
setProperty(prop) {
|
|
633
|
+
setObjAttr(this.getSource(true), 'property', prop);
|
|
634
|
+
this.refreshExtends();
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* If the value is a uri, it is not nice to show it directly.
|
|
639
|
+
* Hence, we need to discover (or provide if we are in edit mode)
|
|
640
|
+
* a suitable label for the URI. Labels are typically provided by
|
|
641
|
+
* having triples with the URI as subject, but which predicates do we use?
|
|
642
|
+
*
|
|
643
|
+
* URIValueLabelProperties is an array of properties to be used in this scenario,
|
|
644
|
+
* were each should be tried in turn. In editing mode the first property should be used,
|
|
645
|
+
* alternatively a dropdown can be used to select among the properties.
|
|
646
|
+
*
|
|
647
|
+
* @return {Array} array of properties
|
|
648
|
+
* The property value pairs corresponds to predicate and objects in required tripples.
|
|
649
|
+
*/
|
|
650
|
+
getURIValueLabelProperties(original) {
|
|
651
|
+
const arr = this.getSource(original).uriValueLabelProperties;
|
|
652
|
+
if (arr != null) {
|
|
653
|
+
return arr.map(uri => rdfjson_namespaceObject.namespaces.expand(uri));
|
|
654
|
+
}
|
|
655
|
+
return arr;
|
|
656
|
+
}
|
|
657
|
+
setURIValueLabelProperties(props) {
|
|
658
|
+
setObjAttr(this.getSource(true), 'uriValueLabelProperties', props);
|
|
659
|
+
this.refreshExtends();
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* @return {Object} never available for Text item type.
|
|
664
|
+
* The property value pairs corresponds to predicate and objects in required tripples.
|
|
665
|
+
*/
|
|
666
|
+
getConstraints(original) {
|
|
667
|
+
const constr = this.getSource(original).constraints;
|
|
668
|
+
if (constr != null) {
|
|
669
|
+
const nc = {};
|
|
670
|
+
Object.keys(constr).forEach(key => {
|
|
671
|
+
const val = constr[key];
|
|
672
|
+
if (Array.isArray(val)) {
|
|
673
|
+
nc[rdfjson_namespaceObject.namespaces.expand(key)] = val.map(v => rdfjson_namespaceObject.namespaces.expand(v));
|
|
674
|
+
} else {
|
|
675
|
+
nc[rdfjson_namespaceObject.namespaces.expand(key)] = rdfjson_namespaceObject.namespaces.expand(val);
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
return nc;
|
|
679
|
+
}
|
|
680
|
+
return constr;
|
|
681
|
+
}
|
|
682
|
+
setConstraints(constr) {
|
|
683
|
+
setObjAttr(this.getSource(true), 'constraints', constr);
|
|
684
|
+
this.refreshExtends();
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Deps is an array of strings corresponding to predicates, "*" may be used to match
|
|
689
|
+
* anything. The final string in the path may correspond to the object,
|
|
690
|
+
* e.g. a literal or uri.
|
|
691
|
+
* By default, the dependency is given relative to the current items parent.
|
|
692
|
+
* If dependency path should start higher up it can be indicated by providing one or more
|
|
693
|
+
* initial strings with value "..".
|
|
694
|
+
*
|
|
695
|
+
* @return {Object} dependency path that must exist for this item to be visible.
|
|
696
|
+
*
|
|
697
|
+
*/
|
|
698
|
+
getDeps(original) {
|
|
699
|
+
const deps = this.getSource(original).deps;
|
|
700
|
+
if (deps != null) {
|
|
701
|
+
return deps.map(d => {
|
|
702
|
+
if (d !== '*' && d !== '..') {
|
|
703
|
+
return rdfjson_namespaceObject.namespaces.expand(d);
|
|
704
|
+
}
|
|
705
|
+
return d;
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
return deps;
|
|
709
|
+
}
|
|
710
|
+
setDeps(deps) {
|
|
711
|
+
setObjAttr(this.getSource(true), 'deps', deps);
|
|
712
|
+
this.refreshExtends();
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* @return {String} a URI indicating the datatype, for example: "http://www.w3.org/2001/XMLSchema.xsd#date".
|
|
717
|
+
*/
|
|
718
|
+
getDatatype(original) {
|
|
719
|
+
const dt = this.getSource(original).datatype;
|
|
720
|
+
if (dt != null && dt !== '') {
|
|
721
|
+
return Array.isArray(dt) ? dt.map(d => rdfjson_namespaceObject.namespaces.expand(d)) : rdfjson_namespaceObject.namespaces.expand(dt);
|
|
722
|
+
}
|
|
723
|
+
return dt;
|
|
724
|
+
}
|
|
725
|
+
setDatatype(dt) {
|
|
726
|
+
setObjAttr(this.getSource(true), 'datatype', dt);
|
|
727
|
+
this.refreshExtends();
|
|
728
|
+
}
|
|
729
|
+
getPattern(original) {
|
|
730
|
+
return this.getSource(original).pattern;
|
|
731
|
+
}
|
|
732
|
+
setPattern(pattern) {
|
|
733
|
+
setObjAttr(this.getSource(true), 'pattern', pattern);
|
|
734
|
+
this.refreshExtends();
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* @return {String} a two character language code, only relevant if the item type is Text and the nodetype is
|
|
739
|
+
* a LANGUAGE_LITERAL, indicating that all matching bindings should be set with this language.
|
|
740
|
+
*/
|
|
741
|
+
getLanguage(original) {
|
|
742
|
+
return this.getSource(original).language;
|
|
743
|
+
}
|
|
744
|
+
setLanguage(lang) {
|
|
745
|
+
setObjAttr(this.getSource(true), 'language', lang);
|
|
746
|
+
this.refreshExtends();
|
|
747
|
+
}
|
|
748
|
+
getMember(original) {
|
|
749
|
+
return this.getSource(original).member;
|
|
750
|
+
}
|
|
751
|
+
setMember(member) {
|
|
752
|
+
setObjAttr(this.getSource(true), 'member', member);
|
|
753
|
+
this.refreshExtends();
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Allowed values are:
|
|
758
|
+
* LITERAL, RESOURCE, URI, BLANK, PLAIN_LITERAL, ONLY_LITERAL, LANGUAGE_LITERAL, DATATYPE_LITERAL
|
|
759
|
+
*/
|
|
760
|
+
getNodetype(original) {
|
|
761
|
+
const s = this.getSource(original);
|
|
762
|
+
return s.nodetype || s.nodeType; // Ugly fix because it is often wrong written in SIRFF.
|
|
763
|
+
}
|
|
764
|
+
setNodetype(nt) {
|
|
765
|
+
setObjAttr(this.getSource(true), 'nodetype', nt);
|
|
766
|
+
this.refreshExtends();
|
|
767
|
+
}
|
|
768
|
+
getValue(original) {
|
|
769
|
+
return this.getSource(original).value;
|
|
770
|
+
}
|
|
771
|
+
setValue(value) {
|
|
772
|
+
setObjAttr(this.getSource(true), 'value', value);
|
|
773
|
+
this.refreshExtends();
|
|
774
|
+
}
|
|
775
|
+
getValueTemplate(original) {
|
|
776
|
+
return this.getSource(original).valueTemplate;
|
|
777
|
+
}
|
|
778
|
+
setValueTemplate(valueTemplate) {
|
|
779
|
+
setObjAttr(this.getSource(true), 'valueTemplate', valueTemplate);
|
|
780
|
+
this.refreshExtends();
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* @return {Object} containing max, min, and preferred properties.
|
|
785
|
+
*/
|
|
786
|
+
getCardinality(original) {
|
|
787
|
+
if (!this.getProperty() && this.getType() === 'text') {
|
|
788
|
+
return {
|
|
789
|
+
min: 1,
|
|
790
|
+
max: 1
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
const source = this.getSource(original);
|
|
794
|
+
if (source && 'cardinality' in source) {
|
|
795
|
+
return source.cardinality;
|
|
796
|
+
}
|
|
797
|
+
if (!this.getProperty()) {
|
|
798
|
+
return {
|
|
799
|
+
max: 1
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
return {};
|
|
803
|
+
}
|
|
804
|
+
setCardinality(card) {
|
|
805
|
+
setObjAttr(this.getSource(true), 'cardinality', card);
|
|
806
|
+
this.refreshExtends();
|
|
807
|
+
}
|
|
808
|
+
isEnabled(original) {
|
|
809
|
+
const s = this.getSource(original);
|
|
810
|
+
return s.enabled == null ? true : s.enabled;
|
|
811
|
+
}
|
|
812
|
+
setEnabled(en) {
|
|
813
|
+
const s = this.getSource(true);
|
|
814
|
+
if (en) {
|
|
815
|
+
delete s.enabled;
|
|
816
|
+
} else {
|
|
817
|
+
s.enabled = en;
|
|
818
|
+
}
|
|
819
|
+
this.refreshExtends();
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* Classes are exposed in CSS, allows external stylesheets to act on the form.
|
|
824
|
+
* @returns {Array}
|
|
825
|
+
*/
|
|
826
|
+
getClasses(original) {
|
|
827
|
+
const source = this.getSource(original);
|
|
828
|
+
if (source && 'cls' in source) {
|
|
829
|
+
return source.cls;
|
|
830
|
+
}
|
|
831
|
+
return [];
|
|
832
|
+
}
|
|
833
|
+
setClasses(arr) {
|
|
834
|
+
setObjAttr(this.getSource(true), 'cls', arr);
|
|
835
|
+
this.refreshExtends();
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* @deprecated only provided for backward compatability, use styles, classes
|
|
840
|
+
* @param cls
|
|
841
|
+
* @returns {boolean}
|
|
842
|
+
*/
|
|
843
|
+
hasClass(cls, original) {
|
|
844
|
+
const s = this.getSource(original);
|
|
845
|
+
if (this.hasStyle(cls, original)) {
|
|
846
|
+
return true;
|
|
847
|
+
}
|
|
848
|
+
if (s.cls == null) {
|
|
849
|
+
return false;
|
|
850
|
+
}
|
|
851
|
+
return s.cls.some(c => c.toLowerCase() === cls.toLowerCase());
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* The available styles, see the _styles variable.
|
|
856
|
+
*
|
|
857
|
+
* @return {Array} that contains strings with all available styles.
|
|
858
|
+
*/
|
|
859
|
+
getAvailableStyles() {
|
|
860
|
+
return this._styles;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* @return {Array} that contains strings with the style, if no style is defined an empty array is returned
|
|
865
|
+
*/
|
|
866
|
+
getStyles(original) {
|
|
867
|
+
return this.getSource(original).styles || [];
|
|
868
|
+
}
|
|
869
|
+
setStyles(arr) {
|
|
870
|
+
setObjAttr(this.getSource(true), 'styles', arr);
|
|
871
|
+
this.refreshExtends();
|
|
872
|
+
}
|
|
873
|
+
hasStyle(sty, original) {
|
|
874
|
+
const source = this.getSource(original);
|
|
875
|
+
if (!source || !('styles' in source)) {
|
|
876
|
+
return false;
|
|
877
|
+
}
|
|
878
|
+
return source.styles.some(s => s.toLowerCase() === sty.toLowerCase());
|
|
879
|
+
}
|
|
880
|
+
getSource(original, attribute) {
|
|
881
|
+
if (original === true) {
|
|
882
|
+
// Get the original source
|
|
883
|
+
return this._source._extendedSource || this._source;
|
|
884
|
+
} else if (original === false) {
|
|
885
|
+
// Get the extended source
|
|
886
|
+
const entryItem = this._itemStore.getItem(this.getExtends());
|
|
887
|
+
if (entryItem == null) {
|
|
888
|
+
return this._source;
|
|
889
|
+
}
|
|
890
|
+
return entryItem.getSource();
|
|
891
|
+
} // Get the merged source.
|
|
892
|
+
return this._source;
|
|
893
|
+
}
|
|
894
|
+
getBundle() {
|
|
895
|
+
return this._bundle;
|
|
896
|
+
}
|
|
897
|
+
toStringShort() {
|
|
898
|
+
return `'${this.getLabel()}'${this.getId() ? ` (ID: '${this.getId()}')` : ''}`;
|
|
899
|
+
}
|
|
900
|
+
toString() {
|
|
901
|
+
const detailsArr = [];
|
|
902
|
+
if (this.getId()) {
|
|
903
|
+
detailsArr.push(`ID: '${this.getId()}'`);
|
|
904
|
+
}
|
|
905
|
+
detailsArr.push(`TYPE: '${this.getType()}'`);
|
|
906
|
+
if (this.getProperty()) {
|
|
907
|
+
detailsArr.push(`PROPERTY: '${this.getProperty()}'`);
|
|
908
|
+
}
|
|
909
|
+
if (this.getExtends()) {
|
|
910
|
+
detailsArr.push(`EXTENDS: '${this.getExtends()}'`);
|
|
911
|
+
}
|
|
912
|
+
return `'${this.getLabel()}' (${detailsArr.join(', ')})`;
|
|
913
|
+
}
|
|
914
|
+
getHash() {
|
|
915
|
+
return `i_${this._internalId}`;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
//= ==================================================
|
|
919
|
+
// Inherited methods
|
|
920
|
+
//= ==================================================
|
|
921
|
+
|
|
922
|
+
//= ==================================================
|
|
923
|
+
// Private methods
|
|
924
|
+
//= ==================================================
|
|
925
|
+
|
|
926
|
+
// eslint-disable-next-line class-methods-use-this
|
|
927
|
+
_setLangHash(hash, value, lang) {
|
|
928
|
+
const _hash = hash || {};
|
|
929
|
+
if (typeof value === 'string') {
|
|
930
|
+
if (typeof lang === 'string') {
|
|
931
|
+
_hash[lang] = value;
|
|
932
|
+
} else {
|
|
933
|
+
_hash[''] = value;
|
|
934
|
+
}
|
|
935
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
936
|
+
return value;
|
|
937
|
+
}
|
|
938
|
+
return _hash;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
;// ./src/template/Group.js
|
|
942
|
+
|
|
943
|
+
const sortItems = items => {
|
|
944
|
+
items.forEach(item => {
|
|
945
|
+
item.__label = (item.getLabel() || '').toLowerCase();
|
|
946
|
+
});
|
|
947
|
+
items.sort((o1, o2) => {
|
|
948
|
+
if (o1._source.priority != null) {
|
|
949
|
+
if (o2._source.priority != null) {
|
|
950
|
+
// eslint-disable-next-line no-nested-ternary
|
|
951
|
+
return o1._source.priority > o2._source.priority ? -1 : o1._source.priority < o2._source.priority ? 1 : 0;
|
|
952
|
+
}
|
|
953
|
+
return o1._source.priority > 0 ? -1 : 1;
|
|
954
|
+
} else if (o2._source.priority != null) {
|
|
955
|
+
return o2._source.priority > 0 ? 1 : -1;
|
|
956
|
+
} else if (o1.__label > o2.__label) {
|
|
957
|
+
return 1;
|
|
958
|
+
} else if (o1.__label < o2.__label) {
|
|
959
|
+
return -1;
|
|
960
|
+
}
|
|
961
|
+
return 0;
|
|
962
|
+
});
|
|
963
|
+
};
|
|
964
|
+
class Group extends Item {
|
|
965
|
+
/**
|
|
966
|
+
* Group extends an Item by having children.
|
|
967
|
+
*/
|
|
968
|
+
constructor(params) {
|
|
969
|
+
super(params);
|
|
970
|
+
this._children = params.children;
|
|
971
|
+
if (this._source.content != null) {
|
|
972
|
+
this._source.items = this._source.content;
|
|
973
|
+
delete this._source.content;
|
|
974
|
+
}
|
|
975
|
+
this._forceChildrenClones = false;
|
|
976
|
+
this._parent = null;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// ===================================================
|
|
980
|
+
// Public API
|
|
981
|
+
// ===================================================
|
|
982
|
+
getChildren(original) {
|
|
983
|
+
const _original = original && this.isExtention();
|
|
984
|
+
let children = _original ? this._ochildren : this._children;
|
|
985
|
+
if (children == null) {
|
|
986
|
+
children = this._itemStore.getChildren(this, _original);
|
|
987
|
+
if (this.getSource().automatic === true && this._itemStore.automaticSortAllowed) {
|
|
988
|
+
sortItems(children);
|
|
989
|
+
}
|
|
990
|
+
this[`_${_original ? 'o' : ''}children`] = children;
|
|
991
|
+
}
|
|
992
|
+
return children;
|
|
993
|
+
}
|
|
994
|
+
originalChildrenChanged() {
|
|
995
|
+
if (this.isExtention()) {
|
|
996
|
+
delete this._children;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
setExtends(extendsStr) {
|
|
1000
|
+
super.setExtends(extendsStr);
|
|
1001
|
+
delete this._children;
|
|
1002
|
+
delete this._ochildren;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
// ===================================================
|
|
1006
|
+
// Inherited methods
|
|
1007
|
+
// ===================================================
|
|
1008
|
+
|
|
1009
|
+
getNodetype() {
|
|
1010
|
+
return super.getNodetype() || 'RESOURCE'; // Ugly fix because it is often wrong written in SIRFF.
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
;// ./src/template/PropertyGroup.js
|
|
1014
|
+
|
|
1015
|
+
class PropertyGroup extends Group {
|
|
1016
|
+
/**
|
|
1017
|
+
* A PropertyGroup captures the special case when both the predicate and object of a
|
|
1018
|
+
* tripple should be changable. This is achieved by having a PropertyGroup where the
|
|
1019
|
+
* first child is a Choice item corresponding to the predicate and the second being
|
|
1020
|
+
* an item corresponding to the object in the triple. The second item can be either a
|
|
1021
|
+
* Text, Choice or Group item depending on the kind of object envisioned in the triple.
|
|
1022
|
+
*/
|
|
1023
|
+
getChildren(original) {
|
|
1024
|
+
if (this._delegatedChildren == null) {
|
|
1025
|
+
const getCardinality = () => ({
|
|
1026
|
+
min: 1,
|
|
1027
|
+
max: 1,
|
|
1028
|
+
pref: 1
|
|
1029
|
+
});
|
|
1030
|
+
const children = super.getChildren(original) || [];
|
|
1031
|
+
this._delegatedChildren = children.map(child => {
|
|
1032
|
+
const delegate = Object.create(child);
|
|
1033
|
+
delegate.getCardinality = getCardinality;
|
|
1034
|
+
return delegate;
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
return this._delegatedChildren;
|
|
1038
|
+
}
|
|
1039
|
+
getPropertyItem() {
|
|
1040
|
+
return this.getChildren()[0];
|
|
1041
|
+
}
|
|
1042
|
+
getObjectItem() {
|
|
1043
|
+
return this.getChildren()[1];
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
;// ./src/template/Text.js
|
|
1047
|
+
|
|
1048
|
+
|
|
1049
|
+
/**
|
|
1050
|
+
* Same functionality as an Item, but separate class to make switching on type possible.
|
|
1051
|
+
*/
|
|
1052
|
+
class Text extends Item {
|
|
1053
|
+
getLabelProperties(original) {
|
|
1054
|
+
return this.getSource(original).labelProperties;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
;// ./src/template/Choice.js
|
|
1058
|
+
|
|
1059
|
+
|
|
1060
|
+
const expandValues = choices => {
|
|
1061
|
+
if (choices == null) {
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
choices.forEach(c => {
|
|
1065
|
+
c.value = rdfjson_namespaceObject.namespaces.expand(c.value);
|
|
1066
|
+
});
|
|
1067
|
+
};
|
|
1068
|
+
class Choice extends Item {
|
|
1069
|
+
/**
|
|
1070
|
+
* A choice item type indicates that the value should be one of a range of predefined choices,
|
|
1071
|
+
* these predefined choices can be defined manually in the template or extracted from an external
|
|
1072
|
+
* ontology (indicated by the ontologyUrl) by means of a query that can be constructed from the constraints.
|
|
1073
|
+
*
|
|
1074
|
+
* TODO:
|
|
1075
|
+
* The choices can also be organized into a hierarchy using the parent and hierarchy properties.
|
|
1076
|
+
*/
|
|
1077
|
+
constructor(params) {
|
|
1078
|
+
super(params);
|
|
1079
|
+
this._ontologyStore = params.ontologyStore;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
// ===================================================
|
|
1083
|
+
// Public API
|
|
1084
|
+
// ===================================================
|
|
1085
|
+
/**
|
|
1086
|
+
* A choice is an object which looks like:
|
|
1087
|
+
* {"value": "http://example.com/choice1",
|
|
1088
|
+
* "label": {"en": "First choice", "sv": "Första valet"}
|
|
1089
|
+
* }
|
|
1090
|
+
*
|
|
1091
|
+
* @return {Array} of choices.
|
|
1092
|
+
*/
|
|
1093
|
+
getChoices(original) {
|
|
1094
|
+
return this.getStaticChoices(original) || this.getDynamicChoices(original) || [];
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
/**
|
|
1098
|
+
* @return {Boolean} true if there is an ontology or static choices.
|
|
1099
|
+
*/
|
|
1100
|
+
hasChoices(original) {
|
|
1101
|
+
const s = this.getSource(original);
|
|
1102
|
+
return s.ontologyUrl != null || s.choices != null;
|
|
1103
|
+
}
|
|
1104
|
+
hasStaticChoices(original) {
|
|
1105
|
+
const s = this.getSource(original);
|
|
1106
|
+
return s.choices != null;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
/**
|
|
1110
|
+
* @return {Array} of choices defined manually in the Template.
|
|
1111
|
+
*/
|
|
1112
|
+
getStaticChoices(original) {
|
|
1113
|
+
const s = this.getSource(original);
|
|
1114
|
+
if (s.choices) {
|
|
1115
|
+
const isURI = this.getNodetype().indexOf('LITERAL') === -1;
|
|
1116
|
+
if (original && this.isExtention()) {
|
|
1117
|
+
if (!this._origStaticIsSorted) {
|
|
1118
|
+
if (isURI) {
|
|
1119
|
+
expandValues(s.choices);
|
|
1120
|
+
}
|
|
1121
|
+
this._origStaticIsSorted = true;
|
|
1122
|
+
}
|
|
1123
|
+
} else if (!this._staticIsSorted) {
|
|
1124
|
+
if (isURI) expandValues(s.choices);
|
|
1125
|
+
this._staticIsSorted = true;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
return s.choices;
|
|
1129
|
+
}
|
|
1130
|
+
setStaticChoices(choices) {
|
|
1131
|
+
const s = this.getSource(true);
|
|
1132
|
+
if (s.choices === choices) {
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
if (choices != null) {
|
|
1136
|
+
this._origStaticIsSorted = true;
|
|
1137
|
+
}
|
|
1138
|
+
s.choices = choices;
|
|
1139
|
+
this.refreshExtends();
|
|
1140
|
+
}
|
|
1141
|
+
setExtends(extendsStr) {
|
|
1142
|
+
super.setExtends(extendsStr);
|
|
1143
|
+
delete this._staticIsSorted;
|
|
1144
|
+
delete this._origStaticIsSorted;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
/**
|
|
1148
|
+
* Fetches choices from an external ontology.
|
|
1149
|
+
*
|
|
1150
|
+
* @param {Object} callback will be called asynchronously, if undefined the call is made synchronously.
|
|
1151
|
+
* @return {Array} of choice objects, only provided if method called without callback.
|
|
1152
|
+
*/
|
|
1153
|
+
getDynamicChoices(callback) {
|
|
1154
|
+
if (this._dynamicChoices == null) {
|
|
1155
|
+
if (callback == null) {
|
|
1156
|
+
this._dynamicChoices = this._ontologyStore.getChoices(this);
|
|
1157
|
+
return this._dynamicChoices;
|
|
1158
|
+
}
|
|
1159
|
+
this._ontologyStore.getChoices(this, choices => {
|
|
1160
|
+
this._dynamicChoices = choices;
|
|
1161
|
+
if (this._dynamicChoices == null) {
|
|
1162
|
+
console.log(`Failed lookup of choices for ${this.getLabel()}`);
|
|
1163
|
+
console.log(`OntologyUrl is: ${this._source.ontologyUrl}`);
|
|
1164
|
+
}
|
|
1165
|
+
callback(this._dynamicChoices);
|
|
1166
|
+
});
|
|
1167
|
+
} else {
|
|
1168
|
+
if (callback == null) {
|
|
1169
|
+
return this._dynamicChoices;
|
|
1170
|
+
}
|
|
1171
|
+
callback(this._dynamicChoices);
|
|
1172
|
+
}
|
|
1173
|
+
return undefined;
|
|
1174
|
+
}
|
|
1175
|
+
getOntologyUrl(original) {
|
|
1176
|
+
const ou = this.getSource(original).ontologyUrl;
|
|
1177
|
+
if (ou != null && ou !== '') {
|
|
1178
|
+
return rdfjson_namespaceObject.namespaces.expand(ou);
|
|
1179
|
+
}
|
|
1180
|
+
return ou;
|
|
1181
|
+
}
|
|
1182
|
+
setOntologyUrl(url) {
|
|
1183
|
+
const s = this.getSource(true);
|
|
1184
|
+
if (url == null || url === '') {
|
|
1185
|
+
delete s.ontologyUrl;
|
|
1186
|
+
} else {
|
|
1187
|
+
s.ontologyUrl = url;
|
|
1188
|
+
}
|
|
1189
|
+
this.refreshExtends();
|
|
1190
|
+
}
|
|
1191
|
+
getLabelProperties(original) {
|
|
1192
|
+
return this.getSource(original).labelProperties || ['http://www.w3.org/2000/01/rdf-schema#label'];
|
|
1193
|
+
}
|
|
1194
|
+
getParentProperty(original) {
|
|
1195
|
+
const pp = this.getSource(original).parentProperty;
|
|
1196
|
+
if (pp != null && pp !== '') {
|
|
1197
|
+
return rdfjson_namespaceObject.namespaces.expand(pp);
|
|
1198
|
+
}
|
|
1199
|
+
return pp;
|
|
1200
|
+
}
|
|
1201
|
+
setParentProperty(prop) {
|
|
1202
|
+
const s = this.getSource(true);
|
|
1203
|
+
if (prop == null || prop === '') {
|
|
1204
|
+
delete s.parentProperty;
|
|
1205
|
+
} else {
|
|
1206
|
+
s.parentProperty = prop;
|
|
1207
|
+
}
|
|
1208
|
+
this.refreshExtends();
|
|
1209
|
+
}
|
|
1210
|
+
getHierarchyProperty(original) {
|
|
1211
|
+
const hp = this.getSource(original).hierarchyProperty;
|
|
1212
|
+
if (hp != null && hp !== '') {
|
|
1213
|
+
return rdfjson_namespaceObject.namespaces.expand(hp);
|
|
1214
|
+
}
|
|
1215
|
+
return hp;
|
|
1216
|
+
}
|
|
1217
|
+
setHierarchyProperty(prop) {
|
|
1218
|
+
const s = this.getSource(true);
|
|
1219
|
+
if (prop == null || prop === '') {
|
|
1220
|
+
delete s.hierarchyProperty;
|
|
1221
|
+
} else {
|
|
1222
|
+
s.hierarchyProperty = prop;
|
|
1223
|
+
}
|
|
1224
|
+
this.refreshExtends();
|
|
1225
|
+
}
|
|
1226
|
+
isParentPropertyInverted(original) {
|
|
1227
|
+
return this.getSource(original).isParentPropertyInverted === true;
|
|
1228
|
+
}
|
|
1229
|
+
setParentPropertyInverted(inverted) {
|
|
1230
|
+
const s = this.getSource(true);
|
|
1231
|
+
if (inverted === true) {
|
|
1232
|
+
s.isParentPropertyInverted = true;
|
|
1233
|
+
} else {
|
|
1234
|
+
delete s.isParentPropertyInverted;
|
|
1235
|
+
}
|
|
1236
|
+
this.refreshExtends();
|
|
1237
|
+
}
|
|
1238
|
+
isHierarchyPropertyInverted(original) {
|
|
1239
|
+
return this.getSource(original).isHierarchyPropertyInverted === true;
|
|
1240
|
+
}
|
|
1241
|
+
setHierarchyPropertyInverted(inverted) {
|
|
1242
|
+
const s = this.getSource(true);
|
|
1243
|
+
if (inverted) {
|
|
1244
|
+
s.isHierarchyPropertyInverted = true;
|
|
1245
|
+
} else {
|
|
1246
|
+
delete s.isHierarchyPropertyInverted;
|
|
1247
|
+
}
|
|
1248
|
+
this.refreshExtends();
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
;// ./src/template/OntologyStore.js
|
|
1252
|
+
|
|
1253
|
+
class OntologyStore {
|
|
1254
|
+
/**
|
|
1255
|
+
* Simple store of ontologies to allow reuse across templates and items.
|
|
1256
|
+
*/
|
|
1257
|
+
constructor() {
|
|
1258
|
+
this._registry = {};
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
//= ==================================================
|
|
1262
|
+
// Public API
|
|
1263
|
+
//= ==================================================
|
|
1264
|
+
importRegistry(registry) {
|
|
1265
|
+
Object.assign(this._registry, registry);
|
|
1266
|
+
}
|
|
1267
|
+
getChoices(choiceItem, callback) {
|
|
1268
|
+
const choices = this._findChoices(choiceItem);
|
|
1269
|
+
if (choices == null) {
|
|
1270
|
+
// TODO load via xhr and deferred.
|
|
1271
|
+
} else {
|
|
1272
|
+
if (callback == null) {
|
|
1273
|
+
return choices;
|
|
1274
|
+
}
|
|
1275
|
+
callback(choices);
|
|
1276
|
+
}
|
|
1277
|
+
return undefined;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
//= ==================================================
|
|
1281
|
+
// Private methods
|
|
1282
|
+
//= ==================================================
|
|
1283
|
+
_findChoices(item) {
|
|
1284
|
+
const ontologyChoiceArr = this._registry[item.getOntologyUrl()];
|
|
1285
|
+
if (ontologyChoiceArr != null) {
|
|
1286
|
+
for (let ind = 0; ind < ontologyChoiceArr.length; ind++) {
|
|
1287
|
+
const obj = ontologyChoiceArr[ind];
|
|
1288
|
+
if ((0,external_lodash_namespaceObject.isEqual)(obj.constraints, item.getConstraints()) && item.getParentProperty() === obj.parentProperty && item.getHierarchyProperty() === obj.hierarchyProperty && item.isParentPropertyInverted() === (obj.isParentPropertyInverted || false) && item.isHierarchyPropertyInverted() === (obj.isHierarchyPropertyInverted || false)) {
|
|
1289
|
+
return obj.choices;
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
return undefined;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
// eslint-disable-next-line class-methods-use-this
|
|
1297
|
+
_constructLoadUrl(choiceItem) {
|
|
1298
|
+
const params = [];
|
|
1299
|
+
params.push(`constr=${encodeURIComponent(JSON.stringify(choiceItem.getConstraints()))}`);
|
|
1300
|
+
if (choiceItem.getParentProperty() != null) {
|
|
1301
|
+
const pp = choiceItem.isParentPropertyInverted() === true ? 'ipp=' : 'pp=';
|
|
1302
|
+
params.push(pp + encodeURIComponent(choiceItem.getParentProperty()));
|
|
1303
|
+
}
|
|
1304
|
+
if (choiceItem.getHierarchyProperty() != null) {
|
|
1305
|
+
const hp = choiceItem.isHierarchyPropertyInverted() === true ? 'ihp=' : 'hp=';
|
|
1306
|
+
params.push(hp + encodeURIComponent(choiceItem.getHierarchyProperty()));
|
|
1307
|
+
}
|
|
1308
|
+
return `${choiceItem.getOntologyUrl()}?${params.join('&')}`;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
;// ./src/template/Bundle.js
|
|
1312
|
+
let counter = 0;
|
|
1313
|
+
class Bundle {
|
|
1314
|
+
/**
|
|
1315
|
+
* A Bundle corresponds to a set of items typically managed in a single file.
|
|
1316
|
+
*/
|
|
1317
|
+
constructor({
|
|
1318
|
+
itemStore,
|
|
1319
|
+
source,
|
|
1320
|
+
path,
|
|
1321
|
+
readOnly = false
|
|
1322
|
+
}) {
|
|
1323
|
+
this._itemStore = itemStore;
|
|
1324
|
+
this._source = source;
|
|
1325
|
+
this._path = path;
|
|
1326
|
+
this._readOnly = readOnly;
|
|
1327
|
+
this._items = [];
|
|
1328
|
+
counter += 1;
|
|
1329
|
+
this._id = `_bundle_${counter}`;
|
|
1330
|
+
this._root = null;
|
|
1331
|
+
this._modified = false;
|
|
1332
|
+
}
|
|
1333
|
+
getInternalId() {
|
|
1334
|
+
return this._id;
|
|
1335
|
+
}
|
|
1336
|
+
getSource() {
|
|
1337
|
+
return this._source;
|
|
1338
|
+
}
|
|
1339
|
+
setRoot(itemId) {
|
|
1340
|
+
this._source.root = itemId;
|
|
1341
|
+
return itemId;
|
|
1342
|
+
}
|
|
1343
|
+
getRoot() {
|
|
1344
|
+
if (this._source.root) {
|
|
1345
|
+
return this._itemStore.getItem(this._source.root);
|
|
1346
|
+
}
|
|
1347
|
+
return undefined;
|
|
1348
|
+
}
|
|
1349
|
+
getItemStore() {
|
|
1350
|
+
return this._itemStore;
|
|
1351
|
+
}
|
|
1352
|
+
getPath() {
|
|
1353
|
+
return this._path;
|
|
1354
|
+
}
|
|
1355
|
+
getItems() {
|
|
1356
|
+
return this._items;
|
|
1357
|
+
}
|
|
1358
|
+
addItem(item) {
|
|
1359
|
+
this._items.push(item);
|
|
1360
|
+
}
|
|
1361
|
+
removeItem(item) {
|
|
1362
|
+
this._items.splice(this._items.indexOf(item), 1);
|
|
1363
|
+
}
|
|
1364
|
+
isModified() {
|
|
1365
|
+
return this._modified;
|
|
1366
|
+
}
|
|
1367
|
+
setModified(modified) {
|
|
1368
|
+
this._modified = modified;
|
|
1369
|
+
}
|
|
1370
|
+
isReadOnly() {
|
|
1371
|
+
return this._readOnly || this._path == null;
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
;// ./src/model/Binding.js
|
|
1375
|
+
/* eslint-disable class-methods-use-this */
|
|
1376
|
+
|
|
1377
|
+
let Binding_counter = 0;
|
|
1378
|
+
|
|
1379
|
+
/**
|
|
1380
|
+
* A binding is a pairing between an item and various RDF statement
|
|
1381
|
+
* (a single statement unless the binding is a group with constraints).
|
|
1382
|
+
* It keeps track of cardinality and validity.
|
|
1383
|
+
* If a binding is valid and all parent bindings are valid,
|
|
1384
|
+
* the statement is asserted, that is, inserted into the RDF graph.
|
|
1385
|
+
*/
|
|
1386
|
+
class Binding {
|
|
1387
|
+
/**
|
|
1388
|
+
* @exports {Binding}
|
|
1389
|
+
* @class
|
|
1390
|
+
*/
|
|
1391
|
+
constructor({
|
|
1392
|
+
item,
|
|
1393
|
+
statement,
|
|
1394
|
+
graph,
|
|
1395
|
+
matchingCode
|
|
1396
|
+
}) {
|
|
1397
|
+
this._item = item;
|
|
1398
|
+
this._statement = statement;
|
|
1399
|
+
this._graph = graph;
|
|
1400
|
+
this._ancestorValid = true;
|
|
1401
|
+
this._cardinalityTracker = null;
|
|
1402
|
+
this._hash = `b_${Binding_counter}`;
|
|
1403
|
+
this._matchingCode = matchingCode || 'correct';
|
|
1404
|
+
Binding_counter += 1;
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
// ===================================================
|
|
1408
|
+
// Public API
|
|
1409
|
+
// ===================================================
|
|
1410
|
+
getGraph() {
|
|
1411
|
+
if (!this._graph) {
|
|
1412
|
+
if (this._statement) {
|
|
1413
|
+
this._graph = this._statement.getGraph();
|
|
1414
|
+
} else if (this._parent) {
|
|
1415
|
+
this._graph = this._parent.getGraph();
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
return this._graph;
|
|
1419
|
+
}
|
|
1420
|
+
isReadOnly() {
|
|
1421
|
+
if (!('_readOnly' in this)) {
|
|
1422
|
+
const parent = this.getParent();
|
|
1423
|
+
if (parent && parent._readOnly) {
|
|
1424
|
+
this._readyOnly = true;
|
|
1425
|
+
} else {
|
|
1426
|
+
this._readOnly = !!(this._statement && this._statement.getNamedGraph());
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
return this._readOnly;
|
|
1430
|
+
}
|
|
1431
|
+
remove() {}
|
|
1432
|
+
setSubject(uri) {}
|
|
1433
|
+
getValue() {}
|
|
1434
|
+
setValue(value, silent) {}
|
|
1435
|
+
getGist() {
|
|
1436
|
+
return utils.extractGist(this.getValue(), this.getItem().getValueTemplate());
|
|
1437
|
+
}
|
|
1438
|
+
setGist(value, silent) {
|
|
1439
|
+
let _value = value;
|
|
1440
|
+
let vt = this.getItem().getValueTemplate();
|
|
1441
|
+
if (vt && _value.length > 0) {
|
|
1442
|
+
if (vt.indexOf('$1') === -1) {
|
|
1443
|
+
vt += '$1';
|
|
1444
|
+
}
|
|
1445
|
+
_value = vt.replace('$1', _value);
|
|
1446
|
+
}
|
|
1447
|
+
this.setValue(_value, silent);
|
|
1448
|
+
}
|
|
1449
|
+
getCardinalityTracker() {
|
|
1450
|
+
return this._cardinalityTracker;
|
|
1451
|
+
}
|
|
1452
|
+
setCardinalityTracker(cardTracker) {
|
|
1453
|
+
this._cardinalityTracker = cardTracker;
|
|
1454
|
+
}
|
|
1455
|
+
getItem() {
|
|
1456
|
+
return this._item;
|
|
1457
|
+
}
|
|
1458
|
+
getStatement() {
|
|
1459
|
+
return this._statement;
|
|
1460
|
+
}
|
|
1461
|
+
getParent() {
|
|
1462
|
+
return this._parent;
|
|
1463
|
+
}
|
|
1464
|
+
setParent(parent) {
|
|
1465
|
+
this._parent = parent;
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
/**
|
|
1469
|
+
* A binding is valid if:
|
|
1470
|
+
* <ol><li> it is a leaf and the corresponding statement is valid, </li>
|
|
1471
|
+
* <li>if it is a group and at least one of its children is valid, or</li>
|
|
1472
|
+
* <li>if it is a predicategroup and both the predicate and the object binding are valid.</ol>
|
|
1473
|
+
*/
|
|
1474
|
+
isValid() {}
|
|
1475
|
+
getMatchingCode() {
|
|
1476
|
+
return this._matchingCode;
|
|
1477
|
+
}
|
|
1478
|
+
setMatchingCode(matchingCode) {
|
|
1479
|
+
this._matchingCode = matchingCode;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
/**
|
|
1483
|
+
* stores the validity of ancestors.
|
|
1484
|
+
*/
|
|
1485
|
+
setAncestorValid(valid) {
|
|
1486
|
+
this._ancestorValid = valid;
|
|
1487
|
+
this.updateAssertions();
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
/**
|
|
1491
|
+
*
|
|
1492
|
+
*/
|
|
1493
|
+
updateAssertions() {}
|
|
1494
|
+
getHash() {
|
|
1495
|
+
return this._hash;
|
|
1496
|
+
}
|
|
1497
|
+
addListener(listener) {
|
|
1498
|
+
if (!this._listeners) {
|
|
1499
|
+
this._listeners = [];
|
|
1500
|
+
}
|
|
1501
|
+
this._listeners.push(listener);
|
|
1502
|
+
}
|
|
1503
|
+
removeListener(listener) {
|
|
1504
|
+
if (this._listeners) {
|
|
1505
|
+
const idx = this._listeners.indexOf(listener);
|
|
1506
|
+
if (idx !== -1) {
|
|
1507
|
+
this._listeners.splice(idx, 1);
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
bindingChange(binding) {
|
|
1512
|
+
if (this._listeners) {
|
|
1513
|
+
for (let i = 0; i < this._listeners.length; i++) {
|
|
1514
|
+
this._listeners[i](binding);
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
if (this._parent) {
|
|
1518
|
+
this._parent.bindingChange(binding);
|
|
1519
|
+
}
|
|
1520
|
+
if (this._cardinalityTracker) {
|
|
1521
|
+
this._cardinalityTracker.touch();
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
// ===================================================
|
|
1526
|
+
// Private methods
|
|
1527
|
+
// ===================================================
|
|
1528
|
+
_isValidObjectValue(value) {
|
|
1529
|
+
let _value = value;
|
|
1530
|
+
if (typeof _value !== 'string' && _value !== null) {
|
|
1531
|
+
throw new Error('In a binding every object value need to be a string!');
|
|
1532
|
+
}
|
|
1533
|
+
const pattern = this._item.getPattern();
|
|
1534
|
+
if (pattern) {
|
|
1535
|
+
_value = utils.extractGist(_value, this.getItem().getValueTemplate());
|
|
1536
|
+
return _value !== undefined && _value !== null && _value !== '' && new RegExp(`^${pattern}$`).test(_value);
|
|
1537
|
+
}
|
|
1538
|
+
return _value !== undefined && _value !== null && _value !== '';
|
|
1539
|
+
}
|
|
1540
|
+
_isValidPredicateValue(value) {
|
|
1541
|
+
if (typeof value !== 'string' && value !== null) {
|
|
1542
|
+
throw new Error('In a binding every predicate need to be a string!');
|
|
1543
|
+
}
|
|
1544
|
+
return value !== undefined && value !== null && value !== '';
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
;// ./src/model/ValueBinding.js
|
|
1548
|
+
|
|
1549
|
+
|
|
1550
|
+
|
|
1551
|
+
class ValueBinding extends Binding {
|
|
1552
|
+
/**
|
|
1553
|
+
* Corresponds to a binding for a Text item type, captures literals, literals with
|
|
1554
|
+
* language, datatyped literals, or non blank resources, that is, URI's.
|
|
1555
|
+
* Validity is determined by a valid predicate and object.
|
|
1556
|
+
* The statement is asserted when the parents are valid and this ValueBinding is valid.
|
|
1557
|
+
* @exports {rdforms/model/ValueBinding}
|
|
1558
|
+
* @class
|
|
1559
|
+
* @see rforms/template/Text
|
|
1560
|
+
*/
|
|
1561
|
+
constructor(params) {
|
|
1562
|
+
super(params);
|
|
1563
|
+
this._validObject = true;
|
|
1564
|
+
this._validPredicate = true;
|
|
1565
|
+
this._excludeFromTreeValidityCheck = false;
|
|
1566
|
+
if (this._statement) {
|
|
1567
|
+
this._validPredicate = this._isValidPredicateValue(this._statement.getPredicate(), true);
|
|
1568
|
+
this._validObject = this._isValidObjectValue(this._statement.getValue());
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
/**
|
|
1573
|
+
* @return {String} corresponding to the value, even if the nodetype is URI
|
|
1574
|
+
* or datatype says for example date.
|
|
1575
|
+
*/
|
|
1576
|
+
getValue() {
|
|
1577
|
+
return this._statement.getValue();
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
/**
|
|
1581
|
+
* @param {String} value the value to set, the value will be trimmed and if the value is empty
|
|
1582
|
+
* (like the empty string or null) the statement will be unasserted.
|
|
1583
|
+
* @param {Boolean} silent if true the graph will not be marked as changed,
|
|
1584
|
+
* useful when initializing with default empty values.
|
|
1585
|
+
*/
|
|
1586
|
+
setValue(value, silent) {
|
|
1587
|
+
const _value = typeof value === 'string' ? value.trim() : value;
|
|
1588
|
+
const oValidObject = this._validObject;
|
|
1589
|
+
if (this._isValidObjectValue(_value)) {
|
|
1590
|
+
this._statement.setValue(_value, silent);
|
|
1591
|
+
this._validObject = true;
|
|
1592
|
+
if (oValidObject !== true && this._validPredicate === true && !this._excludeFromTreeValidityCheck) {
|
|
1593
|
+
this._parent.oneChildValidityChanged(true);
|
|
1594
|
+
}
|
|
1595
|
+
} else {
|
|
1596
|
+
// If it is a null value, change the statement.
|
|
1597
|
+
if (_value === '' || _value === null) {
|
|
1598
|
+
this._statement.setValue('', silent);
|
|
1599
|
+
}
|
|
1600
|
+
// And unassert the statement.
|
|
1601
|
+
this._validObject = false;
|
|
1602
|
+
if (oValidObject !== false && this._validPredicate === true) {
|
|
1603
|
+
this._parent.oneChildValidityChanged(false);
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
this.updateAssertions();
|
|
1607
|
+
}
|
|
1608
|
+
setSubject(uri) {
|
|
1609
|
+
this._statement.setSubject(uri);
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
/**
|
|
1613
|
+
* @return {String} corresponding to a uri.
|
|
1614
|
+
*/
|
|
1615
|
+
getPredicate() {
|
|
1616
|
+
return this._statement.getPredicate();
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
/**
|
|
1620
|
+
* @param {String} predicate corresponding to a uri.
|
|
1621
|
+
*/
|
|
1622
|
+
setPredicate(predicate) {
|
|
1623
|
+
const oValidPredicate = this._validPredicate;
|
|
1624
|
+
if (this._isValidPredicateValue(predicate)) {
|
|
1625
|
+
this._statement.setPredicate(predicate);
|
|
1626
|
+
this._validPredicate = true;
|
|
1627
|
+
if (oValidPredicate !== true && this._validObject === true) {
|
|
1628
|
+
this._parent.oneChildValidityChanged(true);
|
|
1629
|
+
}
|
|
1630
|
+
} else {
|
|
1631
|
+
// Note that we actually do not set the invalid value, just unassert the statement.
|
|
1632
|
+
this._validPredicate = false;
|
|
1633
|
+
if (oValidPredicate !== false && this._validObject === true) {
|
|
1634
|
+
this._parent.oneChildValidityChanged(false);
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
this.updateAssertions();
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
/**
|
|
1641
|
+
* @return {String} a two or three character language code.
|
|
1642
|
+
*/
|
|
1643
|
+
getLanguage() {
|
|
1644
|
+
return this._statement.getLanguage();
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
/**
|
|
1648
|
+
* @param {Object} lang a two or three character language code.
|
|
1649
|
+
*/
|
|
1650
|
+
setLanguage(lang, silent) {
|
|
1651
|
+
this._statement.setLanguage(lang, silent);
|
|
1652
|
+
if (!silent) {
|
|
1653
|
+
this.bindingChange(this);
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
/**
|
|
1658
|
+
* @return {String} corresponding to a uri.
|
|
1659
|
+
*/
|
|
1660
|
+
getDatatype() {
|
|
1661
|
+
return this._statement.getDatatype();
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
/**
|
|
1665
|
+
* @param {String} dt corresponding to a uri.
|
|
1666
|
+
*/
|
|
1667
|
+
setDatatype(dt) {
|
|
1668
|
+
this._statement.setDatatype(rdfjson_namespaceObject.namespaces.expand(dt));
|
|
1669
|
+
this.updateAssertions();
|
|
1670
|
+
}
|
|
1671
|
+
setExcludeFromTreeValidityCheck(value) {
|
|
1672
|
+
this._excludeFromTreeValidityCheck = value;
|
|
1673
|
+
}
|
|
1674
|
+
remove() {
|
|
1675
|
+
this.setValue(null);
|
|
1676
|
+
this._parent.removeChildBinding(this);
|
|
1677
|
+
super.remove(arguments);
|
|
1678
|
+
}
|
|
1679
|
+
updateAssertions() {
|
|
1680
|
+
const assert = this._ancestorValid && this._validObject && this._validPredicate;
|
|
1681
|
+
this._statement.setAsserted(assert, true);
|
|
1682
|
+
this.bindingChange(this);
|
|
1683
|
+
}
|
|
1684
|
+
isValid() {
|
|
1685
|
+
return this._validObject && this._validPredicate && !this._excludeFromTreeValidityCheck;
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
;// ./src/model/CODES.js
|
|
1689
|
+
/* harmony default export */ const model_CODES = ({
|
|
1690
|
+
UNKNOWN: 'unknown',
|
|
1691
|
+
// Used as a marker initially, to not mark everything red initially
|
|
1692
|
+
OK: 'correct',
|
|
1693
|
+
TOO_FEW_VALUES: 'few',
|
|
1694
|
+
// deprecated
|
|
1695
|
+
TOO_FEW_VALUES_MIN: 'min',
|
|
1696
|
+
TOO_FEW_VALUES_PREF: 'pref',
|
|
1697
|
+
TOO_MANY_VALUES: 'many',
|
|
1698
|
+
AT_MOST_ONE_CHILD: 'atmostonechild',
|
|
1699
|
+
AT_LEAST_ONE_CHILD: 'atleastonechild',
|
|
1700
|
+
EXACTLY_ONE_CHILD: 'exactlyonechild',
|
|
1701
|
+
WRONG_VALUE: 'value',
|
|
1702
|
+
WRONG_PATTERN: 'pattern',
|
|
1703
|
+
WRONG_NODETYPE: 'nodetype',
|
|
1704
|
+
WRONG_DATATYPE: 'datatype',
|
|
1705
|
+
MISSING_LANGUAGE: 'language',
|
|
1706
|
+
MISSING_CONSTRAINTS: 'constraints'
|
|
1707
|
+
});
|
|
1708
|
+
;// ./src/model/ChoiceBinding.js
|
|
1709
|
+
|
|
1710
|
+
|
|
1711
|
+
const label = 'http://www.w3.org/2000/01/rdf-schema#label';
|
|
1712
|
+
const seeAlso = 'http://www.w3.org/2000/01/rdf-schema#seeAlso';
|
|
1713
|
+
|
|
1714
|
+
/**
|
|
1715
|
+
* A ValueBinding that only accepts uris from a controlled vocabulary encoded as choices.
|
|
1716
|
+
* @see rforms.template.Choice#getChoices
|
|
1717
|
+
*/
|
|
1718
|
+
class ChoiceBinding extends ValueBinding {
|
|
1719
|
+
constructor({
|
|
1720
|
+
choice,
|
|
1721
|
+
item,
|
|
1722
|
+
statement,
|
|
1723
|
+
matchingCode
|
|
1724
|
+
}) {
|
|
1725
|
+
super({
|
|
1726
|
+
choice,
|
|
1727
|
+
item,
|
|
1728
|
+
statement,
|
|
1729
|
+
matchingCode
|
|
1730
|
+
});
|
|
1731
|
+
this._choice = choice;
|
|
1732
|
+
this._validPredicate = item.getProperty() != null;
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
// Static property on class ChoiceBinding via static getter,
|
|
1736
|
+
static get seeAlso() {
|
|
1737
|
+
return seeAlso;
|
|
1738
|
+
}
|
|
1739
|
+
setChoice(choice, silent) {
|
|
1740
|
+
this._choice = choice;
|
|
1741
|
+
if (choice == null) {
|
|
1742
|
+
this.setValue(null, null, silent);
|
|
1743
|
+
} else if (this.getValue() !== choice.value) {
|
|
1744
|
+
this.setValue(choice.value, choice, silent);
|
|
1745
|
+
}
|
|
1746
|
+
if (choice && choice.mismatch) {
|
|
1747
|
+
this.setMatchingCode(model_CODES.WRONG_VALUE);
|
|
1748
|
+
} else if ((!choice || !choice.mismatch) && this.getMatchingCode() === model_CODES.WRONG_VALUE) {
|
|
1749
|
+
this.setMatchingCode(model_CODES.OK);
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
getChoice() {
|
|
1753
|
+
return this._choice;
|
|
1754
|
+
}
|
|
1755
|
+
setValue(value, choice, silent) {
|
|
1756
|
+
const oldval = this.getValue();
|
|
1757
|
+
super.setValue(value, choice, silent);
|
|
1758
|
+
const graph = this._statement.getGraph();
|
|
1759
|
+
graph.findAndRemove(oldval, label, undefined, silent);
|
|
1760
|
+
graph.findAndRemove(oldval, seeAlso, undefined, silent);
|
|
1761
|
+
if (value != null && choice != null) {
|
|
1762
|
+
if (choice.seeAlso && choice.inlineSeeAlso) {
|
|
1763
|
+
graph.create(value, seeAlso, choice.seeAlso, true, silent);
|
|
1764
|
+
}
|
|
1765
|
+
if (choice.inlineLabel === true) {
|
|
1766
|
+
const labelMap = choice.label || {};
|
|
1767
|
+
Object.keys(labelMap).forEach(lang => graph.create(value, label, {
|
|
1768
|
+
value: labelMap[lang],
|
|
1769
|
+
lang,
|
|
1770
|
+
type: 'literal'
|
|
1771
|
+
}, true, silent));
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
;// ./src/model/CardinalityTracker.js
|
|
1777
|
+
|
|
1778
|
+
class CardinalityTracker {
|
|
1779
|
+
/**
|
|
1780
|
+
* A counter paired with a item with cardinality restrictions.
|
|
1781
|
+
* To update the counter use the increment and decrement methods.
|
|
1782
|
+
* If the counter passes a cardinality restriction the
|
|
1783
|
+
* corresponding hook is called, that is, maxReached, minReached,
|
|
1784
|
+
* and justFine when the counter moved within the acceptable
|
|
1785
|
+
* cardinality restrictions.
|
|
1786
|
+
*/
|
|
1787
|
+
constructor(item) {
|
|
1788
|
+
this._listener = [];
|
|
1789
|
+
this._limits = item.getCardinality() || {};
|
|
1790
|
+
this._counter = 0;
|
|
1791
|
+
this._depsOk = true;
|
|
1792
|
+
this._listeners = [];
|
|
1793
|
+
this._code = 0;
|
|
1794
|
+
}
|
|
1795
|
+
addListener(listener) {
|
|
1796
|
+
this._listeners.push(listener);
|
|
1797
|
+
return listener;
|
|
1798
|
+
}
|
|
1799
|
+
removeListener(listener) {
|
|
1800
|
+
this._listeners.splice(this._listeners.indexOf(listener), 1);
|
|
1801
|
+
}
|
|
1802
|
+
cardinalityChanged() {
|
|
1803
|
+
this._listeners.forEach(listener => listener());
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
// ===================================================
|
|
1807
|
+
// Public API
|
|
1808
|
+
// ===================================================
|
|
1809
|
+
getCardinality() {
|
|
1810
|
+
return this._counter;
|
|
1811
|
+
}
|
|
1812
|
+
isMax() {
|
|
1813
|
+
return this._limits.max != null && this._counter >= this._limits.max;
|
|
1814
|
+
}
|
|
1815
|
+
isMin() {
|
|
1816
|
+
return this._limits.min != null && this._counter <= this._limits.min;
|
|
1817
|
+
}
|
|
1818
|
+
isFine() {
|
|
1819
|
+
return this._fine;
|
|
1820
|
+
}
|
|
1821
|
+
isDepsOk() {
|
|
1822
|
+
return this._depsOk;
|
|
1823
|
+
}
|
|
1824
|
+
getCode() {
|
|
1825
|
+
return this._code;
|
|
1826
|
+
}
|
|
1827
|
+
setCode(code) {
|
|
1828
|
+
if (this._code !== code) {
|
|
1829
|
+
this._code = code;
|
|
1830
|
+
this.cardinalityChanged();
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
setDepsOk(ok) {
|
|
1834
|
+
if (this._depsOk !== ok) {
|
|
1835
|
+
this._code = model_CODES.UNKNOWN;
|
|
1836
|
+
this._depsOk = ok;
|
|
1837
|
+
this.cardinalityChanged();
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
increment() {
|
|
1841
|
+
this._counter += 1;
|
|
1842
|
+
this._code = model_CODES.UNKNOWN;
|
|
1843
|
+
this._checkCounter();
|
|
1844
|
+
}
|
|
1845
|
+
decrement() {
|
|
1846
|
+
this._counter -= 1;
|
|
1847
|
+
this._code = model_CODES.UNKNOWN;
|
|
1848
|
+
this._checkCounter();
|
|
1849
|
+
}
|
|
1850
|
+
getCounter() {
|
|
1851
|
+
return this._counter;
|
|
1852
|
+
}
|
|
1853
|
+
checkCardinality() {
|
|
1854
|
+
this._checkCounter();
|
|
1855
|
+
}
|
|
1856
|
+
touch() {
|
|
1857
|
+
this._code = model_CODES.UNKNOWN;
|
|
1858
|
+
this.cardinalityChanged();
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
// ===================================================
|
|
1862
|
+
// Private methods
|
|
1863
|
+
// ===================================================
|
|
1864
|
+
_checkCounter() {
|
|
1865
|
+
if (this._limits.max != null && this._counter === this._limits.max) {
|
|
1866
|
+
this._fine = true;
|
|
1867
|
+
} else if (this._limits.max != null && this._counter > this._limits.max) {
|
|
1868
|
+
this._fine = false;
|
|
1869
|
+
} else if (this._limits.min != null && this._counter === this._limits.min) {
|
|
1870
|
+
this._fine = true;
|
|
1871
|
+
} else if (this._limits.min != null && this._counter < this._limits.min) {
|
|
1872
|
+
this._fine = false;
|
|
1873
|
+
}
|
|
1874
|
+
this.cardinalityChanged();
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
;// ./src/model/GroupBinding.js
|
|
1878
|
+
|
|
1879
|
+
|
|
1880
|
+
class GroupBinding extends Binding {
|
|
1881
|
+
/**
|
|
1882
|
+
* Corresponds to a binding for a Group item type.
|
|
1883
|
+
* Handles sub-bindings grouped by item.
|
|
1884
|
+
* Validity of a group is determined by at least one of the sub-bindings being valid.
|
|
1885
|
+
* All sub-bindings must notify when their validity changed so that the validity of
|
|
1886
|
+
* the parent group can be checked and possibly updated.
|
|
1887
|
+
* Potential statement and constraints are asserted when both parents are valid and this GroupBinding is valid.
|
|
1888
|
+
*
|
|
1889
|
+
* @see template/Group
|
|
1890
|
+
*/
|
|
1891
|
+
constructor(params) {
|
|
1892
|
+
super(params);
|
|
1893
|
+
const {
|
|
1894
|
+
constraints = [],
|
|
1895
|
+
childrenRootUri = null
|
|
1896
|
+
} = params;
|
|
1897
|
+
this._constraints = constraints;
|
|
1898
|
+
// Generates an array of arrays, one array for each child item.
|
|
1899
|
+
|
|
1900
|
+
this._childBindings = this._item.getChildren().map(() => []);
|
|
1901
|
+
this._rootUri = childrenRootUri;
|
|
1902
|
+
this._validPredicate = true;
|
|
1903
|
+
this._validObject = true;
|
|
1904
|
+
if (this._statement) {
|
|
1905
|
+
this._validPredicate = this._isValidPredicateValue(this._statement.getPredicate());
|
|
1906
|
+
this._validObject = this._isValidObjectValue(this._statement.getValue(), this._statement.getType());
|
|
1907
|
+
}
|
|
1908
|
+
this._cachedChildBindings = null;
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
// ===================================================
|
|
1912
|
+
// Public API
|
|
1913
|
+
// ===================================================
|
|
1914
|
+
|
|
1915
|
+
getValue() {
|
|
1916
|
+
return this.getChildrenRootUri();
|
|
1917
|
+
}
|
|
1918
|
+
oneChildValidityChanged(valid) {
|
|
1919
|
+
if (valid === this._oneValidChild) {
|
|
1920
|
+
return false; // No change
|
|
1921
|
+
}
|
|
1922
|
+
if (!valid) {
|
|
1923
|
+
// Since we do not keep track of which children have valid values,
|
|
1924
|
+
// we need to iterate through all children to check if some other child has a valid value
|
|
1925
|
+
// (and check if valid predicate) if so, abort change.
|
|
1926
|
+
delete this._oneValidChild;
|
|
1927
|
+
if (this.isValid()) {
|
|
1928
|
+
return false; // No change
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
// Below we change the valid state of this group.
|
|
1933
|
+
this._oneValidChild = valid;
|
|
1934
|
+
// If there is no parent or it's valid state did not change,
|
|
1935
|
+
// then the groups assertions has not been changed and the children
|
|
1936
|
+
// have not been notified of this group change in validity.
|
|
1937
|
+
this._notifyValidityChange(valid && this._validPredicate && this._validObject);
|
|
1938
|
+
return this._validPredicate && this._validObject; // Validity of group changed only if valid predicate and object.
|
|
1939
|
+
}
|
|
1940
|
+
setSubject(uri) {
|
|
1941
|
+
if (this._statement) {
|
|
1942
|
+
this._statement.setSubject(uri);
|
|
1943
|
+
} else {
|
|
1944
|
+
this.getChildBindings().forEach(cb => cb.setSubject(uri));
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
getChildrenRootUri() {
|
|
1948
|
+
if (this._statement) {
|
|
1949
|
+
// Either the object of the statement.
|
|
1950
|
+
return this._statement.getValue();
|
|
1951
|
+
} else if (this._rootUri != null) {
|
|
1952
|
+
return this._rootUri;
|
|
1953
|
+
} else if (this._parent != null) {
|
|
1954
|
+
return this._parent.getChildrenRootUri();
|
|
1955
|
+
}
|
|
1956
|
+
return undefined;
|
|
1957
|
+
}
|
|
1958
|
+
addChildBinding(binding) {
|
|
1959
|
+
this.addChildBindings([binding]);
|
|
1960
|
+
}
|
|
1961
|
+
addChildBindings(bindings) {
|
|
1962
|
+
this._cachedChildBindings = null;
|
|
1963
|
+
if (!Array.isArray(bindings) || bindings.length === 0) {
|
|
1964
|
+
return;
|
|
1965
|
+
}
|
|
1966
|
+
const item = bindings[0].getItem();
|
|
1967
|
+
const children = this._item.getChildren();
|
|
1968
|
+
for (let i = children.length; i >= 0; i--) {
|
|
1969
|
+
if (item === children[i]) {
|
|
1970
|
+
const cardTracker = this._childBindings[i].length > 0 ? this._childBindings[i][0].getCardinalityTracker() : new CardinalityTracker(item);
|
|
1971
|
+
this._childBindings[i] = this._childBindings[i].concat(bindings);
|
|
1972
|
+
bindings.forEach(binding => {
|
|
1973
|
+
binding.setParent(this);
|
|
1974
|
+
binding.setCardinalityTracker(cardTracker);
|
|
1975
|
+
cardTracker.increment();
|
|
1976
|
+
}, this);
|
|
1977
|
+
break;
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
removeChildBinding(binding) {
|
|
1982
|
+
this._cachedChildBindings = null;
|
|
1983
|
+
const children = this._item.getChildren();
|
|
1984
|
+
for (let i = children.length; i >= 0; i--) {
|
|
1985
|
+
if (binding.getItem() === children[i]) {
|
|
1986
|
+
this._childBindings[i].splice(this._childBindings[i].indexOf(binding), 1);
|
|
1987
|
+
delete binding._parent;
|
|
1988
|
+
binding.getCardinalityTracker().decrement();
|
|
1989
|
+
break;
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
this.oneChildValidityChanged();
|
|
1993
|
+
this.bindingChange(binding);
|
|
1994
|
+
}
|
|
1995
|
+
getChildBindingsFor(item) {
|
|
1996
|
+
const children = this._item.getChildren();
|
|
1997
|
+
for (let i = children.length; i >= 0; i--) {
|
|
1998
|
+
if (item === children[i]) {
|
|
1999
|
+
return this._childBindings[i];
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
return undefined;
|
|
2003
|
+
}
|
|
2004
|
+
getItemGroupedChildBindings() {
|
|
2005
|
+
return this._childBindings;
|
|
2006
|
+
}
|
|
2007
|
+
getChildBindings() {
|
|
2008
|
+
if (this._cachedChildBindings == null) {
|
|
2009
|
+
if (this._childBindings && this._childBindings.length > 0) {
|
|
2010
|
+
this._cachedChildBindings = [].concat(...this._childBindings);
|
|
2011
|
+
} else {
|
|
2012
|
+
this._cachedChildBindings = [];
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
return this._cachedChildBindings;
|
|
2016
|
+
}
|
|
2017
|
+
setPredicate(predicate) {
|
|
2018
|
+
const oValidPredicate = this._validPredicate;
|
|
2019
|
+
if (this._isValidPredicateValue(predicate)) {
|
|
2020
|
+
this._statement.setPredicate(predicate);
|
|
2021
|
+
this._validPredicate = true;
|
|
2022
|
+
if (oValidPredicate !== true && this._validObject && (this.getItem().getNodetype() === 'URI' ? true : this._oneValidChild)) {
|
|
2023
|
+
this._notifyValidityChange(true);
|
|
2024
|
+
}
|
|
2025
|
+
} else {
|
|
2026
|
+
// Note that we actually do not set the invalid value, just unassert the statement.
|
|
2027
|
+
this._validPredicate = false;
|
|
2028
|
+
if (oValidPredicate !== false && this._validObject && (this.getItem().getNodetype() === 'URI' ? true : this._oneValidChild)) {
|
|
2029
|
+
this._notifyValidityChange(false);
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
this.updateAssertions();
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
// eslint-disable-next-line no-dupe-class-members
|
|
2036
|
+
getValue() {
|
|
2037
|
+
if (this._statement && this._statement.isObjectBlank()) {
|
|
2038
|
+
return '';
|
|
2039
|
+
}
|
|
2040
|
+
return this._statement && this._statement.getValue();
|
|
2041
|
+
}
|
|
2042
|
+
setValue(value) {
|
|
2043
|
+
if (this.getItem().getNodetype() !== 'URI' || !this._statement) {
|
|
2044
|
+
throw new Error('Cannot change the value of a group unless its nodetype is URI and' + ' also that it corresponds to a statement');
|
|
2045
|
+
}
|
|
2046
|
+
const oldIsValidObject = this._validObject;
|
|
2047
|
+
const isValidObject = this._isValidObjectValue(value, 'uri');
|
|
2048
|
+
if (isValidObject) {
|
|
2049
|
+
this._validObject = true;
|
|
2050
|
+
this._statement._o.type = 'uri';
|
|
2051
|
+
this._statement.setValue(value);
|
|
2052
|
+
this._constraints.forEach(stmt => stmt.setSubject(value));
|
|
2053
|
+
this.getChildBindings().forEach(cb => cb.setSubject(value));
|
|
2054
|
+
if (!oldIsValidObject && this._validPredicate) {
|
|
2055
|
+
this._notifyValidityChange(true);
|
|
2056
|
+
}
|
|
2057
|
+
} else {
|
|
2058
|
+
this._validObject = false;
|
|
2059
|
+
if (oldIsValidObject && this._validPredicate) {
|
|
2060
|
+
this._notifyValidityChange(false);
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
getPredicate() {
|
|
2065
|
+
return this._statement ? this._statement.getPredicate() : undefined;
|
|
2066
|
+
}
|
|
2067
|
+
remove() {
|
|
2068
|
+
this._oneValidChild = false;
|
|
2069
|
+
this.setAncestorValid(false);
|
|
2070
|
+
if (this._parent != null) {
|
|
2071
|
+
this._parent.removeChildBinding(this);
|
|
2072
|
+
}
|
|
2073
|
+
super.remove(arguments);
|
|
2074
|
+
}
|
|
2075
|
+
_isValid() {
|
|
2076
|
+
if (this.getItem().getNodetype() === 'URI') {
|
|
2077
|
+
return this._validObject && this._validPredicate;
|
|
2078
|
+
}
|
|
2079
|
+
return this._oneValidChild && this._validPredicate && this._validObject;
|
|
2080
|
+
}
|
|
2081
|
+
isValid() {
|
|
2082
|
+
if (this._oneValidChild == null) {
|
|
2083
|
+
this._oneValidChild = this._forceOneValidChildCheck();
|
|
2084
|
+
}
|
|
2085
|
+
return this._isValid();
|
|
2086
|
+
}
|
|
2087
|
+
setAncestorValid(valid) {
|
|
2088
|
+
this._ancestorValid = valid;
|
|
2089
|
+
this.updateAssertions();
|
|
2090
|
+
this._childBindings.forEach(bindingArr => bindingArr.forEach(binding => binding.setAncestorValid(valid && this._isValid()), this), this);
|
|
2091
|
+
}
|
|
2092
|
+
updateAssertions() {
|
|
2093
|
+
if (this._oneValidChild == null) {
|
|
2094
|
+
this.isValid();
|
|
2095
|
+
}
|
|
2096
|
+
const assert = this._ancestorValid && this._isValid();
|
|
2097
|
+
if (this._statement != null) {
|
|
2098
|
+
this._statement.setAsserted(assert);
|
|
2099
|
+
}
|
|
2100
|
+
this._constraints.forEach(constraintStmt => constraintStmt.setAsserted(assert));
|
|
2101
|
+
this.getChildBindings().forEach(binding => binding.updateAssertions());
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
// ===================================================
|
|
2105
|
+
// Private methods
|
|
2106
|
+
// ===================================================
|
|
2107
|
+
_forceOneValidChildCheck() {
|
|
2108
|
+
return this._childBindings.some(arr => arr.some(binding => binding.isValid()));
|
|
2109
|
+
}
|
|
2110
|
+
_notifyValidityChange(newValidity) {
|
|
2111
|
+
if (!this._parent || !this._parent.oneChildValidityChanged(newValidity)) {
|
|
2112
|
+
// We can reuse the setAncestorValid method by setting the current value.
|
|
2113
|
+
this.updateAssertions();
|
|
2114
|
+
this._childBindings.forEach(bindingArr => bindingArr.forEach(binding => binding.setAncestorValid(newValidity)));
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
_isValidObjectValue(value, type) {
|
|
2118
|
+
if (this.getItem().getNodetype() === 'URI' && type !== 'uri') {
|
|
2119
|
+
return false;
|
|
2120
|
+
}
|
|
2121
|
+
return super._isValidObjectValue(value);
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
;// ./src/model/PropertyChoiceBinding.js
|
|
2125
|
+
/* eslint-disable class-methods-use-this */
|
|
2126
|
+
|
|
2127
|
+
class PropertyChoiceBinding extends ChoiceBinding {
|
|
2128
|
+
/**
|
|
2129
|
+
* Are only used as the first child of a PropertyGroupBinding to capture
|
|
2130
|
+
* a variable predicate.
|
|
2131
|
+
*/
|
|
2132
|
+
constructor(params) {
|
|
2133
|
+
super(params);
|
|
2134
|
+
this._objectBinding = params.objectBinding;
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
/**
|
|
2138
|
+
* Remove shold not be doubled, hence it is handled by the objectBinding
|
|
2139
|
+
* delegated from the parent PropertyGroupBinding.
|
|
2140
|
+
*/
|
|
2141
|
+
remove() {}
|
|
2142
|
+
|
|
2143
|
+
/**
|
|
2144
|
+
* The object binding handles the RDF statement, hence go through it to set
|
|
2145
|
+
* the predicate, note not the setValue function because that sets the object
|
|
2146
|
+
* rather than the predicate.
|
|
2147
|
+
* @param {Object} value
|
|
2148
|
+
*/
|
|
2149
|
+
setValue(value) {
|
|
2150
|
+
this._objectBinding.setPredicate(value);
|
|
2151
|
+
}
|
|
2152
|
+
getValue() {
|
|
2153
|
+
return this._objectBinding.getPredicate();
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
/**
|
|
2157
|
+
* Validity is controlled via the objectBinding, when it is valid the
|
|
2158
|
+
* parent PropertyGroupBinding will be valid since only one child is
|
|
2159
|
+
* enough to enable validity. Hence, this PropertyChoiceBinding can
|
|
2160
|
+
* be false all the time since it has no children and does not affect
|
|
2161
|
+
* the above hierarchy (and does not control assertment of statements).
|
|
2162
|
+
*/
|
|
2163
|
+
isValid() {
|
|
2164
|
+
return false;
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
/**
|
|
2168
|
+
* Does nothing, similar reason as for isValid.
|
|
2169
|
+
*/
|
|
2170
|
+
updateAssertions() {}
|
|
2171
|
+
}
|
|
2172
|
+
;// ./src/model/PropertyGroupBinding.js
|
|
2173
|
+
|
|
2174
|
+
|
|
2175
|
+
|
|
2176
|
+
|
|
2177
|
+
|
|
2178
|
+
|
|
2179
|
+
class PropertyGroupBinding extends GroupBinding {
|
|
2180
|
+
/**
|
|
2181
|
+
* This is a syntactical grouping to capture the case when both the predicate and
|
|
2182
|
+
* the object is variable. It is achieved by having exactly two children, the first being
|
|
2183
|
+
* a binding for the predicate in the form of a PredicateChoiceBinding and the second
|
|
2184
|
+
* being a binding for the object. The object binding can be a ValueBinding, ChoiceBinding or
|
|
2185
|
+
* a GroupBinding. It is not allowed to be another PropertyGroupBinding though.
|
|
2186
|
+
*
|
|
2187
|
+
* @see rforms.template.PropertyGroup
|
|
2188
|
+
*/
|
|
2189
|
+
constructor(params) {
|
|
2190
|
+
super(params);
|
|
2191
|
+
const {
|
|
2192
|
+
statement,
|
|
2193
|
+
constraints
|
|
2194
|
+
} = params;
|
|
2195
|
+
this._statement = undefined;
|
|
2196
|
+
this._validPredicate = true; // Reset to initial value to ignore check from incorrect given statement in this case.
|
|
2197
|
+
this._constraints = [];
|
|
2198
|
+
const children = this._item.getChildren();
|
|
2199
|
+
const item = children[1];
|
|
2200
|
+
let oBinding;
|
|
2201
|
+
if (item instanceof Group) {
|
|
2202
|
+
oBinding = new GroupBinding({
|
|
2203
|
+
item,
|
|
2204
|
+
statement,
|
|
2205
|
+
constraints
|
|
2206
|
+
});
|
|
2207
|
+
} else if (item instanceof Choice) {
|
|
2208
|
+
oBinding = new ChoiceBinding({
|
|
2209
|
+
item,
|
|
2210
|
+
statement
|
|
2211
|
+
});
|
|
2212
|
+
} else {
|
|
2213
|
+
oBinding = new ValueBinding({
|
|
2214
|
+
item,
|
|
2215
|
+
statement
|
|
2216
|
+
});
|
|
2217
|
+
}
|
|
2218
|
+
const pBinding = new PropertyChoiceBinding({
|
|
2219
|
+
item: children[0],
|
|
2220
|
+
objectBinding: oBinding
|
|
2221
|
+
});
|
|
2222
|
+
this.addChildBinding(pBinding);
|
|
2223
|
+
this.addChildBinding(oBinding);
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
// ===================================================
|
|
2227
|
+
// Public API
|
|
2228
|
+
// ===================================================
|
|
2229
|
+
getPredicateBinding() {
|
|
2230
|
+
return this._childBindings[0][0];
|
|
2231
|
+
}
|
|
2232
|
+
getObjectBinding() {
|
|
2233
|
+
return this._childBindings[1][0];
|
|
2234
|
+
}
|
|
2235
|
+
getGraph() {
|
|
2236
|
+
return this.getObjectBinding().getGraph();
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
;// ./src/model/GroupURIBinding.js
|
|
2240
|
+
|
|
2241
|
+
|
|
2242
|
+
/**
|
|
2243
|
+
* A binding corresponding to a text item with no property, allowing editing the
|
|
2244
|
+
* object in the statement of the group above.
|
|
2245
|
+
* The purpose it to allow editing URIs inside of a hierarchy, e.g. consider the following graph:
|
|
2246
|
+
*
|
|
2247
|
+
* http://example.com rdfs:seeAlso http://slashdot.org .
|
|
2248
|
+
* http://slashdot.org rdfs:label "Slashdot" .
|
|
2249
|
+
*
|
|
2250
|
+
* Allowing to edit both the URI (slashdot above) and it's label requires a construction like the following:
|
|
2251
|
+
*
|
|
2252
|
+
* - GroupItem with property rdfs:seeAlso (GroupBinding)
|
|
2253
|
+
* - TextItem without property (GroupURIBinding)
|
|
2254
|
+
* - TextItem with property rdfs:label (ValueBinding)
|
|
2255
|
+
*
|
|
2256
|
+
* @exports {rdforms/model/GroupURIBinding}
|
|
2257
|
+
* @class
|
|
2258
|
+
* @see rdforms/template/Text
|
|
2259
|
+
*/
|
|
2260
|
+
class GroupURIBinding extends ValueBinding {
|
|
2261
|
+
/**
|
|
2262
|
+
* @return {String} corresponding to the value, even if the nodetype is URI
|
|
2263
|
+
* or datatype says for example date.
|
|
2264
|
+
*/
|
|
2265
|
+
getValue() {
|
|
2266
|
+
return this._parent.getValue();
|
|
2267
|
+
}
|
|
2268
|
+
setValue(value) {
|
|
2269
|
+
return this._parent.setValue(value);
|
|
2270
|
+
}
|
|
2271
|
+
getGist() {
|
|
2272
|
+
return this._parent.getGist();
|
|
2273
|
+
}
|
|
2274
|
+
setGist(value, silent) {
|
|
2275
|
+
return this._parent.setGist(value, silent);
|
|
2276
|
+
}
|
|
2277
|
+
setSubject(uri) {}
|
|
2278
|
+
|
|
2279
|
+
/**
|
|
2280
|
+
* @return {String} corresponding to a uri.
|
|
2281
|
+
*/
|
|
2282
|
+
getPredicate() {
|
|
2283
|
+
return this._parent.getPredicate();
|
|
2284
|
+
}
|
|
2285
|
+
remove() {
|
|
2286
|
+
this._parent.removeChildBinding(this);
|
|
2287
|
+
super.remove(arguments);
|
|
2288
|
+
}
|
|
2289
|
+
updateAssertions() {}
|
|
2290
|
+
isValid() {
|
|
2291
|
+
return true;
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
;// ./src/model/engine.js
|
|
2295
|
+
/* eslint-disable no-use-before-define */
|
|
2296
|
+
|
|
2297
|
+
|
|
2298
|
+
|
|
2299
|
+
|
|
2300
|
+
|
|
2301
|
+
|
|
2302
|
+
|
|
2303
|
+
|
|
2304
|
+
|
|
2305
|
+
|
|
2306
|
+
|
|
2307
|
+
|
|
2308
|
+
|
|
2309
|
+
|
|
2310
|
+
// See public API at the bottom of this file.
|
|
2311
|
+
|
|
2312
|
+
let _matchGroupItemChildren;
|
|
2313
|
+
let _clearDibbs;
|
|
2314
|
+
let _noDibbs;
|
|
2315
|
+
let _dibbs;
|
|
2316
|
+
let _createStatementsForConstraints;
|
|
2317
|
+
let _fuzzy = false;
|
|
2318
|
+
const match = (graph, uri, template) => {
|
|
2319
|
+
const rootBinding = new GroupBinding({
|
|
2320
|
+
item: template,
|
|
2321
|
+
childrenRootUri: uri,
|
|
2322
|
+
graph
|
|
2323
|
+
});
|
|
2324
|
+
_matchGroupItemChildren(rootBinding);
|
|
2325
|
+
_clearDibbs(rootBinding);
|
|
2326
|
+
return rootBinding;
|
|
2327
|
+
};
|
|
2328
|
+
const fuzzyMatch = (graph, uri, template) => {
|
|
2329
|
+
const rootBinding = new GroupBinding({
|
|
2330
|
+
item: template,
|
|
2331
|
+
childrenRootUri: uri,
|
|
2332
|
+
graph
|
|
2333
|
+
});
|
|
2334
|
+
_matchGroupItemChildren(rootBinding);
|
|
2335
|
+
_fuzzy = true;
|
|
2336
|
+
_matchGroupItemChildren(rootBinding);
|
|
2337
|
+
_fuzzy = false;
|
|
2338
|
+
_clearDibbs(rootBinding);
|
|
2339
|
+
return rootBinding;
|
|
2340
|
+
};
|
|
2341
|
+
const constructTemplate = (graph, uri, itemStore, requiredItems) => {
|
|
2342
|
+
const props = graph.findProperties(uri);
|
|
2343
|
+
const items = [];
|
|
2344
|
+
const fixedProps = {};
|
|
2345
|
+
if (requiredItems != null) {
|
|
2346
|
+
const addProperty = item => {
|
|
2347
|
+
if (item.getProperty() != null) {
|
|
2348
|
+
fixedProps[item.getProperty()] = true;
|
|
2349
|
+
} else if (item instanceof Group) {
|
|
2350
|
+
item.getChildren().forEach(addProperty);
|
|
2351
|
+
}
|
|
2352
|
+
};
|
|
2353
|
+
const addItem = item => {
|
|
2354
|
+
if (item != null) {
|
|
2355
|
+
addProperty(item);
|
|
2356
|
+
items.push(item);
|
|
2357
|
+
}
|
|
2358
|
+
};
|
|
2359
|
+
requiredItems.forEach(id => {
|
|
2360
|
+
let item = itemStore.getItem(id);
|
|
2361
|
+
if (item != null) {
|
|
2362
|
+
if (item instanceof Group && item.getProperty() == null) {
|
|
2363
|
+
item.getChildren().forEach(addItem);
|
|
2364
|
+
} else {
|
|
2365
|
+
addItem(item);
|
|
2366
|
+
}
|
|
2367
|
+
} else {
|
|
2368
|
+
item = itemStore.getItemByProperty(id);
|
|
2369
|
+
if (item) {
|
|
2370
|
+
addItem(item);
|
|
2371
|
+
} else {
|
|
2372
|
+
console.warn(`Warning, when autodetecting a template: Required item '${id}' is neither an id for a loaded item or a property for a loaded item, ignoring.`);
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
});
|
|
2376
|
+
}
|
|
2377
|
+
props.forEach(prop => {
|
|
2378
|
+
if (fixedProps[prop]) {
|
|
2379
|
+
return;
|
|
2380
|
+
}
|
|
2381
|
+
const item = itemStore.getItemByProperty(prop);
|
|
2382
|
+
if (item != null) {
|
|
2383
|
+
items.push(item);
|
|
2384
|
+
}
|
|
2385
|
+
}, undefined);
|
|
2386
|
+
// TODO sort according to priority.
|
|
2387
|
+
return itemStore.createTemplateFromChildren(items);
|
|
2388
|
+
};
|
|
2389
|
+
|
|
2390
|
+
//= ==============================================
|
|
2391
|
+
// Core creation engine
|
|
2392
|
+
//= ==============================================
|
|
2393
|
+
const getFirstDataType = item => Array.isArray(item.getDatatype()) ? item.getDatatype()[0] : item.getDatatype();
|
|
2394
|
+
const _createTextItem = (parentBinding, item) => {
|
|
2395
|
+
if (!item.getProperty()) {
|
|
2396
|
+
const groupURIBinding = new GroupURIBinding({
|
|
2397
|
+
item,
|
|
2398
|
+
statement: null,
|
|
2399
|
+
matchingCode: model_CODES.OK
|
|
2400
|
+
});
|
|
2401
|
+
parentBinding.addChildBinding(groupURIBinding);
|
|
2402
|
+
return groupURIBinding;
|
|
2403
|
+
}
|
|
2404
|
+
const graph = parentBinding.getGraph();
|
|
2405
|
+
const nt = item.getNodetype();
|
|
2406
|
+
const obj = {
|
|
2407
|
+
value: '',
|
|
2408
|
+
type: 'literal'
|
|
2409
|
+
};
|
|
2410
|
+
if (nt === 'URI') {
|
|
2411
|
+
obj.type = 'uri';
|
|
2412
|
+
} else if (nt === 'DATATYPE_LITERAL') {
|
|
2413
|
+
obj.datatype = getFirstDataType(item);
|
|
2414
|
+
}
|
|
2415
|
+
const defaultValue = item.getValue();
|
|
2416
|
+
if (defaultValue != null && parentBinding.getChildBindingsFor(item).length === 0) {
|
|
2417
|
+
obj.value = defaultValue;
|
|
2418
|
+
const la = item.getLanguage();
|
|
2419
|
+
if (la != null) {
|
|
2420
|
+
obj.lang = la;
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
const stmt = graph.create(parentBinding.getChildrenRootUri(), item.getProperty(), obj, false);
|
|
2424
|
+
const nbinding = new ValueBinding({
|
|
2425
|
+
item,
|
|
2426
|
+
statement: stmt
|
|
2427
|
+
});
|
|
2428
|
+
parentBinding.addChildBinding(nbinding);
|
|
2429
|
+
return nbinding;
|
|
2430
|
+
};
|
|
2431
|
+
const _createChoiceItem = (parentBinding, item) => {
|
|
2432
|
+
const graph = parentBinding.getGraph();
|
|
2433
|
+
const nt = item.getNodetype();
|
|
2434
|
+
const obj = {
|
|
2435
|
+
type: 'literal',
|
|
2436
|
+
value: ''
|
|
2437
|
+
};
|
|
2438
|
+
if (nt === 'DATATYPE_LITERAL') {
|
|
2439
|
+
obj.datatype = getFirstDataType(item);
|
|
2440
|
+
} else if (nt === 'RESOURCE' || nt === 'URI') {
|
|
2441
|
+
obj.type = 'uri';
|
|
2442
|
+
}
|
|
2443
|
+
const stmt = graph.create(parentBinding.getChildrenRootUri(), item.getProperty(), obj, false);
|
|
2444
|
+
const nbinding = new ChoiceBinding({
|
|
2445
|
+
item,
|
|
2446
|
+
statement: stmt
|
|
2447
|
+
});
|
|
2448
|
+
parentBinding.addChildBinding(nbinding);
|
|
2449
|
+
const defaultValue = item.getValue();
|
|
2450
|
+
const cardTracker = nbinding.getCardinalityTracker();
|
|
2451
|
+
if (defaultValue != null && cardTracker.getCounter() === 1) {
|
|
2452
|
+
nbinding.setChoice(_findChoice(item, defaultValue, graph), true);
|
|
2453
|
+
}
|
|
2454
|
+
return nbinding;
|
|
2455
|
+
};
|
|
2456
|
+
const _createGroupItem = (parentBinding, item, parentItems) => {
|
|
2457
|
+
let stmt;
|
|
2458
|
+
let constr;
|
|
2459
|
+
if (item.getProperty() !== undefined) {
|
|
2460
|
+
const graph = parentBinding.getGraph();
|
|
2461
|
+
if (item.getNodetype() === 'URI') {
|
|
2462
|
+
/*stmt = graph.create(parentBinding.getChildrenRootUri(), item.getProperty(), {
|
|
2463
|
+
type: 'uri',
|
|
2464
|
+
value: system.createURI(item, parentBinding),
|
|
2465
|
+
}, false);*/
|
|
2466
|
+
// Create as a blank, will not be asserted until changed into a uri.
|
|
2467
|
+
stmt = graph.create(parentBinding.getChildrenRootUri(), item.getProperty(), null, false);
|
|
2468
|
+
} else {
|
|
2469
|
+
stmt = graph.create(parentBinding.getChildrenRootUri(), item.getProperty(), null, false);
|
|
2470
|
+
}
|
|
2471
|
+
constr = _createStatementsForConstraints(graph, stmt.getValue(), item);
|
|
2472
|
+
}
|
|
2473
|
+
const nBinding = new GroupBinding({
|
|
2474
|
+
item,
|
|
2475
|
+
statement: stmt,
|
|
2476
|
+
constraints: constr
|
|
2477
|
+
});
|
|
2478
|
+
parentBinding.addChildBinding(nBinding);
|
|
2479
|
+
|
|
2480
|
+
// Only do loop detection for items that are stored in the itemStore and hence are
|
|
2481
|
+
// used in more than one place.
|
|
2482
|
+
const itemId = item._source['@id'];
|
|
2483
|
+
if (itemId) {
|
|
2484
|
+
// If loop stop.
|
|
2485
|
+
if (parentItems[itemId]) {
|
|
2486
|
+
return nBinding;
|
|
2487
|
+
}
|
|
2488
|
+
parentItems[itemId] = true;
|
|
2489
|
+
}
|
|
2490
|
+
// Do not create substructures directly, let the view model and user interaction decide
|
|
2491
|
+
// when to create children.
|
|
2492
|
+
/*
|
|
2493
|
+
array.forEach(item.getChildren(), function(childItem) {
|
|
2494
|
+
create(nBinding, childItem, parentItems);
|
|
2495
|
+
}); */
|
|
2496
|
+
return nBinding;
|
|
2497
|
+
};
|
|
2498
|
+
const _createPropertyGroupItem = (parentBinding, item) => {
|
|
2499
|
+
let stmt;
|
|
2500
|
+
let constr;
|
|
2501
|
+
const oItem = item.getChildren()[1];
|
|
2502
|
+
const graph = parentBinding.getGraph();
|
|
2503
|
+
if (oItem instanceof Group) {
|
|
2504
|
+
stmt = graph.create(parentBinding.getChildrenRootUri(), '', null, false);
|
|
2505
|
+
constr = _createStatementsForConstraints(graph, stmt.getSubject(), oItem);
|
|
2506
|
+
} else if (oItem instanceof Choice) {
|
|
2507
|
+
stmt = graph.create(parentBinding.getChildrenRootUri(), '', {
|
|
2508
|
+
type: 'uri',
|
|
2509
|
+
value: ''
|
|
2510
|
+
}, false);
|
|
2511
|
+
} else {
|
|
2512
|
+
stmt = graph.create(parentBinding.getChildrenRootUri(), '', {
|
|
2513
|
+
type: 'literal',
|
|
2514
|
+
value: ''
|
|
2515
|
+
}, false);
|
|
2516
|
+
}
|
|
2517
|
+
const nBinding = new PropertyGroupBinding({
|
|
2518
|
+
item,
|
|
2519
|
+
statement: stmt,
|
|
2520
|
+
constraints: constr
|
|
2521
|
+
});
|
|
2522
|
+
parentBinding.addChildBinding(nBinding);
|
|
2523
|
+
if (oItem instanceof Group) {
|
|
2524
|
+
oItem.getChildren().forEach(childItem => {
|
|
2525
|
+
create(nBinding.getObjectBinding(), childItem);
|
|
2526
|
+
});
|
|
2527
|
+
}
|
|
2528
|
+
return nBinding;
|
|
2529
|
+
};
|
|
2530
|
+
const create = (parentBinding, item, parentItems) => {
|
|
2531
|
+
if (item instanceof Text) {
|
|
2532
|
+
return _createTextItem(parentBinding, item);
|
|
2533
|
+
} else if (item instanceof PropertyGroup) {
|
|
2534
|
+
return _createPropertyGroupItem(parentBinding, item);
|
|
2535
|
+
} else if (item instanceof Group) {
|
|
2536
|
+
return _createGroupItem(parentBinding, item, parentItems || {});
|
|
2537
|
+
} else if (item instanceof Choice) {
|
|
2538
|
+
return _createChoiceItem(parentBinding, item);
|
|
2539
|
+
}
|
|
2540
|
+
return undefined;
|
|
2541
|
+
};
|
|
2542
|
+
|
|
2543
|
+
// ===============================================
|
|
2544
|
+
// Core matching engine
|
|
2545
|
+
// ===============================================
|
|
2546
|
+
|
|
2547
|
+
let _matchItem;
|
|
2548
|
+
let _isNodeTypeMatch;
|
|
2549
|
+
let _isDataTypeMatch;
|
|
2550
|
+
let _isPatternMatch;
|
|
2551
|
+
let _findChoice;
|
|
2552
|
+
let _findStatementsForConstraints;
|
|
2553
|
+
_matchGroupItemChildren = pb => {
|
|
2554
|
+
if (_fuzzy) {
|
|
2555
|
+
pb.getChildBindings().forEach(cb => {
|
|
2556
|
+
if (cb.getItem().getType() === 'group') {
|
|
2557
|
+
_matchGroupItemChildren(cb);
|
|
2558
|
+
}
|
|
2559
|
+
});
|
|
2560
|
+
}
|
|
2561
|
+
pb.getItem().getChildren().forEach(item => {
|
|
2562
|
+
if (_fuzzy) {
|
|
2563
|
+
if (item.getProperty() !== undefined) {
|
|
2564
|
+
_matchItem(pb, item);
|
|
2565
|
+
}
|
|
2566
|
+
} else {
|
|
2567
|
+
_matchItem(pb, item);
|
|
2568
|
+
}
|
|
2569
|
+
});
|
|
2570
|
+
};
|
|
2571
|
+
const _matchGroupItem = (pb, item) => {
|
|
2572
|
+
let stmts;
|
|
2573
|
+
let bindings;
|
|
2574
|
+
let constStmts;
|
|
2575
|
+
let groupBinding;
|
|
2576
|
+
const graph = pb.getGraph();
|
|
2577
|
+
// Case 1: there is a property in the item
|
|
2578
|
+
if (item.getProperty() !== undefined) {
|
|
2579
|
+
stmts = graph.find(pb.getChildrenRootUri(), item.getProperty());
|
|
2580
|
+
if (stmts.length > 0) {
|
|
2581
|
+
bindings = [];
|
|
2582
|
+
stmts.forEach(stmt => {
|
|
2583
|
+
if (_noDibbs(stmt)) {
|
|
2584
|
+
const pMatch = _isPatternMatch(item, stmt);
|
|
2585
|
+
const ntMatch = _isNodeTypeMatch(item, stmt);
|
|
2586
|
+
if (pMatch && ntMatch || _fuzzy) {
|
|
2587
|
+
constStmts = _findStatementsForConstraints(graph, stmt.getValue(), item);
|
|
2588
|
+
if (constStmts !== undefined || _fuzzy) {
|
|
2589
|
+
let matchingCode = !ntMatch ? model_CODES.WRONG_NODETYPE : model_CODES.OK;
|
|
2590
|
+
if (!pMatch) {
|
|
2591
|
+
matchingCode = model_CODES.WRONG_PATTERN;
|
|
2592
|
+
} else if (constStmts === undefined) {
|
|
2593
|
+
matchingCode = model_CODES.MISSING_CONSTRAINTS;
|
|
2594
|
+
}
|
|
2595
|
+
_dibbs(stmt);
|
|
2596
|
+
groupBinding = new GroupBinding({
|
|
2597
|
+
item,
|
|
2598
|
+
statement: stmt,
|
|
2599
|
+
constraints: constStmts,
|
|
2600
|
+
matchingCode
|
|
2601
|
+
});
|
|
2602
|
+
bindings.push(groupBinding);
|
|
2603
|
+
if (matchingCode === model_CODES.OK) {
|
|
2604
|
+
_matchGroupItemChildren(groupBinding); // Recursive call
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
});
|
|
2610
|
+
pb.addChildBindings(bindings);
|
|
2611
|
+
}
|
|
2612
|
+
// Case 2: there is no property in the item, i.e. a layout item.
|
|
2613
|
+
} else {
|
|
2614
|
+
groupBinding = new GroupBinding({
|
|
2615
|
+
item
|
|
2616
|
+
});
|
|
2617
|
+
pb.addChildBindings([groupBinding]);
|
|
2618
|
+
_matchGroupItemChildren(groupBinding); // Recursive call
|
|
2619
|
+
}
|
|
2620
|
+
};
|
|
2621
|
+
const _matchPropertyGroupItem = (pb, item) => {
|
|
2622
|
+
const pItem = item.getPropertyItem();
|
|
2623
|
+
const oItem = item.getObjectItem();
|
|
2624
|
+
const graph = pb.getGraph();
|
|
2625
|
+
let constStmts;
|
|
2626
|
+
let binding;
|
|
2627
|
+
let pChoice;
|
|
2628
|
+
let oChoice;
|
|
2629
|
+
const bindings = [];
|
|
2630
|
+
graph.find(pb.getChildrenRootUri()).forEach(stmt => {
|
|
2631
|
+
if (_noDibbs(stmt) && _isNodeTypeMatch(oItem, stmt) && _isPatternMatch(oItem, stmt)) {
|
|
2632
|
+
pChoice = _findChoice(pItem, stmt.getPredicate(), stmt.getGraph());
|
|
2633
|
+
if (pChoice !== undefined) {
|
|
2634
|
+
binding = null;
|
|
2635
|
+
if (oItem instanceof Group) {
|
|
2636
|
+
constStmts = _findStatementsForConstraints(graph, stmt.getValue(), oItem);
|
|
2637
|
+
if (constStmts !== undefined) {
|
|
2638
|
+
_dibbs(stmt);
|
|
2639
|
+
binding = new PropertyGroupBinding({
|
|
2640
|
+
item,
|
|
2641
|
+
statement: stmt,
|
|
2642
|
+
constraints: constStmts
|
|
2643
|
+
});
|
|
2644
|
+
_matchGroupItemChildren(binding.getObjectBinding()); // Recursive call
|
|
2645
|
+
}
|
|
2646
|
+
} else if (oItem instanceof Choice) {
|
|
2647
|
+
oChoice = _findChoice(oItem, stmt.getValue(), stmt.getGraph());
|
|
2648
|
+
if (oChoice !== undefined) {
|
|
2649
|
+
_dibbs(stmt);
|
|
2650
|
+
binding = new PropertyGroupBinding({
|
|
2651
|
+
item,
|
|
2652
|
+
statement: stmt
|
|
2653
|
+
});
|
|
2654
|
+
binding.getObjectBinding().setChoice(oChoice);
|
|
2655
|
+
}
|
|
2656
|
+
} else {
|
|
2657
|
+
_dibbs(stmt);
|
|
2658
|
+
binding = new PropertyGroupBinding({
|
|
2659
|
+
item,
|
|
2660
|
+
statement: stmt
|
|
2661
|
+
});
|
|
2662
|
+
}
|
|
2663
|
+
if (binding !== null) {
|
|
2664
|
+
binding.getPredicateBinding().setChoice(pChoice);
|
|
2665
|
+
bindings.push(binding);
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2669
|
+
});
|
|
2670
|
+
if (bindings.length > 0) {
|
|
2671
|
+
pb.addChildBindings(bindings);
|
|
2672
|
+
}
|
|
2673
|
+
};
|
|
2674
|
+
const _matchTextItem = (pb, item) => {
|
|
2675
|
+
const bindings = [];
|
|
2676
|
+
if (item.getProperty() == null) {
|
|
2677
|
+
bindings.push(new GroupURIBinding({
|
|
2678
|
+
item,
|
|
2679
|
+
statement: null,
|
|
2680
|
+
matchingCode: model_CODES.OK
|
|
2681
|
+
}));
|
|
2682
|
+
} else {
|
|
2683
|
+
pb.getGraph().find(pb.getChildrenRootUri(), item.getProperty()).forEach(stmt => {
|
|
2684
|
+
if (_noDibbs(stmt) && _isPatternMatch(item, stmt)) {
|
|
2685
|
+
const ntMatch = _isNodeTypeMatch(item, stmt);
|
|
2686
|
+
const dtMatch = _isDataTypeMatch(item, stmt);
|
|
2687
|
+
if (ntMatch && (dtMatch || item.hasStyle('relaxedDatatypeMatch')) || _fuzzy) {
|
|
2688
|
+
_dibbs(stmt);
|
|
2689
|
+
let matchingCode = ntMatch ? model_CODES.OK : model_CODES.WRONG_NODETYPE;
|
|
2690
|
+
if (!dtMatch) {
|
|
2691
|
+
matchingCode = model_CODES.WRONG_DATATYPE;
|
|
2692
|
+
}
|
|
2693
|
+
bindings.push(new ValueBinding({
|
|
2694
|
+
item,
|
|
2695
|
+
statement: stmt,
|
|
2696
|
+
matchingCode
|
|
2697
|
+
}));
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
});
|
|
2701
|
+
}
|
|
2702
|
+
if (bindings.length > 0) {
|
|
2703
|
+
pb.addChildBindings(bindings);
|
|
2704
|
+
}
|
|
2705
|
+
};
|
|
2706
|
+
const _matchChoiceItem = (pb, item) => {
|
|
2707
|
+
if (item.getProperty() == null) {
|
|
2708
|
+
return;
|
|
2709
|
+
}
|
|
2710
|
+
const bindings = [];
|
|
2711
|
+
pb.getGraph().find(pb.getChildrenRootUri(), item.getProperty()).forEach(stmt => {
|
|
2712
|
+
if (_noDibbs(stmt)) {
|
|
2713
|
+
const ntMatch = _isNodeTypeMatch(item, stmt);
|
|
2714
|
+
const dtMatch = _isDataTypeMatch(item, stmt);
|
|
2715
|
+
const pMatch = _isPatternMatch(item, stmt);
|
|
2716
|
+
if (ntMatch && dtMatch && pMatch || _fuzzy) {
|
|
2717
|
+
const choice = _findChoice(item, stmt.getValue(), stmt.getGraph());
|
|
2718
|
+
if (choice !== undefined) {
|
|
2719
|
+
_dibbs(stmt);
|
|
2720
|
+
let matchingCode = !ntMatch ? model_CODES.WRONG_NODETYPE : model_CODES.OK;
|
|
2721
|
+
if (!dtMatch) {
|
|
2722
|
+
matchingCode = model_CODES.WRONG_DATATYPE;
|
|
2723
|
+
} else if (!pMatch) {
|
|
2724
|
+
matchingCode = model_CODES.WRONG_PATTERN;
|
|
2725
|
+
} else if (choice.mismatch) {
|
|
2726
|
+
matchingCode = model_CODES.WRONG_VALUE;
|
|
2727
|
+
}
|
|
2728
|
+
bindings.push(new ChoiceBinding({
|
|
2729
|
+
item,
|
|
2730
|
+
statement: stmt,
|
|
2731
|
+
choice,
|
|
2732
|
+
matchingCode
|
|
2733
|
+
}));
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
});
|
|
2738
|
+
if (bindings.length > 0) {
|
|
2739
|
+
pb.addChildBindings(bindings);
|
|
2740
|
+
}
|
|
2741
|
+
};
|
|
2742
|
+
_matchItem = (pb, item) => {
|
|
2743
|
+
if (item instanceof Choice) {
|
|
2744
|
+
_matchChoiceItem(pb, item);
|
|
2745
|
+
} else if (item instanceof PropertyGroup) {
|
|
2746
|
+
_matchPropertyGroupItem(pb, item);
|
|
2747
|
+
} else if (item instanceof Group) {
|
|
2748
|
+
_matchGroupItem(pb, item);
|
|
2749
|
+
} else if (item instanceof Text) {
|
|
2750
|
+
_matchTextItem(pb, item);
|
|
2751
|
+
}
|
|
2752
|
+
};
|
|
2753
|
+
|
|
2754
|
+
// ===============================================
|
|
2755
|
+
// Utility functions used for matching purposes
|
|
2756
|
+
// ===============================================
|
|
2757
|
+
|
|
2758
|
+
/**
|
|
2759
|
+
* Compares the the type specified in the item and the type of the statements object.
|
|
2760
|
+
* @param {rdforms/template/Item} item
|
|
2761
|
+
* @param {jsonrdf/Statement} stmt
|
|
2762
|
+
*/
|
|
2763
|
+
_isNodeTypeMatch = (item, stmt) => {
|
|
2764
|
+
const objectType = stmt.getType();
|
|
2765
|
+
// eslint-disable-next-line default-case
|
|
2766
|
+
switch (item.getNodetype()) {
|
|
2767
|
+
case 'LITERAL': // Any form of literal
|
|
2768
|
+
case 'ONLY_LITERAL': // No language, no datatype
|
|
2769
|
+
case 'PLAIN_LITERAL': // No datatype, perhaps a language
|
|
2770
|
+
case 'LANGUAGE_LITERAL':
|
|
2771
|
+
// Definitely a language
|
|
2772
|
+
return objectType === 'literal';
|
|
2773
|
+
case 'DATATYPE_LITERAL':
|
|
2774
|
+
// Definitiely a datatype
|
|
2775
|
+
return objectType === 'literal';
|
|
2776
|
+
case 'RESOURCE':
|
|
2777
|
+
return objectType === 'uri' || objectType === 'bnode';
|
|
2778
|
+
case 'URI':
|
|
2779
|
+
return objectType === 'uri';
|
|
2780
|
+
case 'BLANK':
|
|
2781
|
+
return objectType === 'bnode';
|
|
2782
|
+
}
|
|
2783
|
+
return false;
|
|
2784
|
+
};
|
|
2785
|
+
_isDataTypeMatch = (item, stmt) => {
|
|
2786
|
+
const dt = item.getNodetype() === 'DATATYPE_LITERAL' ? item.getDatatype() || null : null;
|
|
2787
|
+
if (dt != null) {
|
|
2788
|
+
return Array.isArray(dt) ? dt.indexOf(stmt.getDatatype()) !== -1 : stmt.getDatatype() === dt;
|
|
2789
|
+
}
|
|
2790
|
+
return true;
|
|
2791
|
+
};
|
|
2792
|
+
_isPatternMatch = (item, stmt) => {
|
|
2793
|
+
const pattern = item.getPattern();
|
|
2794
|
+
const value = utils.extractGist(stmt.getValue(), item.getValueTemplate());
|
|
2795
|
+
if (typeof pattern !== 'undefined') {
|
|
2796
|
+
try {
|
|
2797
|
+
return new RegExp(`^${pattern}$`).test(value);
|
|
2798
|
+
} catch (e) {
|
|
2799
|
+
return true;
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
return true;
|
|
2803
|
+
};
|
|
2804
|
+
|
|
2805
|
+
/**
|
|
2806
|
+
* Matches constraints in the item to statements in the graph with the given uri as subject.
|
|
2807
|
+
*
|
|
2808
|
+
* @param {rdfjson/Graph} graph containing all available statements to match against.
|
|
2809
|
+
* @param {String} uri the subject to start matching from
|
|
2810
|
+
* @param {rdforms/template/Item} item containing the constraints.
|
|
2811
|
+
* @return an array of statements on success, undefined on failure.
|
|
2812
|
+
* If there are no constraints to match in the item an empty array is returned.
|
|
2813
|
+
*/
|
|
2814
|
+
_findStatementsForConstraints = (graph, uri, item) => {
|
|
2815
|
+
let stmts;
|
|
2816
|
+
const constr = item.getConstraints();
|
|
2817
|
+
const results = [];
|
|
2818
|
+
const f = (predicate, object) => {
|
|
2819
|
+
stmts = graph.find(uri, predicate, {
|
|
2820
|
+
type: 'uri',
|
|
2821
|
+
value: object
|
|
2822
|
+
});
|
|
2823
|
+
if (stmts.length === 1) {
|
|
2824
|
+
results.push(stmts[0]);
|
|
2825
|
+
return undefined;
|
|
2826
|
+
}
|
|
2827
|
+
return false;
|
|
2828
|
+
};
|
|
2829
|
+
if (typeof constr === 'object' && constr !== null) {
|
|
2830
|
+
const keys = Object.keys(constr);
|
|
2831
|
+
for (let idx = 0; idx < keys.length; idx++) {
|
|
2832
|
+
const key = keys[idx];
|
|
2833
|
+
const obj = constr[key];
|
|
2834
|
+
if (obj instanceof Array) {
|
|
2835
|
+
let noMatch = true;
|
|
2836
|
+
obj.forEach(o => {
|
|
2837
|
+
if (f(key, o) !== false) {
|
|
2838
|
+
noMatch = false;
|
|
2839
|
+
}
|
|
2840
|
+
});
|
|
2841
|
+
if (noMatch) {
|
|
2842
|
+
return undefined;
|
|
2843
|
+
}
|
|
2844
|
+
} else if (f(key, obj) === false) {
|
|
2845
|
+
return undefined;
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
return results;
|
|
2849
|
+
}
|
|
2850
|
+
return [];
|
|
2851
|
+
};
|
|
2852
|
+
_createStatementsForConstraints = (graph, uri, item) => {
|
|
2853
|
+
const results = [];
|
|
2854
|
+
const constr = item.getConstraints();
|
|
2855
|
+
if (typeof constr === 'object' && constr !== null) {
|
|
2856
|
+
Object.keys(constr).forEach(key => {
|
|
2857
|
+
const obj = constr[key];
|
|
2858
|
+
if (Array.isArray(obj)) {
|
|
2859
|
+
results.push(graph.create(uri, key, {
|
|
2860
|
+
type: 'uri',
|
|
2861
|
+
value: obj[0]
|
|
2862
|
+
}, false));
|
|
2863
|
+
} else {
|
|
2864
|
+
results.push(graph.create(uri, key, {
|
|
2865
|
+
type: 'uri',
|
|
2866
|
+
value: obj
|
|
2867
|
+
}, false));
|
|
2868
|
+
}
|
|
2869
|
+
});
|
|
2870
|
+
}
|
|
2871
|
+
return results;
|
|
2872
|
+
};
|
|
2873
|
+
_findChoice = (item, obj, graph) => {
|
|
2874
|
+
let index;
|
|
2875
|
+
let choices;
|
|
2876
|
+
if (item.hasChoices()) {
|
|
2877
|
+
choices = item.getChoices();
|
|
2878
|
+
for (index = 0; index < choices.length; index++) {
|
|
2879
|
+
if (choices[index].value === obj) {
|
|
2880
|
+
return choices[index];
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2883
|
+
if (!item.hasStyle('strictmatch') || _fuzzy) {
|
|
2884
|
+
return {
|
|
2885
|
+
value: obj,
|
|
2886
|
+
label: {
|
|
2887
|
+
'': obj
|
|
2888
|
+
},
|
|
2889
|
+
mismatch: true
|
|
2890
|
+
};
|
|
2891
|
+
}
|
|
2892
|
+
} else {
|
|
2893
|
+
let label = utils.getLocalizedMap(graph, obj, item.getLabelProperties());
|
|
2894
|
+
if (label == null && _fuzzy) {
|
|
2895
|
+
label = {
|
|
2896
|
+
'': obj,
|
|
2897
|
+
mismatch: true
|
|
2898
|
+
};
|
|
2899
|
+
}
|
|
2900
|
+
const sa = graph.findFirstValue(obj, ChoiceBinding.seeAlso);
|
|
2901
|
+
if (label != null) {
|
|
2902
|
+
const choice = {
|
|
2903
|
+
label,
|
|
2904
|
+
value: obj
|
|
2905
|
+
};
|
|
2906
|
+
if (sa) {
|
|
2907
|
+
choice.seeAlso = sa;
|
|
2908
|
+
}
|
|
2909
|
+
return choice;
|
|
2910
|
+
} else if (system.getChoice != null) {
|
|
2911
|
+
return system.getChoice(item, obj, sa, graph);
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
return undefined;
|
|
2915
|
+
};
|
|
2916
|
+
_dibbs = stmt => {
|
|
2917
|
+
stmt._dibbs = true;
|
|
2918
|
+
};
|
|
2919
|
+
_noDibbs = stmt => stmt._dibbs !== true;
|
|
2920
|
+
_clearDibbs = groupBinding => {
|
|
2921
|
+
let i;
|
|
2922
|
+
let j;
|
|
2923
|
+
let arr;
|
|
2924
|
+
const arrarr = groupBinding.getItemGroupedChildBindings() || [];
|
|
2925
|
+
for (i = 0; i < arrarr.length; i++) {
|
|
2926
|
+
arr = arrarr[i];
|
|
2927
|
+
for (j = 0; j < arr.length; j++) {
|
|
2928
|
+
const binding = arr[j];
|
|
2929
|
+
if (binding._statement) {
|
|
2930
|
+
delete binding._statement._dibbs;
|
|
2931
|
+
}
|
|
2932
|
+
if (binding instanceof GroupBinding || binding instanceof PropertyGroupBinding) {
|
|
2933
|
+
_clearDibbs(binding);
|
|
2934
|
+
}
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
};
|
|
2938
|
+
const findFirstValueBinding = (binding, createIfMissing) => {
|
|
2939
|
+
if (binding instanceof ValueBinding) {
|
|
2940
|
+
return binding;
|
|
2941
|
+
}
|
|
2942
|
+
const cbs = binding.getItemGroupedChildBindings();
|
|
2943
|
+
const loc = external_moment_default().locale();
|
|
2944
|
+
for (let idx = 0; idx < cbs.length; idx++) {
|
|
2945
|
+
const vbs = cbs[idx];
|
|
2946
|
+
const childItem = binding.getItem().getChildren()[idx];
|
|
2947
|
+
if (vbs.length !== 0) {
|
|
2948
|
+
if (!(childItem instanceof Text)) {
|
|
2949
|
+
return findFirstValueBinding(vbs[0]);
|
|
2950
|
+
} else if (childItem.getNodetype() === 'LANGUAGE_LITERAL') {
|
|
2951
|
+
const result = {
|
|
2952
|
+
firstValue: vbs[0]
|
|
2953
|
+
};
|
|
2954
|
+
for (let i = 0; i < vbs.length; i++) {
|
|
2955
|
+
const l = vbs[i].getLanguage();
|
|
2956
|
+
if (l == null) {
|
|
2957
|
+
result.emptyLanguageValue = vbs[i];
|
|
2958
|
+
} else if (l === loc) {
|
|
2959
|
+
result.perfectLocaleLanguageValue = vbs[i];
|
|
2960
|
+
} else if (l.substring(0, 1) === loc.substring(0, 1)) {
|
|
2961
|
+
result.localeLanguageValue = vbs[i];
|
|
2962
|
+
} else if (l.indexOf('en') !== -1) {
|
|
2963
|
+
result.defaultLanguageValue = vbs[i];
|
|
2964
|
+
} else {
|
|
2965
|
+
result.anyLanguageValue = vbs[i];
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
return result.perfectLocaleLanguageValue || result.localeLanguageValue || result.defaultLanguageValue || result.anyLanguageValue || result.firstValue;
|
|
2969
|
+
}
|
|
2970
|
+
return vbs[0];
|
|
2971
|
+
} else if (createIfMissing) {
|
|
2972
|
+
const b = create(binding, childItem, {});
|
|
2973
|
+
if (b instanceof ValueBinding) {
|
|
2974
|
+
b.setLanguage(loc);
|
|
2975
|
+
return b;
|
|
2976
|
+
}
|
|
2977
|
+
return findFirstValueBinding(b, true);
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2980
|
+
return undefined;
|
|
2981
|
+
};
|
|
2982
|
+
const matchPathBelowBinding = (bindingTree, path) => {
|
|
2983
|
+
const _path = path[0] === '/' ? path.slice(1) : path;
|
|
2984
|
+
const gb = bindingTree.getItemGroupedChildBindings();
|
|
2985
|
+
const pred = _path[0];
|
|
2986
|
+
for (let i = 0; i < gb.length; i++) {
|
|
2987
|
+
const bindings = gb[i];
|
|
2988
|
+
let res;
|
|
2989
|
+
for (let j = 0; j < bindings.length; j++) {
|
|
2990
|
+
let b = bindings[j];
|
|
2991
|
+
let item = b.getItem();
|
|
2992
|
+
// Empty property choices.
|
|
2993
|
+
if (item instanceof Choice && typeof item.getProperty() === 'undefined' && pred === item.getId() && _path.length === 2 && _path[1] === b.getValue()) {
|
|
2994
|
+
return b;
|
|
2995
|
+
}
|
|
2996
|
+
if (!b.isValid()) {
|
|
2997
|
+
// eslint-disable-next-line no-continue
|
|
2998
|
+
continue;
|
|
2999
|
+
}
|
|
3000
|
+
if (item.getType() === 'propertygroup') {
|
|
3001
|
+
b = b.getObjectBinding();
|
|
3002
|
+
item = b.getItem();
|
|
3003
|
+
} else if (typeof item.getProperty() === 'undefined') {
|
|
3004
|
+
res = matchPathBelowBinding(b, _path);
|
|
3005
|
+
if (res) {
|
|
3006
|
+
return res;
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
3009
|
+
if (pred === '*' || pred === b.getPredicate() || pred === item.getId()) {
|
|
3010
|
+
if (item.getType() === 'group') {
|
|
3011
|
+
res = matchPathBelowBinding(b, _path.slice(1));
|
|
3012
|
+
if (res) {
|
|
3013
|
+
return res;
|
|
3014
|
+
}
|
|
3015
|
+
} else if (_path.length === 1 || _path[1] === '*' || _path[1] === b.getValue()) {
|
|
3016
|
+
return b;
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
return undefined;
|
|
3022
|
+
};
|
|
3023
|
+
const findBindingRelativeToParentBinding = (parentBinding, path) => {
|
|
3024
|
+
const first = path[0];
|
|
3025
|
+
let b = parentBinding;
|
|
3026
|
+
if (first === '/') {
|
|
3027
|
+
while (b.getParent()) {
|
|
3028
|
+
b = b.getParent();
|
|
3029
|
+
}
|
|
3030
|
+
return b;
|
|
3031
|
+
} else if (first === '..') {
|
|
3032
|
+
for (let i = 0; i < path.length; i++) {
|
|
3033
|
+
if (path[i] === '..' && b.getParent()) {
|
|
3034
|
+
b = b.getParent();
|
|
3035
|
+
} else {
|
|
3036
|
+
return b;
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
return b;
|
|
3040
|
+
}
|
|
3041
|
+
return parentBinding;
|
|
3042
|
+
};
|
|
3043
|
+
const findPopularChoice = (choiceItem, rootBinding) => {
|
|
3044
|
+
const id = choiceItem.getId();
|
|
3045
|
+
const values = {};
|
|
3046
|
+
const val2choice = {};
|
|
3047
|
+
choiceItem.getChoices().forEach(choice => {
|
|
3048
|
+
values[choice.value] = 0;
|
|
3049
|
+
val2choice[choice.value] = choice;
|
|
3050
|
+
});
|
|
3051
|
+
const recurse = groupB => {
|
|
3052
|
+
groupB.getChildBindings().forEach(cb => {
|
|
3053
|
+
const deps = cb.getItem().getDeps();
|
|
3054
|
+
if (deps && (deps[0] === id || deps[0] === '/' && deps[1] === id)) {
|
|
3055
|
+
const val = deps[0] === '/' ? deps[2] : deps[1];
|
|
3056
|
+
const counter = values[val];
|
|
3057
|
+
if (typeof counter !== 'undefined') {
|
|
3058
|
+
values[val] = counter + 1;
|
|
3059
|
+
}
|
|
3060
|
+
}
|
|
3061
|
+
});
|
|
3062
|
+
};
|
|
3063
|
+
recurse(rootBinding);
|
|
3064
|
+
let popularCount = 0;
|
|
3065
|
+
let value;
|
|
3066
|
+
Object.keys(values).forEach(val => {
|
|
3067
|
+
if (!value || values[val] > popularCount) {
|
|
3068
|
+
value = val;
|
|
3069
|
+
popularCount = values[val];
|
|
3070
|
+
}
|
|
3071
|
+
});
|
|
3072
|
+
return val2choice[value];
|
|
3073
|
+
};
|
|
3074
|
+
const _levelProfile = (profile, item, ignoreTopLevelGroup) => {
|
|
3075
|
+
const card = item.getCardinality();
|
|
3076
|
+
if (!ignoreTopLevelGroup || item.getType() !== 'group') {
|
|
3077
|
+
if (card != null) {
|
|
3078
|
+
if (card.min > 0) {
|
|
3079
|
+
profile.mandatory += 1;
|
|
3080
|
+
} else if (card.pref > 0) {
|
|
3081
|
+
profile.recommended += 1;
|
|
3082
|
+
} else {
|
|
3083
|
+
profile.optional += 1;
|
|
3084
|
+
}
|
|
3085
|
+
} else {
|
|
3086
|
+
profile.optional += 1;
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
if (item.getType() === 'group') {
|
|
3090
|
+
item.getChildren().forEach(i => _levelProfile(profile, i));
|
|
3091
|
+
}
|
|
3092
|
+
return profile;
|
|
3093
|
+
};
|
|
3094
|
+
const levelProfile = item => {
|
|
3095
|
+
const profile = _levelProfile({
|
|
3096
|
+
mandatory: 0,
|
|
3097
|
+
recommended: 0,
|
|
3098
|
+
optional: 0
|
|
3099
|
+
}, item, true);
|
|
3100
|
+
profile.itemCount = profile.mandatory + profile.recommended + profile.optional;
|
|
3101
|
+
return profile;
|
|
3102
|
+
};
|
|
3103
|
+
const detectLevel = profile => {
|
|
3104
|
+
if (profile.mandatory > 0) {
|
|
3105
|
+
if (profile.recommended === 0 && profile.optional === 0) {
|
|
3106
|
+
return 'mandatory';
|
|
3107
|
+
} else if (profile.optional === 0) {
|
|
3108
|
+
return 'mixed_mandatory_recommended';
|
|
3109
|
+
} else if (profile.recommended === 0) {
|
|
3110
|
+
return 'mixed_mandatory_optional';
|
|
3111
|
+
}
|
|
3112
|
+
} else if (profile.recommended > 0) {
|
|
3113
|
+
if (profile.optional === 0) {
|
|
3114
|
+
return 'recommended';
|
|
3115
|
+
}
|
|
3116
|
+
return 'mixed_recommended_optional';
|
|
3117
|
+
} else if (profile.recommended === 0 && profile.recommended > 0 && profile.optional === 0) {
|
|
3118
|
+
return 'optional';
|
|
3119
|
+
}
|
|
3120
|
+
return 'mixed_all';
|
|
3121
|
+
};
|
|
3122
|
+
|
|
3123
|
+
//= ==============================================
|
|
3124
|
+
// Public API for matching and creation engine
|
|
3125
|
+
//= ==============================================
|
|
3126
|
+
/**
|
|
3127
|
+
* Matches a tree of statements with the uri as root
|
|
3128
|
+
* according to the constraints of the given template.
|
|
3129
|
+
* All the statements matched are found in the graph.
|
|
3130
|
+
* All statements in the graph that could be matched into the tree
|
|
3131
|
+
* are matched into the tree.
|
|
3132
|
+
* The tree is represented as a binding tree.
|
|
3133
|
+
*
|
|
3134
|
+
* @param {rdfjson/Graph} graph
|
|
3135
|
+
* @param {String} uri
|
|
3136
|
+
* @param {rdforms/template/Item} template
|
|
3137
|
+
* @return {rdforms/model/GroupBinding} which is the root of binding tree.
|
|
3138
|
+
*/
|
|
3139
|
+
|
|
3140
|
+
|
|
3141
|
+
|
|
3142
|
+
|
|
3143
|
+
|
|
3144
|
+
/**
|
|
3145
|
+
* Finds the choice in a choice item that are the most popular, i.e. the choice that most
|
|
3146
|
+
* valid bindings hava a dependency to.
|
|
3147
|
+
* @param {rdforms/template/Choice} choiceItem
|
|
3148
|
+
* @param {rdforms/model/GroupBinding} rootBinding
|
|
3149
|
+
*/
|
|
3150
|
+
|
|
3151
|
+
|
|
3152
|
+
/**
|
|
3153
|
+
* Constructs a template by finding an item per outgoing property for provided graph and
|
|
3154
|
+
* resource starting point.
|
|
3155
|
+
*
|
|
3156
|
+
* @param {Object} graph
|
|
3157
|
+
* @param {Object} uri
|
|
3158
|
+
* @param {Object} itemStore
|
|
3159
|
+
* @param {Array} requiredItems an array of required items specified by id or property that
|
|
3160
|
+
* will be enforced independent of corresponding property exists in the graph or not.
|
|
3161
|
+
* @return {rdforms/template/Item} the constructed template.
|
|
3162
|
+
*/
|
|
3163
|
+
|
|
3164
|
+
|
|
3165
|
+
/**
|
|
3166
|
+
* Creates a new binding below the given parentBinding according to what the item specifies.
|
|
3167
|
+
* New triples are created in the provided graph although not expressed if they have an
|
|
3168
|
+
* empty predicate or object. The item must be a direct child of the item
|
|
3169
|
+
* of the parentBinding.
|
|
3170
|
+
*
|
|
3171
|
+
* @param {rdforms/model/Binding} parentBinding
|
|
3172
|
+
* @param {rdforms/template/Item} item
|
|
3173
|
+
* @param {Object} parentItems is a hash of parent Items to use for loop detection.
|
|
3174
|
+
*/
|
|
3175
|
+
|
|
3176
|
+
|
|
3177
|
+
/**
|
|
3178
|
+
* Finds the first value binding in a binding tree, depth first.
|
|
3179
|
+
* If multiple value bindings are found with nodeType LANGUAGE_LITERAL
|
|
3180
|
+
* on the same level, the binding with the most appropriate language is chosen.
|
|
3181
|
+
* Most appropriate means checking for:
|
|
3182
|
+
* 1) exact current language (e.g. en_US),
|
|
3183
|
+
* 2) current language (e.g. matches en even if locale is en_US)
|
|
3184
|
+
* 3) default language (currently set to en)
|
|
3185
|
+
* 4) any literal with a language set found
|
|
3186
|
+
* 5) the first literal found
|
|
3187
|
+
*
|
|
3188
|
+
* @return {rdforms/model/ValueBinding}
|
|
3189
|
+
*/
|
|
3190
|
+
|
|
3191
|
+
|
|
3192
|
+
/**
|
|
3193
|
+
* Calculates the level profile, i.e. the amount of items on mandatory, recommended
|
|
3194
|
+
* and optional level in a given template.
|
|
3195
|
+
*
|
|
3196
|
+
* @param {rdforms/template/Item} item
|
|
3197
|
+
* @return {Object} with keys mandatory, recommended and optional, each pointing to an integer.
|
|
3198
|
+
*/
|
|
3199
|
+
|
|
3200
|
+
|
|
3201
|
+
/**
|
|
3202
|
+
* Investigates a level profile and provides the following responses:
|
|
3203
|
+
* * mandatory - only mandatory items
|
|
3204
|
+
* * recommended - only recommended items
|
|
3205
|
+
* * optional - only optional items
|
|
3206
|
+
* * mixed_all - a mix of all items
|
|
3207
|
+
* * mixed_mandatory_recommended - only mandatory and recommended items
|
|
3208
|
+
* * mixed_mandatory_optional - only mandatory and optional items
|
|
3209
|
+
* * mixed_recommended_optional - only recommended and optional items
|
|
3210
|
+
* @param {object} profile as provided by the levelProfile function.
|
|
3211
|
+
* @return {string} one of the responses outlined
|
|
3212
|
+
*/
|
|
3213
|
+
|
|
3214
|
+
|
|
3215
|
+
/* harmony default export */ const engine = ({
|
|
3216
|
+
// TODO @valentino anti-pattern. This is done because engine is used in EntryScape. It shouldn't really...
|
|
3217
|
+
detectLevel,
|
|
3218
|
+
levelProfile,
|
|
3219
|
+
findFirstValueBinding,
|
|
3220
|
+
create,
|
|
3221
|
+
constructTemplate,
|
|
3222
|
+
findPopularChoice,
|
|
3223
|
+
match,
|
|
3224
|
+
fuzzyMatch,
|
|
3225
|
+
matchPathBelowBinding,
|
|
3226
|
+
findBindingRelativeToParentBinding,
|
|
3227
|
+
CODES: model_CODES
|
|
3228
|
+
});
|
|
3229
|
+
;// ./src/template/ItemStore.js
|
|
3230
|
+
|
|
3231
|
+
|
|
3232
|
+
|
|
3233
|
+
|
|
3234
|
+
|
|
3235
|
+
|
|
3236
|
+
|
|
3237
|
+
|
|
3238
|
+
const deepMerge = (source1, source2) => {
|
|
3239
|
+
if (!source1 || !source2) {
|
|
3240
|
+
return source2 === undefined ? source1 : source2;
|
|
3241
|
+
}
|
|
3242
|
+
if (Array.isArray(source1) && Array.isArray(source2)) {
|
|
3243
|
+
return [].concat(origSource[key], extSource[key]);
|
|
3244
|
+
}
|
|
3245
|
+
if (typeof source1 === 'object' && typeof source2 === 'object') {
|
|
3246
|
+
const obj = {};
|
|
3247
|
+
Object.keys(source1).concat(Object.keys(source2)).forEach(key => {
|
|
3248
|
+
obj[key] = deepMerge(source1[key], source2[key]);
|
|
3249
|
+
});
|
|
3250
|
+
return obj;
|
|
3251
|
+
}
|
|
3252
|
+
return source2;
|
|
3253
|
+
};
|
|
3254
|
+
class ItemStore {
|
|
3255
|
+
/**
|
|
3256
|
+
* Keeps a registry of templates and reusable items.
|
|
3257
|
+
* Use the createTemplate method to create templates from a source
|
|
3258
|
+
* json structure, if the structure contains reusable items they are
|
|
3259
|
+
* created and stored separately as well.
|
|
3260
|
+
*/
|
|
3261
|
+
constructor(ontologyStore) {
|
|
3262
|
+
this.automaticSortAllowed = true;
|
|
3263
|
+
/**
|
|
3264
|
+
* Value may be console methods or 'throw'.
|
|
3265
|
+
* @type {string}
|
|
3266
|
+
*/
|
|
3267
|
+
this.handleErrorAs = 'throw';
|
|
3268
|
+
|
|
3269
|
+
//= =================================================;
|
|
3270
|
+
// Private Attribute;
|
|
3271
|
+
//= =================================================;
|
|
3272
|
+
this._bundles = [];
|
|
3273
|
+
this._registry = {};
|
|
3274
|
+
this._registryByProperty = {};
|
|
3275
|
+
this._ontologyStore = ontologyStore || new OntologyStore();
|
|
3276
|
+
}
|
|
3277
|
+
|
|
3278
|
+
//= ==================================================
|
|
3279
|
+
// Public API
|
|
3280
|
+
//= ==================================================
|
|
3281
|
+
getTemplate(id) {
|
|
3282
|
+
return this.getItem(id);
|
|
3283
|
+
}
|
|
3284
|
+
getChildren(group, original) {
|
|
3285
|
+
if (group == null) {
|
|
3286
|
+
return [];
|
|
3287
|
+
}
|
|
3288
|
+
const origSource = group.getSource(true);
|
|
3289
|
+
const origSourceContent = origSource.content || origSource.items || [];
|
|
3290
|
+
if (original) {
|
|
3291
|
+
return this._createItems(origSourceContent, group._forceChildrenClones, group.getBundle());
|
|
3292
|
+
}
|
|
3293
|
+
const ext = this.getItem(origSource.extends);
|
|
3294
|
+
if (ext) {
|
|
3295
|
+
const children = group.getChildren(true);
|
|
3296
|
+
if (group.getEnhanced('items') || children.length === 0) {
|
|
3297
|
+
return ext.getChildren().concat(children);
|
|
3298
|
+
}
|
|
3299
|
+
return children;
|
|
3300
|
+
}
|
|
3301
|
+
return group.getChildren(true);
|
|
3302
|
+
}
|
|
3303
|
+
getItem(id) {
|
|
3304
|
+
if (id != null) {
|
|
3305
|
+
return this._registry[id];
|
|
3306
|
+
}
|
|
3307
|
+
return undefined;
|
|
3308
|
+
}
|
|
3309
|
+
getItems() {
|
|
3310
|
+
return Object.keys(this._registry).map(key => this._registry[key]);
|
|
3311
|
+
}
|
|
3312
|
+
renameItem(from, to) {
|
|
3313
|
+
if (this._registry[to]) {
|
|
3314
|
+
this._handleError(`Cannot rename to ${to} since an item with that id already exists.`);
|
|
3315
|
+
return;
|
|
3316
|
+
}
|
|
3317
|
+
if (to === '' || to === null) {
|
|
3318
|
+
this._handleError('Cannot give an item an empty string or null as id.');
|
|
3319
|
+
return;
|
|
3320
|
+
}
|
|
3321
|
+
const item = this._registry[from];
|
|
3322
|
+
if (item) {
|
|
3323
|
+
delete this._registry[from];
|
|
3324
|
+
this._registry[to] = item;
|
|
3325
|
+
item.setId(to);
|
|
3326
|
+
}
|
|
3327
|
+
const renameInGroup = source => {
|
|
3328
|
+
const children = source.content;
|
|
3329
|
+
if (children) {
|
|
3330
|
+
for (let j = 0; j < children.length; j++) {
|
|
3331
|
+
const child = children[j];
|
|
3332
|
+
if (child.id === from || child['@id'] === from) {
|
|
3333
|
+
child.id = to;
|
|
3334
|
+
delete child['@id']; // Clean up backward compatability.
|
|
3335
|
+
}
|
|
3336
|
+
if (child.content) {
|
|
3337
|
+
renameInGroup(child);
|
|
3338
|
+
}
|
|
3339
|
+
}
|
|
3340
|
+
}
|
|
3341
|
+
};
|
|
3342
|
+
const items = this.getItems();
|
|
3343
|
+
for (let i = 0; i < items.length; i++) {
|
|
3344
|
+
const childItem = items[i];
|
|
3345
|
+
if (childItem instanceof Group) {
|
|
3346
|
+
renameInGroup(childItem._source);
|
|
3347
|
+
}
|
|
3348
|
+
}
|
|
3349
|
+
}
|
|
3350
|
+
getItemIds() {
|
|
3351
|
+
return Object.keys(this._registry);
|
|
3352
|
+
}
|
|
3353
|
+
getItemByProperty(property) {
|
|
3354
|
+
return this._registryByProperty[property];
|
|
3355
|
+
}
|
|
3356
|
+
detectTemplate(graph, uri, requiredItems) {
|
|
3357
|
+
return constructTemplate(graph, uri, this, requiredItems);
|
|
3358
|
+
}
|
|
3359
|
+
|
|
3360
|
+
/**
|
|
3361
|
+
* Bundle is an object containing:
|
|
3362
|
+
* path - can be a relative or absolute path to where the templates are/will be loaded from, optional.
|
|
3363
|
+
* source - a RDForms template object, mandatory.
|
|
3364
|
+
*
|
|
3365
|
+
* @param {Object} bundleSrc
|
|
3366
|
+
* @return {Bundle} the created bundle.
|
|
3367
|
+
*/
|
|
3368
|
+
registerBundle(bundle) {
|
|
3369
|
+
bundle.itemStore = this;
|
|
3370
|
+
const b = new Bundle(bundle);
|
|
3371
|
+
this._bundles.push(b);
|
|
3372
|
+
if (bundle.source && bundle.source.namespaces) {
|
|
3373
|
+
rdfjson_namespaceObject.namespaces.add(bundle.source.namespaces);
|
|
3374
|
+
}
|
|
3375
|
+
const templates = bundle.source.templates || bundle.source.auxilliary;
|
|
3376
|
+
if (templates instanceof Array) {
|
|
3377
|
+
this._createItems(templates, false, b);
|
|
3378
|
+
}
|
|
3379
|
+
if (typeof bundle.source.cachedChoices === 'object') {
|
|
3380
|
+
this._ontologyStore.importRegistry(bundle.source.cachedChoices);
|
|
3381
|
+
}
|
|
3382
|
+
return b;
|
|
3383
|
+
}
|
|
3384
|
+
getBundles() {
|
|
3385
|
+
return this._bundles;
|
|
3386
|
+
}
|
|
3387
|
+
|
|
3388
|
+
// Backward compatability
|
|
3389
|
+
createTemplate(source) {
|
|
3390
|
+
const b = this.registerBundle({
|
|
3391
|
+
source
|
|
3392
|
+
});
|
|
3393
|
+
return b.getRoot();
|
|
3394
|
+
}
|
|
3395
|
+
createTemplateFromChildren(children) {
|
|
3396
|
+
const childrenObj = (children || []).map(child => typeof child === 'string' ? this.getItem(child) : child);
|
|
3397
|
+
return new Group({
|
|
3398
|
+
source: {},
|
|
3399
|
+
children: childrenObj,
|
|
3400
|
+
itemStore: this
|
|
3401
|
+
});
|
|
3402
|
+
}
|
|
3403
|
+
setPriorities(priorities) {
|
|
3404
|
+
this.priorities = priorities;
|
|
3405
|
+
}
|
|
3406
|
+
|
|
3407
|
+
// eslint-disable-next-line class-methods-use-this
|
|
3408
|
+
createExtendedSource(origSource, extSource) {
|
|
3409
|
+
const newSource = Object.assign({}, origSource, extSource);
|
|
3410
|
+
if (extSource.id === undefined) {
|
|
3411
|
+
// If no new id is provided in the original source,
|
|
3412
|
+
// don't inherit the id of the extention as then
|
|
3413
|
+
// it will be overwrite it in the ItemStore
|
|
3414
|
+
delete newSource.id;
|
|
3415
|
+
}
|
|
3416
|
+
if (extSource.enhanced) {
|
|
3417
|
+
let keys;
|
|
3418
|
+
if (extSource.enhanced === true) {
|
|
3419
|
+
keys = Object.keys(origSource).concat(Object.keys(extSource));
|
|
3420
|
+
} else {
|
|
3421
|
+
keys = Object.keys(extSource.enhanced);
|
|
3422
|
+
}
|
|
3423
|
+
keys.forEach(key => {
|
|
3424
|
+
newSource[key] = deepMerge(origSource[key], extSource[key]);
|
|
3425
|
+
});
|
|
3426
|
+
}
|
|
3427
|
+
newSource._extendedSource = extSource;
|
|
3428
|
+
newSource.extends = null; // Avoid infinite recursion when creating the fleshed out item.
|
|
3429
|
+
delete newSource.children;
|
|
3430
|
+
return newSource;
|
|
3431
|
+
}
|
|
3432
|
+
|
|
3433
|
+
/**
|
|
3434
|
+
* At a minimum the source must contain a type, the rest can be changed later.
|
|
3435
|
+
*
|
|
3436
|
+
* @param source
|
|
3437
|
+
* @returns {*}
|
|
3438
|
+
*/
|
|
3439
|
+
createItem(source, forceClone, skipRegistration, bundle) {
|
|
3440
|
+
let item;
|
|
3441
|
+
const id = source.id || source['@id'];
|
|
3442
|
+
const type = source.type || source['@type'];
|
|
3443
|
+
if (source.extends) {
|
|
3444
|
+
// Explicit extends given
|
|
3445
|
+
const extItem = this._registry[source.extends];
|
|
3446
|
+
if (extItem == null) {
|
|
3447
|
+
this._handleError(`Cannot find item to extend with id: ${source.extends}`);
|
|
3448
|
+
}
|
|
3449
|
+
if (extItem) {
|
|
3450
|
+
const newSource = this.createExtendedSource(extItem.getSource(), source);
|
|
3451
|
+
return this.createItem(newSource, false, false, bundle);
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3454
|
+
if (type != null) {
|
|
3455
|
+
// If there is a type in the source then it means that the object is a new item.
|
|
3456
|
+
// eslint-disable-next-line default-case
|
|
3457
|
+
switch (type) {
|
|
3458
|
+
case 'text':
|
|
3459
|
+
item = new Text({
|
|
3460
|
+
source,
|
|
3461
|
+
itemStore: this,
|
|
3462
|
+
bundle
|
|
3463
|
+
});
|
|
3464
|
+
break;
|
|
3465
|
+
case 'choice':
|
|
3466
|
+
item = new Choice({
|
|
3467
|
+
source,
|
|
3468
|
+
itemStore: this,
|
|
3469
|
+
ontologyStore: this._ontologyStore,
|
|
3470
|
+
bundle
|
|
3471
|
+
});
|
|
3472
|
+
break;
|
|
3473
|
+
case 'group':
|
|
3474
|
+
item = new Group({
|
|
3475
|
+
source,
|
|
3476
|
+
children: null,
|
|
3477
|
+
itemStore: this,
|
|
3478
|
+
bundle
|
|
3479
|
+
}); // Lazy loading of children.
|
|
3480
|
+
break;
|
|
3481
|
+
case 'propertygroup':
|
|
3482
|
+
item = new PropertyGroup({
|
|
3483
|
+
source,
|
|
3484
|
+
children: null,
|
|
3485
|
+
itemStore: this,
|
|
3486
|
+
bundle
|
|
3487
|
+
}); // Lazy loading of children.
|
|
3488
|
+
break;
|
|
3489
|
+
}
|
|
3490
|
+
if (skipRegistration !== true) {
|
|
3491
|
+
if (source.property != null) {
|
|
3492
|
+
this._registryByProperty[source.property] = item;
|
|
3493
|
+
if (this.priorities && this.priorities[source.property] != null) {
|
|
3494
|
+
item.priority = this.priorities[source.property];
|
|
3495
|
+
}
|
|
3496
|
+
}
|
|
3497
|
+
if (id != null) {
|
|
3498
|
+
if (this._registry[id]) {
|
|
3499
|
+
console.log(`RDForms conflict with item id ${id}, overwriting item from bundle "${this._registry[id].getBundle()?.getPath() || ''}" with item from bundle "${item.getBundle()?.getPath() || ''}".`);
|
|
3500
|
+
}
|
|
3501
|
+
this._registry[id] = item;
|
|
3502
|
+
if (bundle != null) {
|
|
3503
|
+
bundle.addItem(item);
|
|
3504
|
+
}
|
|
3505
|
+
}
|
|
3506
|
+
}
|
|
3507
|
+
return item;
|
|
3508
|
+
}
|
|
3509
|
+
// No type means it is a reference, check that the referred item (via id) exists
|
|
3510
|
+
if (id == null) {
|
|
3511
|
+
this._handleError('Cannot create subitem, `type` for creating new or `id` for referencing external are required.');
|
|
3512
|
+
return;
|
|
3513
|
+
}
|
|
3514
|
+
if (this._registry[id] == null) {
|
|
3515
|
+
this._handleError(`Cannot find referenced subitem using identifier: ${id}`);
|
|
3516
|
+
return;
|
|
3517
|
+
}
|
|
3518
|
+
|
|
3519
|
+
// Clone if forceClone set to true or if the source contains non-id properties.
|
|
3520
|
+
if (forceClone === true || Object.keys(source).find(key => key !== 'id' && key !== '@id')) {
|
|
3521
|
+
const newSource = Object.assign(Object.assign({}, this._registry[id]._source), source);
|
|
3522
|
+
return this.createItem(newSource, false, true);
|
|
3523
|
+
}
|
|
3524
|
+
return this._registry[id];
|
|
3525
|
+
}
|
|
3526
|
+
removeItem(item, removereferences) {
|
|
3527
|
+
const b = item.getBundle();
|
|
3528
|
+
if (b != null) {
|
|
3529
|
+
b.removeItem(item);
|
|
3530
|
+
}
|
|
3531
|
+
if (item.getId() != null) {
|
|
3532
|
+
delete this._registry[item.getId()];
|
|
3533
|
+
}
|
|
3534
|
+
const prop = item.getProperty();
|
|
3535
|
+
if (prop != null && this._registryByProperty[prop] === item) {
|
|
3536
|
+
delete this._registryByProperty[prop];
|
|
3537
|
+
}
|
|
3538
|
+
if (removereferences) {
|
|
3539
|
+
// TODO
|
|
3540
|
+
}
|
|
3541
|
+
}
|
|
3542
|
+
|
|
3543
|
+
//= ==================================================
|
|
3544
|
+
// Private methods
|
|
3545
|
+
//= ==================================================
|
|
3546
|
+
_createItems(sourceArray, forceClone, bundle) {
|
|
3547
|
+
return sourceArray.map((child, index) => {
|
|
3548
|
+
// If child is not a object but a direct string reference,
|
|
3549
|
+
const childToUse = typeof child === 'string' ? sourceArray[index] = {
|
|
3550
|
+
id: child
|
|
3551
|
+
} : child;
|
|
3552
|
+
return this.createItem(childToUse, forceClone, false, bundle);
|
|
3553
|
+
}).filter(item => item);
|
|
3554
|
+
}
|
|
3555
|
+
_handleError(message) {
|
|
3556
|
+
if (this.handleErrorAs === 'throw') {
|
|
3557
|
+
throw new Error(message);
|
|
3558
|
+
}
|
|
3559
|
+
console[this.handleErrorAs](message);
|
|
3560
|
+
}
|
|
3561
|
+
}
|
|
3562
|
+
;// ./src/template/bundleLoader.js
|
|
3563
|
+
/* eslint-disable no-await-in-loop */
|
|
3564
|
+
/**
|
|
3565
|
+
* Check if there's any iterations left in a hypothetical array with 'length' given.
|
|
3566
|
+
*
|
|
3567
|
+
* @param {number} iteration
|
|
3568
|
+
* @param {number} length
|
|
3569
|
+
* @param {string} templateId
|
|
3570
|
+
* @throws
|
|
3571
|
+
*/
|
|
3572
|
+
const stopFetchingOrJustLog = (iteration, length, templateId) => {
|
|
3573
|
+
const message = `Fetching template bundle ${templateId} failed.`;
|
|
3574
|
+
if (iteration === length - 1) {
|
|
3575
|
+
throw Error(`${message} Cannot recover from this, please fix.`);
|
|
3576
|
+
} else {
|
|
3577
|
+
console.log(`${message} Will try to fetch from a fallback option.`);
|
|
3578
|
+
}
|
|
3579
|
+
};
|
|
3580
|
+
|
|
3581
|
+
/**
|
|
3582
|
+
* Return the first successfully fetched bundle from a list of urls or throw en error if none could be fetched
|
|
3583
|
+
*
|
|
3584
|
+
* @param {Array<String>} urls
|
|
3585
|
+
* @returns {Promise<Response | never | void>}
|
|
3586
|
+
*/
|
|
3587
|
+
const fetchBundle = async urls => {
|
|
3588
|
+
const totalUrls = urls.length;
|
|
3589
|
+
let response;
|
|
3590
|
+
let bundle;
|
|
3591
|
+
let path;
|
|
3592
|
+
for (let i = 0; i < totalUrls; i++) {
|
|
3593
|
+
// try to fetch the bundle, fails only if there's some network error. A 404 is not an error
|
|
3594
|
+
path = urls[i];
|
|
3595
|
+
try {
|
|
3596
|
+
response = await fetch(path);
|
|
3597
|
+
} catch (e) {
|
|
3598
|
+
throw Error(`A network error ocurred while trying to fetch bundle ${path}`);
|
|
3599
|
+
}
|
|
3600
|
+
|
|
3601
|
+
// check if we got a 2xx
|
|
3602
|
+
if (response && response.ok) {
|
|
3603
|
+
// check if what we got back looks like json and try to parse
|
|
3604
|
+
// if all good, then you're done
|
|
3605
|
+
// if it cannot parse, then fail soft or hard depending on if there's a fallback left to check
|
|
3606
|
+
try {
|
|
3607
|
+
const contentType = response.headers.has('content-type') && response.headers.get('content-type');
|
|
3608
|
+
if (contentType && contentType.includes('application/json')) {
|
|
3609
|
+
bundle = await response.json();
|
|
3610
|
+
break;
|
|
3611
|
+
} else {
|
|
3612
|
+
throw new Error(`Failed fetching template ${path}. Expected a JSON file and got ${contentType}`);
|
|
3613
|
+
}
|
|
3614
|
+
} catch (e) {
|
|
3615
|
+
stopFetchingOrJustLog(i, totalUrls, path);
|
|
3616
|
+
}
|
|
3617
|
+
// got back something that's not a 2xx
|
|
3618
|
+
} else {
|
|
3619
|
+
stopFetchingOrJustLog(i, totalUrls, path);
|
|
3620
|
+
}
|
|
3621
|
+
}
|
|
3622
|
+
return {
|
|
3623
|
+
path,
|
|
3624
|
+
source: bundle
|
|
3625
|
+
};
|
|
3626
|
+
};
|
|
3627
|
+
|
|
3628
|
+
/**
|
|
3629
|
+
* Fetch or if loaded just wrap it in Promise.resolve
|
|
3630
|
+
* @param bundles
|
|
3631
|
+
* @returns {Promise<*>}
|
|
3632
|
+
*/
|
|
3633
|
+
const promisifyBundles = bundles => bundles.map(bundle => bundle instanceof Array ? fetchBundle(bundle) : Promise.resolve({
|
|
3634
|
+
source: bundle
|
|
3635
|
+
}));
|
|
3636
|
+
|
|
3637
|
+
/**
|
|
3638
|
+
* Register bundle templates
|
|
3639
|
+
*
|
|
3640
|
+
* @param {ItemStore} itemStore
|
|
3641
|
+
* @param {array} bundles
|
|
3642
|
+
*/
|
|
3643
|
+
const registerBundles = (itemStore, bundles = []) => bundles.map(bundle => itemStore.registerBundle(bundle));
|
|
3644
|
+
|
|
3645
|
+
/**
|
|
3646
|
+
*
|
|
3647
|
+
* @param {ItemStore} itemStore
|
|
3648
|
+
* @param bundlePaths {Array<Object|String>} an array of object (bundles) or paths
|
|
3649
|
+
* @param callback
|
|
3650
|
+
*/
|
|
3651
|
+
/* harmony default export */ const bundleLoader = (async (itemStore, bundlePaths = [], callback = () => {}) => {
|
|
3652
|
+
if (bundlePaths.length === 0 && callback) {
|
|
3653
|
+
// nothing to load
|
|
3654
|
+
callback([]);
|
|
3655
|
+
}
|
|
3656
|
+
|
|
3657
|
+
// Fetch or if loaded just wrap it in Promise.resolve
|
|
3658
|
+
const bundlePromises = promisifyBundles(bundlePaths);
|
|
3659
|
+
const loadedBundles = await Promise.all(bundlePromises);
|
|
3660
|
+
const registeredBundles = registerBundles(itemStore, loadedBundles);
|
|
3661
|
+
callback(registeredBundles); // TODO remove; should be deprecated
|
|
3662
|
+
return registeredBundles;
|
|
3663
|
+
});
|
|
3664
|
+
;// ./src/model/validate.js
|
|
3665
|
+
|
|
3666
|
+
|
|
3667
|
+
|
|
3668
|
+
const _clearMatchingCodes = binding => {
|
|
3669
|
+
binding.setMatchingCode(CODES.OK);
|
|
3670
|
+
if (binding.getItem().getType() === 'group') {
|
|
3671
|
+
binding.getChildBindings().forEach(childBinding => _clearMatchingCodes(childBinding));
|
|
3672
|
+
}
|
|
3673
|
+
};
|
|
3674
|
+
|
|
3675
|
+
/**
|
|
3676
|
+
* Generates a report for the given binding. Below is an example of a report:
|
|
3677
|
+
* {
|
|
3678
|
+
* errors: [{
|
|
3679
|
+
* parentBinding: parent_group_binding_instance
|
|
3680
|
+
* item: item_instance_where_error_is,
|
|
3681
|
+
* code: "many"
|
|
3682
|
+
* }, ...],
|
|
3683
|
+
* warnings: [{
|
|
3684
|
+
* parentBinding: parent_group_binding_instance
|
|
3685
|
+
* item: item_instance_where_warning_is,
|
|
3686
|
+
* code: "few"
|
|
3687
|
+
* }, ...],
|
|
3688
|
+
* deprecated: [binding_instance_of_deprecated_value, ...]
|
|
3689
|
+
* }
|
|
3690
|
+
*
|
|
3691
|
+
* @type {Object}
|
|
3692
|
+
*/
|
|
3693
|
+
const bindingReport = (groupbinding, reportObj) => {
|
|
3694
|
+
let _reportObj = reportObj;
|
|
3695
|
+
if (_reportObj == null) {
|
|
3696
|
+
_reportObj = {
|
|
3697
|
+
errors: [],
|
|
3698
|
+
warnings: [],
|
|
3699
|
+
deprecated: []
|
|
3700
|
+
};
|
|
3701
|
+
} else {
|
|
3702
|
+
_reportObj.errors = _reportObj.errors || [];
|
|
3703
|
+
_reportObj.warnings = _reportObj.warnings || [];
|
|
3704
|
+
_reportObj.deprecated = _reportObj.deprecated || [];
|
|
3705
|
+
}
|
|
3706
|
+
// _clearMatchingCodes(groupbinding);
|
|
3707
|
+
// eslint-disable-next-line no-use-before-define
|
|
3708
|
+
return _createReport(groupbinding, _reportObj, true);
|
|
3709
|
+
};
|
|
3710
|
+
const _countValidBindings = bindings => {
|
|
3711
|
+
let counter = 0;
|
|
3712
|
+
for (let i = 0; i < bindings.length; i++) {
|
|
3713
|
+
if (bindings[i].isValid()) {
|
|
3714
|
+
counter += 1;
|
|
3715
|
+
}
|
|
3716
|
+
}
|
|
3717
|
+
return counter;
|
|
3718
|
+
};
|
|
3719
|
+
const updateViaCardinalityTracker = (bindings, code) => {
|
|
3720
|
+
if (bindings.length > 0) {
|
|
3721
|
+
const cardTr = bindings[0].getCardinalityTracker();
|
|
3722
|
+
cardTr.setCode(code);
|
|
3723
|
+
}
|
|
3724
|
+
};
|
|
3725
|
+
const doNotProceedFurther = (groupBinding, childItem) => {
|
|
3726
|
+
// Don't check further if the childItem is deprecated
|
|
3727
|
+
if (childItem.hasStyle('deprecated')) {
|
|
3728
|
+
return true;
|
|
3729
|
+
}
|
|
3730
|
+
|
|
3731
|
+
// Don't check further if the binding is hidden due to missing dependencies
|
|
3732
|
+
const childPath = childItem.getDeps();
|
|
3733
|
+
if (childPath) {
|
|
3734
|
+
const fromBinding = findBindingRelativeToParentBinding(groupBinding, childPath);
|
|
3735
|
+
if (!matchPathBelowBinding(fromBinding, childPath)) {
|
|
3736
|
+
return true;
|
|
3737
|
+
}
|
|
3738
|
+
}
|
|
3739
|
+
return false;
|
|
3740
|
+
};
|
|
3741
|
+
const _createReport = (groupbinding, report, firstLevel) => {
|
|
3742
|
+
if (groupbinding.getMatchingCode() !== model_CODES.OK) {
|
|
3743
|
+
return undefined;
|
|
3744
|
+
}
|
|
3745
|
+
const groupitem = groupbinding.getItem();
|
|
3746
|
+
|
|
3747
|
+
// Abort check if the groupbinding is hidden due to a missing dependency.
|
|
3748
|
+
// Check disabled since it is done for each child before recursive call
|
|
3749
|
+
/* const path = groupitem.getDeps();
|
|
3750
|
+
if (path && groupbinding.getParent() != null) {
|
|
3751
|
+
const fromBinding = findBindingRelativeToParentBinding(groupbinding.getParent(), path);
|
|
3752
|
+
if (!matchPathBelowBinding(fromBinding, path)) {
|
|
3753
|
+
return undefined;
|
|
3754
|
+
}
|
|
3755
|
+
} */
|
|
3756
|
+
|
|
3757
|
+
if (firstLevel === true || groupbinding.isValid() || groupitem.getProperty() == null || groupitem.hasStyle('atLeastOneChild') || groupitem.hasStyle('exactlyOneChild')) {
|
|
3758
|
+
const childrenItems = groupitem.getChildren();
|
|
3759
|
+
|
|
3760
|
+
// disjoint is deprecated in favour of atMostOneChild
|
|
3761
|
+
if (groupitem.hasStyle('disjoint') || groupitem.hasStyle('atMostOneChild') || groupitem.hasStyle('atLeastOneChild') || groupitem.hasStyle('exactlyOneChild')) {
|
|
3762
|
+
const bindings = groupbinding.getChildBindings();
|
|
3763
|
+
const nrOfValid = _countValidBindings(bindings);
|
|
3764
|
+
let code;
|
|
3765
|
+
if (nrOfValid > 1 && (groupitem.hasStyle('disjoint') || groupitem.hasStyle('atMostOneChild'))) {
|
|
3766
|
+
code = model_CODES.AT_MOST_ONE_CHILD;
|
|
3767
|
+
} else if (nrOfValid !== 1 && groupitem.hasStyle('exactlyOneChild')) {
|
|
3768
|
+
code = model_CODES.EXACTLY_ONE_CHILD;
|
|
3769
|
+
} else if (nrOfValid === 0 && groupitem.hasStyle('atLeastOneChild')) {
|
|
3770
|
+
code = model_CODES.AT_LEAST_ONE_CHILD;
|
|
3771
|
+
}
|
|
3772
|
+
if (code) {
|
|
3773
|
+
updateViaCardinalityTracker([groupbinding], code);
|
|
3774
|
+
// groupbinding.setMatchingCode(code);
|
|
3775
|
+
// Correct to set only on first child?
|
|
3776
|
+
if (childrenItems.length > 0) {
|
|
3777
|
+
report.errors.push({
|
|
3778
|
+
parentBinding: groupbinding,
|
|
3779
|
+
item: childrenItems[0],
|
|
3780
|
+
code
|
|
3781
|
+
});
|
|
3782
|
+
}
|
|
3783
|
+
}
|
|
3784
|
+
} else {
|
|
3785
|
+
groupbinding.getItemGroupedChildBindings().forEach((bindings, index) => {
|
|
3786
|
+
const childItem = childrenItems[index];
|
|
3787
|
+
if (doNotProceedFurther(groupbinding, childItem)) {
|
|
3788
|
+
return;
|
|
3789
|
+
}
|
|
3790
|
+
if (childItem.getProperty() != null) {
|
|
3791
|
+
const nrOfValid = _countValidBindings(bindings);
|
|
3792
|
+
const card = childItem.getCardinality();
|
|
3793
|
+
if (card.min != null && card.min > nrOfValid) {
|
|
3794
|
+
report.errors.push({
|
|
3795
|
+
parentBinding: groupbinding,
|
|
3796
|
+
item: childItem,
|
|
3797
|
+
code: model_CODES.TOO_FEW_VALUES_MIN
|
|
3798
|
+
});
|
|
3799
|
+
updateViaCardinalityTracker(bindings, model_CODES.TOO_FEW_VALUES_MIN);
|
|
3800
|
+
/* let counter = 0;
|
|
3801
|
+
bindings.forEach((binding) => {
|
|
3802
|
+
if (!binding.isValid()) {
|
|
3803
|
+
if (counter < card.min) {
|
|
3804
|
+
counter += 1;
|
|
3805
|
+
binding.setMatchingCode(CODES.TOO_FEW_VALUES_MIN);
|
|
3806
|
+
}
|
|
3807
|
+
}
|
|
3808
|
+
}); */
|
|
3809
|
+
} else if (card.pref != null && card.pref > nrOfValid) {
|
|
3810
|
+
report.warnings.push({
|
|
3811
|
+
parentBinding: groupbinding,
|
|
3812
|
+
item: childItem,
|
|
3813
|
+
code: model_CODES.TOO_FEW_VALUES_PREF
|
|
3814
|
+
});
|
|
3815
|
+
// updateViaCardinalityTracker(bindings, CODES.TOO_FEW_VALUES_PREF);
|
|
3816
|
+
}
|
|
3817
|
+
if (card.max != null && card.max < nrOfValid) {
|
|
3818
|
+
report.errors.push({
|
|
3819
|
+
parentBinding: groupbinding,
|
|
3820
|
+
item: childItem,
|
|
3821
|
+
code: model_CODES.TOO_MANY_VALUES
|
|
3822
|
+
});
|
|
3823
|
+
updateViaCardinalityTracker(bindings, model_CODES.TOO_MANY_VALUES);
|
|
3824
|
+
/* let counter = 0;
|
|
3825
|
+
bindings.forEach((binding) => {
|
|
3826
|
+
if (binding.isValid()) {
|
|
3827
|
+
counter += 1;
|
|
3828
|
+
if (counter > card.max) {
|
|
3829
|
+
binding.setMatchingCode(CODES.TOO_MANY_VALUES);
|
|
3830
|
+
}
|
|
3831
|
+
}
|
|
3832
|
+
}); */
|
|
3833
|
+
}
|
|
3834
|
+
}
|
|
3835
|
+
}, undefined);
|
|
3836
|
+
}
|
|
3837
|
+
groupbinding.getChildBindings().forEach(binding => {
|
|
3838
|
+
const item = binding.getItem();
|
|
3839
|
+
if (binding.getMatchingCode() !== model_CODES.OK) {
|
|
3840
|
+
report.errors.push({
|
|
3841
|
+
parentBinding: binding,
|
|
3842
|
+
item,
|
|
3843
|
+
code: binding.getMatchingCode()
|
|
3844
|
+
});
|
|
3845
|
+
}
|
|
3846
|
+
if (item.hasStyle('deprecated')) {
|
|
3847
|
+
report.deprecated.push(binding);
|
|
3848
|
+
}
|
|
3849
|
+
if (!doNotProceedFurther(groupbinding, item)) {
|
|
3850
|
+
// Recursive step
|
|
3851
|
+
if (item.getType() === 'group') {
|
|
3852
|
+
_createReport(binding, report);
|
|
3853
|
+
}
|
|
3854
|
+
}
|
|
3855
|
+
});
|
|
3856
|
+
}
|
|
3857
|
+
return report;
|
|
3858
|
+
};
|
|
3859
|
+
|
|
3860
|
+
/**
|
|
3861
|
+
* Generates a report for all resources identified. Resources are identified
|
|
3862
|
+
* according to their type. If a resource is present in the graph but its type is not
|
|
3863
|
+
* in the type2template map it will not be in the report.
|
|
3864
|
+
* Each resource will be validated according to the template for its type.
|
|
3865
|
+
* Below is an example of a report:
|
|
3866
|
+
* {
|
|
3867
|
+
* errors: 5,
|
|
3868
|
+
* warnings: 10,
|
|
3869
|
+
* deprecated: 1,
|
|
3870
|
+
* mandatoryError: ["dcat:Dataset"]
|
|
3871
|
+
* resources: [
|
|
3872
|
+
* {
|
|
3873
|
+
* uri: "http://example.com",
|
|
3874
|
+
* type: "vcard:Kind",
|
|
3875
|
+
* template: "vc:Kind",
|
|
3876
|
+
* errors: [{
|
|
3877
|
+
* path: "vcard:hasAddress > vcard:hasStreetAddress",
|
|
3878
|
+
* code: "few"
|
|
3879
|
+
* }],
|
|
3880
|
+
* warnings: [{
|
|
3881
|
+
* path: "vcard:hasFN",
|
|
3882
|
+
* code: "few"
|
|
3883
|
+
* }],
|
|
3884
|
+
* deprecated: [https://challengesgov.se/challenge-sthlm/
|
|
3885
|
+
* "vcard:fn"
|
|
3886
|
+
* ]
|
|
3887
|
+
* },
|
|
3888
|
+
* ...
|
|
3889
|
+
* ]
|
|
3890
|
+
* }
|
|
3891
|
+
*
|
|
3892
|
+
* @param {rdfjson/Graph} graph an rdf graph against which all validation is done
|
|
3893
|
+
* @param {Object} type2template a map between each type to check for and the template to use for validation
|
|
3894
|
+
* (the type may be given with namespace abbreviations)
|
|
3895
|
+
* @param {Array} mandatoryTypes an array of types to check that there are instances for
|
|
3896
|
+
* @return {Object} a report of the validity of the graph
|
|
3897
|
+
*/
|
|
3898
|
+
const graphReport = (graph, type2template, mandatoryTypes = []) => {
|
|
3899
|
+
const type2resources = {};
|
|
3900
|
+
const allResources = {};
|
|
3901
|
+
let template;
|
|
3902
|
+
let rr;
|
|
3903
|
+
const report = {
|
|
3904
|
+
errors: 0,
|
|
3905
|
+
warnings: 0,
|
|
3906
|
+
deprecated: 0,
|
|
3907
|
+
resources: []
|
|
3908
|
+
};
|
|
3909
|
+
Object.keys(type2template).forEach(type => {
|
|
3910
|
+
// eslint-disable-next-line no-use-before-define
|
|
3911
|
+
const resources = _findResources(graph, type);
|
|
3912
|
+
type2resources[type] = resources;
|
|
3913
|
+
resources.forEach(resource => {
|
|
3914
|
+
allResources[resource] = true;
|
|
3915
|
+
});
|
|
3916
|
+
});
|
|
3917
|
+
Object.keys(type2resources).forEach(type => {
|
|
3918
|
+
template = type2template[type];
|
|
3919
|
+
type2resources[type].forEach(resource => {
|
|
3920
|
+
// eslint-disable-next-line no-use-before-define
|
|
3921
|
+
rr = _resourceReport(resource, graph, template, allResources);
|
|
3922
|
+
rr.uri = resource;
|
|
3923
|
+
rr.type = type;
|
|
3924
|
+
rr.template = template.getId();
|
|
3925
|
+
report.resources.push(rr);
|
|
3926
|
+
report.errors += rr.errors.length;
|
|
3927
|
+
report.warnings += rr.warnings.length;
|
|
3928
|
+
report.deprecated += rr.deprecated.length;
|
|
3929
|
+
});
|
|
3930
|
+
});
|
|
3931
|
+
const mandatoryError = [];
|
|
3932
|
+
mandatoryTypes.forEach(mt => {
|
|
3933
|
+
if (type2resources[mt].length === 0) {
|
|
3934
|
+
mandatoryError.push(mt);
|
|
3935
|
+
}
|
|
3936
|
+
});
|
|
3937
|
+
if (mandatoryError.length > 0) {
|
|
3938
|
+
report.mandatoryError = mandatoryError;
|
|
3939
|
+
report.errors += mandatoryError.length;
|
|
3940
|
+
}
|
|
3941
|
+
return report;
|
|
3942
|
+
};
|
|
3943
|
+
const _findResources = (graph, cls) => graph.find(null, 'rdf:type', cls).map(stmt => stmt.getSubject());
|
|
3944
|
+
const _includeIssue = (binding, resource, otherResources) => {
|
|
3945
|
+
let pb = binding;
|
|
3946
|
+
while (true) {
|
|
3947
|
+
const uri = pb.getChildrenRootUri ? pb.getChildrenRootUri() : pb.getParent().getChildrenRootUri();
|
|
3948
|
+
if (uri === resource) {
|
|
3949
|
+
return true;
|
|
3950
|
+
} else if (otherResources[uri]) {
|
|
3951
|
+
return false;
|
|
3952
|
+
}
|
|
3953
|
+
pb = pb.getParent();
|
|
3954
|
+
}
|
|
3955
|
+
};
|
|
3956
|
+
const _createPath = (binding, item) => {
|
|
3957
|
+
let _binding = binding;
|
|
3958
|
+
const path = [];
|
|
3959
|
+
if (item.getProperty() != null && _binding.getItem() !== item) {
|
|
3960
|
+
path.push(rdfjson_namespaceObject.namespaces.shorten(item.getProperty()));
|
|
3961
|
+
}
|
|
3962
|
+
while (true) {
|
|
3963
|
+
if (_binding.getItem().getProperty() != null) {
|
|
3964
|
+
path.push(rdfjson_namespaceObject.namespaces.shorten(_binding.getItem().getProperty()));
|
|
3965
|
+
}
|
|
3966
|
+
if (_binding.getParent() == null) {
|
|
3967
|
+
break;
|
|
3968
|
+
} else {
|
|
3969
|
+
_binding = _binding.getParent();
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
path.reverse();
|
|
3973
|
+
return path.join(' > ');
|
|
3974
|
+
};
|
|
3975
|
+
const _filterReport = (report, resource, otherResources) => {
|
|
3976
|
+
const {
|
|
3977
|
+
errors,
|
|
3978
|
+
warnings,
|
|
3979
|
+
deprecated
|
|
3980
|
+
} = report;
|
|
3981
|
+
report.errors = errors.filter(err => _includeIssue(err.parentBinding, resource, otherResources));
|
|
3982
|
+
report.warnings = warnings.filter(warn => _includeIssue(warn.parentBinding, resource, otherResources));
|
|
3983
|
+
report.deprecated = deprecated.filter(depr => _includeIssue(depr, resource, otherResources));
|
|
3984
|
+
};
|
|
3985
|
+
const _createDepPath = dep => `${_createPath(dep.getParent(), dep.getItem())} > ${dep.getValue()}`;
|
|
3986
|
+
const _simplifyReport = report => {
|
|
3987
|
+
const {
|
|
3988
|
+
errors,
|
|
3989
|
+
warnings,
|
|
3990
|
+
deprecated
|
|
3991
|
+
} = report;
|
|
3992
|
+
report.errors = errors.map(err => ({
|
|
3993
|
+
path: _createPath(err.parentBinding, err.item),
|
|
3994
|
+
code: err.code
|
|
3995
|
+
}));
|
|
3996
|
+
report.warnings = warnings.map(warn => ({
|
|
3997
|
+
path: _createPath(warn.parentBinding, warn.item),
|
|
3998
|
+
code: warn.code
|
|
3999
|
+
}));
|
|
4000
|
+
report.deprecated = deprecated.map(dep => _createDepPath(dep));
|
|
4001
|
+
};
|
|
4002
|
+
const _resourceReport = (resource, graph, template, ignoreResources) => {
|
|
4003
|
+
const binding = fuzzyMatch(graph, resource, template);
|
|
4004
|
+
const report = bindingReport(binding);
|
|
4005
|
+
_filterReport(report, resource, ignoreResources || {});
|
|
4006
|
+
_simplifyReport(report);
|
|
4007
|
+
return report;
|
|
4008
|
+
};
|
|
4009
|
+
|
|
4010
|
+
/* harmony default export */ const validate = ({
|
|
4011
|
+
// TODO @valentino don't export default. Used in EntryScape
|
|
4012
|
+
graphReport,
|
|
4013
|
+
bindingReport
|
|
4014
|
+
});
|
|
4015
|
+
;// ./main.node.js
|
|
4016
|
+
// import 'node-fetch';
|
|
4017
|
+
|
|
4018
|
+
global.fetch = (__webpack_require__(229)["default"]);
|
|
4019
|
+
|
|
4020
|
+
|
|
4021
|
+
|
|
4022
|
+
|
|
4023
|
+
|
|
4024
|
+
|
|
4025
|
+
module.exports = __webpack_exports__;
|
|
4026
|
+
/******/ })()
|
|
4027
|
+
;
|