| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649 |
1x
1x
1x
1x
1x
1x
1x
1x
1x
233x
233x
233x
233x
233x
233x
1x
233x
233x
699x
699x
233x
466x
466x
466x
466x
1x
341x
387x
894x
33x
341x
319x
341x
1x
467x
467x
1x
233x
233x
467x
467x
467x
467x
233x
233x
233x
467x
467x
467x
233x
1x
240x
240x
240x
240x
240x
1x
240x
240x
240x
982x
982x
240x
1x
1964x
966x
966x
1x
240x
966x
240x
966x
240x
982x
982x
982x
982x
1x
240x
966x
3894x
15726x
1212x
1212x
1x
201x
8x
193x
193x
193x
193x
1x
192x
192x
201x
201x
192x
1x
463x
463x
162x
301x
301x
1x
40x
28x
40x
87x
87x
74x
74x
74x
74x
13x
12x
13x
1x
221x
166x
55x
55x
36x
8x
8x
28x
28x
28x
28x
31x
4x
4x
27x
7x
7x
7x
189x
189x
54x
7x
20x
28x
28x
28x
28x
28x
55x
1x
317x
317x
135x
182x
182x
8x
174x
178x
178x
178x
178x
178x
174x
1x
178x
203x
474x
474x
1x
1x
152x
152x
152x
152x
173x
173x
167x
6x
6x
152x
152x
1x
144x
144x
152x
152x
144x
1x
13x
13x
13x
23x
13x
10x
10x
10x
7x
7x
7x
13x
| /*!
GPII Ontology Server
Copyright 2012 OCAD University
Copyright 2014 Raising The Floor - International
Licensed under the New BSD license. You may not use this file except in
compliance with this License.
The research leading to these results has received funding from the European Union's
Seventh Framework Programme (FP7/2007-2013) under grant agreement no. 289016.
You may obtain a copy of the License at
https://github.com/GPII/universal/blob/master/LICENSE.txt
The ontologyHandler component is responsible for all the functionality related to ontologization,
etc., of preferences sets. It is not implemented as a kettle server and hence does _not_ expose any
URLs - this aspect is taken care of in the preferences server component.
*/
/* eslint strict: ["error", "function"] */
(function () {
"use strict";
var fluid = require("infusion"),
gpii = fluid.registerNamespace("gpii"),
fs = require("fs"),
path = require("path"),
JSON5 = require("json5");
fluid.registerNamespace("gpii.ontologyHandler.floydWarshall");
// TODO: Create an incarnation of the ontologyHandler that is not tied to node.js and can be tested
// the browser - it will need to be moved to using dataSources for I/O and have an "index" file rather than
// calling fs.readdirSync().
fluid.defaults("gpii.ontologyHandler", {
gradeNames: ["fluid.component"],
invokers: {
prefsToOntology: {
funcName: "gpii.ontologyHandler.prefsToOntology",
args: [ "{that}", "{arguments}.0", "{arguments}.1", "{arguments}.2" ]
},
rawPrefsToOntology: {
funcName: "gpii.ontologyHandler.rawPrefsToOntology",
args: [ "{that}", "{arguments}.0", "{arguments}.1" ]
},
addPrefsToRawPrefs: {
funcName: "gpii.ontologyHandler.addPrefsToRawPrefs",
args: [ "{that}", "{arguments}.0", "{arguments}.1", "{arguments}.2" ]
}
},
listeners: {
onCreate: "gpii.ontologyHandler.loadOntologies"
},
ontologyTransformationListFolder: "%gpii-universal/testData/ontologies/mappings",
ontologyMetadataFolder: "%gpii-universal/testData/ontologies", // directory with ontologies
members: {
ontologyTransformSpecs: {}, // will hold the actual ontology transformation definitions after read
ontologyTransformRoutes: {}, // will hold results of Floyd-Warshall for generating paths between specs
ontologyMetadata: {} // will hold the actual ontology metadata after files have been read
}
});
gpii.ontologyHandler.loadOntologies = function (that) {
// Check for re-occurence of GPII-1063
fluid.log("loadOntologies for " + that.id + " path " + fluid.pathForComponent(that));
// Load ontology transformations
var transformsFolder = fluid.module.resolvePath(that.options.ontologyTransformationListFolder);
var ontologyTransformsList = fs.readdirSync(transformsFolder); // Read the list of available ontologyTransformations
// Load the ontologies:
gpii.ontologyHandler.loadOntologyTransformSpecs(that, transformsFolder, ontologyTransformsList);
// Load the ontology metadata:
var metadataFolder = fluid.module.resolvePath(that.options.ontologyMetadataFolder);
gpii.ontologyHandler.loadOntologyMetadata(that, metadataFolder);
};
gpii.ontologyHandler.loadOntologyMetadata = function (that, metadataFolder) {
var metadataList = fs.readdirSync(metadataFolder);
fluid.each(metadataList, function (filename) {
var fullPath = metadataFolder + "/" + filename;
// Skip any directories we encounter:
if (fs.statSync(fullPath).isDirectory()) {
return;
}
fluid.log("ontologyHandler Loading filename " + filename);
var metadata = fs.readFileSync(fullPath, "utf8");
// get the ontology name by removing the extension of the filename
var ontologyName = path.parse(filename).name;
that.ontologyMetadata[ontologyName] = JSON5.parse(metadata);
});
};
/**
* Sanitizes the context sections and the outer metadata section by removing entries for which
* * Arrays are empty
* * Values are undefined
*
* @param prefs {Object} - the preferences to sanitize
* @return {Object} - the sanitized settings (according to above definition)
*/
gpii.ontologyHandler.sanitizePreferencesSet = function (prefs) {
fluid.each(prefs.contexts, function (context) {
fluid.each(context, function (val, key) {
if (val === undefined || (Array.isArray(val) && val.length === 0)) {
delete context[key];
}
});
});
if (prefs.metadata === undefined || (Array.isArray(prefs.metadata) && prefs.metadata.length === 0)) {
delete prefs.metadata;
}
return prefs;
};
/**
* The current format for naming ontology mapping files is source-target.
*
* @param ontologyMapping {String} mapping in source-target format.
* @return {Object} object with source and target keys with ontology names.
*/
gpii.ontologyHandler.parseOntologyMapping = function (ontologyMapping) {
var onts = ontologyMapping.split("-");
return {
source: onts[0],
target: onts[1]
};
};
/**
* Reads the files (or URLs) that contains the ontology transformation specs and stores it in
* the ontologyTransformSpecs propery of the gpii.ontologyHandler object
*
* @param that {Object} The gpii.ontologyHandler object. Should at minimum contain ontologySourceURL
* and ontologyTransformSpecs properties
* @param ontologyDir {string} path to folder containing the ontology transformation specs
* @param ontologyTransformList {Array} A list of available ontology transformations
*/
gpii.ontologyHandler.loadOntologyTransformSpecs = function (that, ontologyDir, ontologyTransformsList) {
var transformSpecs = {};
fluid.each(ontologyTransformsList, function (filename) {
fluid.log("ontologyHandler loading transformSpec filename " + filename);
var transformSpec = fs.readFileSync(ontologyDir + "/" + filename, "utf8");
// get the ontology name by removing the extension of the filename
var ontologyPair = path.parse(filename).name;
transformSpecs[ontologyPair] = JSON5.parse(transformSpec);
});
that.ontologyTransformSpecs = transformSpecs;
// Generate Ontology Routes
var edges = [];
fluid.each(transformSpecs, function (spec, key) {
// The transforms are bidirectional so we add both directed edges
var mapping = gpii.ontologyHandler.parseOntologyMapping(key);
edges.push(mapping);
edges.push({
source: mapping.target,
target: mapping.source
});
});
that.ontologyTransformRoutes =
gpii.ontologyHandler.floydWarshall.fullAlgorithmWithReconstruction(edges);
};
/**
* Implementation of the Floyd-Warshall algorithm to determine the shortest
* paths between a set of named edges. This is mostly generic, but currently
* used in generating a path between multiple linked ontologies.
* https://en.wikipedia.org/wiki/Floyd–Warshall_algorithm#Path_reconstruction
* Currently assumes that all edges are weight 1.
*
* @param edges {Array} An array of arrays of length 2, consisting of the named
* edges such as [['A','B'], ['B','C']]
* @return {Object} An object containing the necessary structures to resolve
* paths using the floydWarshallPath function. Contains 4 members:
* vertices (Object) key/value pair where key is the vertex name, and value
* is an integer to be used in the algorithm marking it as a vertex.
* For the ontology path use case, the vertex is the name of the
* ontology.
* numVertices (Integer) Convenience variable for the total number of vertices(ontologies).
* dist (2D Array) dist and next are matrices used in the algorithm, see
* documentation link above for algorithm details.
* next (2D Array) see dist above
*/
gpii.ontologyHandler.floydWarshall.fullAlgorithmWithReconstruction = function ( edges ) {
var data = {};
gpii.ontologyHandler.floydWarshall.algorithmInitVertices(data, edges);
gpii.ontologyHandler.floydWarshall.algorithmInitMatrices(data, edges);
gpii.ontologyHandler.floydWarshall.algorithmCore(data.dist, data.next, data.numVertices);
return data;
};
/**
* Internal Use only
* Function used in gpii.ontologyHandler.floydWarshall.fullAlgorithmWithReconstruction
* to initialize the mapping of vertices named by ontology to integers used in
* the algorithm.
*
* @param data {Object} Data structure used in main function.
* @param edges {Array} List of edges where each is an Object contains a directed source and
* target parameter.
* @return Does not a return a value, operates directly on the data structure.
*/
gpii.ontologyHandler.floydWarshall.algorithmInitVertices = function (data, edges) {
data.vertices = {};
var record = {
hash: data.vertices,
count: 0
};
fluid.each(edges, function (edge) {
gpii.ontologyHandler.floydWarshall.incrementingHashAppend(record, edge.source);
gpii.ontologyHandler.floydWarshall.incrementingHashAppend(record, edge.target);
});
data.numVertices = record.count;
};
/**
* Fill a hashmap as if it were a set, assigning each new unique entry a
* sequential integer value. A datastructure for each item is passed in that includes
* the hash we are incrementing and the current size.
*
* @param record {Object} A record containing two items, "hash", which is the current hashmap
* we are filling, and count, the current size of hash.
* example: { hash: ourKeyedObject, count: 3 }
* @param item {Object} The next value to add to the set. If this item is already
* in the set, the existing value for it will be unchanged.
* @return Does not return a value, operates directly on the hash.
*/
gpii.ontologyHandler.floydWarshall.incrementingHashAppend = function (record, item) {
if (!record.hash[item]) {
record.hash[item] = record.count;
record.count += 1;
}
};
/**
* Internal Use only
* Function used in gpii.ontologyHandler.floydWarshall.fullAlgorithmWithReconstruction
* to initialize the core matrices used in the algorithm, next and dist.
*
* @param data {Object} Data structure used in main function.
* @param edges {Array} List of edges where each is an Object contains a directed source and
* target parameter.
* @return Does not a return a value, operates directly on the data structure.
*/
gpii.ontologyHandler.floydWarshall.algorithmInitMatrices = function (data, edges) {
// Create |V|x|V| dist and next arrays and initialize to Infinity and null
data.dist = fluid.generate(data.numVertices, function () {
return fluid.generate(data.numVertices, Infinity, false);
}, true);
data.next = fluid.generate(data.numVertices, function () {
return fluid.generate(data.numVertices, null, false);
}, true);
fluid.each(edges, function (edge) {
var u = data.vertices[edge.source],
v = data.vertices[edge.target];
data.dist[u][v] = 1; // All weights currently 1
data.next[u][v] = v;
});
};
/**
* Internal Use only
* Function used in gpii.ontologyHandler.floydWarshall.fullAlgorithmWithReconstruction
* to perform the core algorithm of Floyd-Warshall.
*
* @param dist {2D array} See algorithm documentation
* @param next {2D array} See algorithm documentation
* @param numVertices {Integer} total number of vertices
* @return Does not a return a value, operates directly on the dist and next matrices.
*/
gpii.ontologyHandler.floydWarshall.algorithmCore = function (dist, next, numVertices) {
// Standard Floyd-Warshall implementation
for (var k = 0; k < numVertices; k++) {
for (var i = 0; i < numVertices; i++) {
for (var j = 0; j < numVertices; j++) {
if (dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
next[i][j] = next[i][k];
}
}
}
}
};
/**
* Uses the data generated in floydWarshall.fullAlgorithmWithReconstruction to
* generate the shortest path as an array of vertex names.
*
* @param data {Object} The data structure returned from
* floydWarshall.fullAlgorithmWithReconstruction
* @param v1 {String} The starting vertex
* @param v2 {String} The ending vertex
* @return {Array} An array of vertices for the shortest path, or an
* empty array if the path does not exist.
*/
gpii.ontologyHandler.floydWarshallPath = function ( data, v1, v2 ) {
if (data.vertices[v1] === undefined || data.vertices[v2] === undefined) {
return [];
}
var path = [],
u = data.vertices[v1],
v = data.vertices[v2];
if (data.next[u][v] === null) {
return path;
}
path.push(v1);
while (u !== v) {
u = data.next[u][v];
path.push(fluid.keyForValue(data.vertices, u));
}
return path;
};
/**
* Function for getting a transformation spec translating between ontologies.
*
* If the desired transformation spec from the requested ontology to the requested ontology
* can be produced, return it. Else return undefined.
*
* @param transformSpecs {Object} with all the available transformationSpecs. It should be keyed by:
* "<from>-<to>" values (where <from> and <to> are replaced by the keys of ontologies) and
* value should be the actual transformationSpec
* @param from {String} The key of the ontology to transform from
* @param to {String} The key of the ontology to transform to
*
* @return Transformation spec for translating the from ontology defined by param 'from' to
* ontology defined by param 'from'
*/
gpii.ontologyHandler.getTransformSpec = function (transformSpecs, from, to) {
var fromTo = from + "-" + to;
if (transformSpecs[fromTo] !== undefined) {
return transformSpecs[fromTo];
}
var toFrom = to + "-" + from;
return (transformSpecs[toFrom] === undefined) ? undefined :
fluid.model.transform.invertConfiguration(transformSpecs[toFrom]);
};
/**
* Function to collect all paths in the given object that has a certain value and return an
* array of these paths as el-paths (strings). This is useful when transforming metadata and
* we need to translate from an el-path in one ontology into an el-path in another ontology.
*
* The function is recursive and the 'target' parameter should be an array that will be
* populated by the found paths.
*
* @param obj {Object} to search through
* @param search {Object} The value to search for. Whenever this value is found, an el-path string leading to
* this location is saved to the target array
* @param target {Array} It will be modified by this function to contain the el-paths that contain the
* 'search' value
* @param pathArray {Array} Is used by the function to keep track of the current path. Should generally
* not be supplied to this function unless one wishes to have the paths that populate the
* the target being prefixed by some path.
*
* @return undefined - rather the array of collection will be built in the @target parameter
*/
gpii.ontologyHandler.collectPaths = function (obj, search, target, pathArray) {
if (!(pathArray instanceof Array)) {
pathArray = [];
}
fluid.each(obj, function (entry, key) {
pathArray.push(key);
if (entry === search) {
var path = fluid.pathUtil.composeSegments.apply(null, pathArray);
target.push(path);
pathArray.pop(key);
return;
}
if (!fluid.isPrimitive(entry)) {
gpii.ontologyHandler.collectPaths(entry, search, target, pathArray);
}
pathArray.pop(key);
});
};
/**
* Function to translate the metadata section of a preferences document - more specifically,
* the scope section of a metadata part of a preferences document. For individual values
* they will be translated as expected (i.e. by a lookup in the transformation rules). In case
* of wildcard characters, these are _only_ supported at the end of a string. A string containing
* only the wildcard ("*") will be kept as it, as this is globally applicable across
* ontologies. For other wildcards (eg. "display.*") they will be exploded when requested in a
* different ontology than they're specified in. So for example, a metadata section with a scope
* of "display.*" would first be exploded to all the preferences under the display object
* and then each of these would be transformed to the requested ontology.
*
* @param metadata {Array} The metadata section to be transformed
* @param transformSpec {Object} The transformation rules to be applied to the scopes
*
* @return the transformed metadata section
*/
gpii.ontologyHandler.transformMetadata = function (metadata, transformSpec) {
if (metadata === undefined) {
return undefined;
}
var togo = [];
fluid.each(metadata, function (entry) {
// if metadata section is application priority, we dont want to have the
// scope ontologized. We want to keep the flat-style application ID
if (entry.type === "priority") {
togo.push(entry);
return;
}
var tmpEntry = fluid.copy(entry),
toTransform = {},
transformed,
target;
tmpEntry.scope = [];
fluid.each(entry.scope, function (preference) {
if (preference === "*") { // ontology independent wildcard - leave as is
tmpEntry.scope.push(preference);
return;
}
if (fluid.model.transform.hasWildcard(preference)) {
// explode wildcard preference to paths, and set all of these in toTransform
var paths = fluid.model.transform.collectInputPaths(transformSpec);
var prefix = preference.substring(0, preference.indexOf("*"));
for (var i in paths) {
var path = paths[i];
// each matching path should be added to toTransform for trannsform
if (path.indexOf(prefix) === 0) {
fluid.set(toTransform, path, true, fluid.model.escapedSetConfig);
}
}
return;
}
fluid.set(toTransform, preference, true, fluid.model.escapedSetConfig);
});
// transform to new ontology and collect all paths with 'true' value - these equal
// the settings from the original scope array
transformed = fluid.model.transformWithRules(toTransform, transformSpec);
target = [];
gpii.ontologyHandler.collectPaths(transformed, true, target);
tmpEntry.scope = tmpEntry.scope.concat(target);
togo.push(tmpEntry);
});
return togo;
};
/**
* Takes a set of preferences in some ontology X and transforms they into some ontology Y. Note
* that the preferences given should NOT be keyed by an ontology ID.
*
* @param that {Component} A gpii.ontologyHandler component
* @param prefs {Object} The preferences set to be translated into a different ontology. The NP set
* passed should be in the ontology specified by the 'fromView' parameter and NOT keyed by
* an ontology ID.
* @param fromView {String} The ontology in which the NP set given in the 'prefs' parameter is formatted
* @param toView {String} The ontology to which the NP set should be transformed.
*
* @return The NP transformed into the ontology requested via the 'toView' parameter. If no
* valid transformation to that ontology was found, an empty NP set is returned
*/
gpii.ontologyHandler.prefsToOntology = function (that, prefs, fromView, toView) {
var transformSpec,
transformed = {
name: prefs.name,
contexts: prefs.contexts,
metadata: prefs.metadata
};
// if we're in same ontology, return as is
if (fromView === toView) {
return prefs;
}
// else get the appropriate shortest route between ontologies. Return {} if none found
var transformSpecRoute = gpii.ontologyHandler.floydWarshallPath(that.ontologyTransformRoutes,
fromView, toView);
if (transformSpecRoute.length === 0) {
return {};
}
// We are iterating through the array of specs by creating the dashed filenames.
// e.g transformSpecRoute is ['A', 'B', 'C'] so we would look up spec files
// 'A-B' and 'B-C'
for (var i = 0; i < transformSpecRoute.length - 1; i++) {
transformSpec = gpii.ontologyHandler.getTransformSpec(that.ontologyTransformSpecs,
transformSpecRoute[i], transformSpecRoute[i + 1]);
Iif (transformSpec === undefined) {
return {};
}
var contextTransform = gpii.ontologyHandler.makePreferencesContextTransform(transformSpec);
transformed.contexts = fluid.transform(transformed.contexts, contextTransform);
// translate the context independent metadata block:
transformed.metadata = gpii.ontologyHandler.transformMetadata(transformed.metadata, transformSpec);
}
return gpii.ontologyHandler.sanitizePreferencesSet(transformed);
};
/**
* Creates a transform function to be used with fluid.transform to take a
* set of preferences in one context and translate them to another
* ontology.
*
* @param transformSpec {Object} The transform spec used to go between ontologies.
* @return {Function} A transform function for the conversion suitable to
* use in fluid.transform.
*/
gpii.ontologyHandler.makePreferencesContextTransform = function (transformSpec) {
return function (context) {
// copy values directly or transform - depending on key
return fluid.transform(context, function (val, key) {
var rule = gpii.ontologyHandler.contextBlocks[key];
return rule === null ? val : fluid.invokeGlobalFunction(rule, [val, transformSpec]);
});
};
};
// Helper object for prefsToOntology function
gpii.ontologyHandler.contextBlocks = {
name: null,
priority: null,
preferences: "fluid.model.transformWithRules",
metadata: "gpii.ontologyHandler.transformMetadata",
conditions: null
};
/**
* Function to merge two NP sets.
*
* Takes two (non-raw) prefs set, merges them and returns the resulting set. One cannot just
* use fluid.extend, as this would override array entries in the metadata section, so a more
* informed merge is required. The preferences sets are expected to be in the same ontology
* and should not be keyed by ontology.
*
* @param master {Object} The first preference set to be merged
* @param secondary {Object} The second preference set to be merged
*
* @return {Object} A merged preferences set (non-raw).
*/
gpii.ontologyHandler.mergePrefsSets = function (master, secondary) {
master = fluid.copy(master);
secondary = fluid.copy(secondary);
master.name = master.name || secondary.name;
fluid.each(secondary.contexts, function (context, contextId) {
var mcontext = master.contexts[contextId];
// if not in master, just copy over from secondary:
if (!mcontext) {
master.contexts[contextId] = context;
} else {
// preferences and metadata should be the only things varying
fluid.extend(true, mcontext.preferences, context.preferences);
mcontext.metadata = fluid.makeArray(mcontext.metadata).concat(fluid.makeArray(context.metadata));
}
});
// merge outer metadata block
master.metadata = fluid.makeArray(master.metadata).concat(fluid.makeArray(secondary.metadata));
return master;
};
/**
* Function to take a raw preferences set and transform it into a desired ontology (incl. doing
* the required transformations for each of the preferences. metadata, etc). This function does
* not have any side-effects
*
* @param that {Object} A gpii.ontologyHandler object
* @param rawPrefs {Object} The raw preferences as can be found on the raw preferences server - NOT
* expected keyed by 'preferences'
* @param toView {String} The ontology to translate the raw NP set into
*
* @return Preferences in the ontology given in 'toView' parameter. This includes the preferences,
* metadata, etc., resulting from transforming all compatible preferences from different ontologies
*/
gpii.ontologyHandler.rawPrefsToOntology = function (that, rawPrefs, toView) {
var togo = {
contexts: {}
};
fluid.each(rawPrefs, function (fromContent, fromView) {
var transformed = gpii.ontologyHandler.prefsToOntology(that, fromContent, fromView, toView);
togo = gpii.ontologyHandler.mergePrefsSets(togo, transformed);
});
return gpii.ontologyHandler.sanitizePreferencesSet(togo);
};
/**
* Takes a preferences set X in a particular ontology and removes all settings in the various
* ontologies of the raw preferences set (Y) that are duplicates of the settings in the X
* preferences set. The result is a raw preferences set that contains the preferences of X in
* it's ontology, and no duplicates of settings of the X settings in the other ontologies. Note
* that the preferneces given will overwrite the entire entry of that ontology in the raw
* preferences set provided (i.e. it wont get merged with any settings already existing in that
* ontology)
*
* In other words: if any of the preferences that are being added are present in another
* ontology, that entry should be removed from the other ontology, to avoid duplication of the
* same term in different ontologies.
*
* @param that {Object} A gpii.ontologyHandler object
* @param prefs {Object} A preferences set in some ontology
* @param prefsView {String} The format of the preferences set given in the 'prefs' parameter
* @param rawPrefs {Object} The raw preferences set to be modified
*
* @return {Object} A raw preferences set that contains the settings of 'prefs' in the ontology
* 'prefsView', and has no settings in any of the other ontologies that can be transformed
* (or created/inferred) from the settings of 'prefs'
*/
gpii.ontologyHandler.addPrefsToRawPrefs = function (that, prefs, prefsView, rawPrefs) {
var togo = fluid.copy(rawPrefs);
// on an update, the consumer is responsible for sending _ALL_ relevant preferences in an
// ontology, so we can safely replace the existing preferences in that ontology
togo[prefsView] = prefs;
// for each of the remaining ontologies in the raw preferences, check if there are any
// preferences that are duplicate and remove them if that is the case:
fluid.each(togo, function (existingPrefs, existingView) {
if (existingView === prefsView) {
return;
}
// TODO - GPII-848: The preference merging needs to be improved once the user has the
// ability to specifically 'unset' settings in the PCP/PMT - i.e. decide that they do not
// want eg. font-size defined anymore.
// transform settings from the new preferences set (prefsView) into the ontology
// we're current looking at (i.e. 'existingView')
var transformed = gpii.ontologyHandler.prefsToOntology(that, prefs, prefsView, existingView);
// dig down to each of the prefs sets:
var contexts = transformed.contexts;
fluid.each(contexts, function (context, cname) {
Eif (context.preferences && existingPrefs.contexts[cname] && existingPrefs.contexts[cname].preferences) {
var filteredPrefs = gpii.ontologyHandler.utils.filterPrefs(
existingPrefs.contexts[cname].preferences,
context.preferences);
togo[existingView].contexts[cname].preferences = filteredPrefs;
}
});
});
// clean up prefs sets for each ontology
return fluid.transform(togo, gpii.ontologyHandler.sanitizePreferencesSet);
};
})();
|