blob: 194aa2f99ad4bf556636d8347986144aeb564cb2 [file] [log] [blame]
Radek Krejci9b41f5b2018-01-31 14:17:50 +01001import { Injectable } from '@angular/core';
2
3import { Session} from './session';
4import { SessionsService } from './sessions.service';
5
6@Injectable()
Radek Krejci5a69fa32018-02-01 11:03:04 +01007export class ModificationsService {
Radek Krejci9b41f5b2018-01-31 14:17:50 +01008
9 constructor(private sessionsService: SessionsService) {}
10
11 createModificationsRecord(activeSession, path) {
12 if (!activeSession.modifications) {
13 activeSession.modifications = {};
14 }
15
16 if (!(path in activeSession.modifications)) {
17 activeSession.modifications[path] = {};
18 }
19 return activeSession.modifications[path];
20 }
21
22 getModificationsRecord(activeSession, path) {
23 if (!activeSession.modifications) {
24 return null;
25 }
26
27 if (!(path in activeSession.modifications)) {
28 return null;
29 }
30 return activeSession.modifications[path];
31 }
32
33 removeModificationsRecord(activeSession, path = null) {
34 if (!activeSession.modifications) {
35 return;
36 }
37
38 if (path && (path in activeSession.modifications)) {
39 delete activeSession.modifications[path];
40 }
41
42 if (!Object.keys(activeSession.modifications).length) {
43 delete activeSession.modifications;
44 }
45 }
46
47 setDirty(activeSession, node) {
48 if (!activeSession.modifications) {
49 return;
50 }
51
52 if (node['path'] in activeSession.modifications) {
53 node['dirty'] = true;
54 if (activeSession.modifications[node['path']]['type'] == 'change') {
55 activeSession.modifications[node['path']]['original'] = node['value'];
56 }
57 node['value'] = activeSession.modifications[node['path']]['value'];
58 }
59 /* recursion */
60 if ('children' in node) {
61 for (let child of node['children']) {
62 this.setDirty(activeSession, child);
63 }
64 }
65 }
66
67 setEdit(node, set = true) {
68 if (set && node['info']['datatypebase'] == 'empty') {
69 node['value'] = '';
70 return;
71 }
72 node['edit'] = set;
73 }
74
75 setLast(list) {
76 let last;
77 for (let iter of list) {
78 delete iter['last'];
79 last = iter;
80 }
81 last['last'] = true;
82 }
83
84 nodeParent(activeSession, node) {
85 if (node['path'] =='/') {
86 return null;
87 }
88
89 let match = false;
90 let parent = null;
91 let children = activeSession.data['children'];
92 let newChildren;
93
94 while (children || newChildren) {
95 match = false;
96
97 if (children) {
98 for (let iter of children) {
99 if (node['path'] == iter['path']) {
100 match = true;
101 children = null;
102 newChildren = null;
103 break;
104 } else if (node['path'].startsWith(iter['path'] + '/')) {
105 match = true;
106 parent = iter;
107 children = iter['children'];
108 if (('new' in node) && ('newChildren' in iter)) {
109 newChildren = iter['newChildren'];
110 } else {
111 newChildren = null;
112 }
113 break;
114 }
115 }
116 if (!match) {
117 children = null;
118 }
119 }
120 if (match) {
121 continue;
122 }
123 if (newChildren) {
124 for (let iter of newChildren) {
125 if (node['path'] == iter['path']) {
126 match = true;
127 children = null;
128 newChildren = null;
129 break;
130 } else if (node['path'].startsWith(iter['path'] + '/')) {
131 match = true;
132 parent = iter;
133 children = iter['children'];
134 if (('new' in node) && ('newChildren' in iter)) {
135 newChildren = iter['newChildren'];
136 } else {
137 newChildren = null;
138 }
139 break;
140 }
141 }
142 if (!match) {
143 children = null;
144 }
145 }
146 }
147
148 if (!parent) {
149 parent = activeSession.data;
150 }
151 return parent;
152 }
153
154 schemaName(parent, child):string {
155 if (parent['module'] != child['module']) {
156 return child['module'] + ':' + child['name'];
157 } else {
158 return child['name'];
159 }
160 }
161
162 delete(activeSession, node) {
163 if ('new' in node) {
164 /* removing newly created subtree */
165 let parent = this.nodeParent(activeSession, node);
166 if ('new' in parent) {
167 /* removing just a subtree of the created tree */
168 for (let i in parent['children']) {
169 if (parent['children'][i] == node) {
170 parent['children'].splice(i, 1);
171 break;
172 }
173 }
174 } else {
175 this.removeModificationsRecord(activeSession, node['path']);
176 for (let i in parent['newChildren']) {
177 if (parent['newChildren'][i]['path'] == node['path']) {
178 parent['newChildren'].splice(i, 1);
179 break;
180 }
181 }
182 if (!parent['newChildren'].length) {
183 delete parent['newChildren'];
184 }
185 }
186 } else {
187 let record = this.createModificationsRecord(activeSession, node['path']);
188
189 if (!('type' in record)) {
190 /* new record */
191 record['type'] = 'delete';
192 record['original'] = node;
193 node['deleted'] = true;
194 node['dirty'] = true;
195 } else if (record['type'] == 'change') {
196 record['type'] = 'delete';
197 node['value'] = record['original'];
198 delete record['original'];
199 delete record['value'];
200 node['deleted'] = true;
201 }
202 }
203 }
204
205 change(activeSession, node, leafValue) {
206 if (!('new' in node)) {
207 let record = this.createModificationsRecord(activeSession, node['path']);
208 if (!('type' in record)) {
209 console.log(record);
210 /* new record */
211 if (node['value'] == leafValue) {
212 /* no change to the original value */
213 this.setEdit(node, false);
214 this.removeModificationsRecord(activeSession);
215 return;
216 }
217 record['type'] = 'change';
218 record['original'] = node['value'];
219 record['value'] = leafValue;
220 node['dirty'] = true;
221 } else if (record['type'] == 'change' && record['original'] == leafValue) {
222 console.log(record);
223 /* change back to the original value, remove the change record */
224 this.removeModificationsRecord(activeSession, node['path']);
225 node['dirty'] = false;
226 } else {
227 console.log(record);
228 /* another change of existing change record */
229 record['value'] = leafValue;
230 node['dirty'] = true;
231 }
232 }
233
234 node['value'] = leafValue;
235 this.setEdit(node, false);
236 }
237
238 createOpen(schemas, node) {
239 //console.trace();
240 node['schemaChildren'] = schemas;
241 node['creatingChild'] = {};
242
243 if (schemas.length) {
244 if (('newChildren' in node) && node['newChildren'].length) {
245 delete node['newChildren'][node['newChildren'].length - 1]['last']
246 } else if (('children' in node) && node['children'].length) {
247 delete node['children'][node['children'].length - 1]['last'];
248 }
249 }
250 }
251
252 createClose(node, reason='abort') {
253 //console.trace();
254 if (reason == 'abort' && node['schemaChildren'].length) {
255 if (('newChildren' in node) && node['newChildren'].length) {
256 node['newChildren'][node['newChildren'].length - 1]['last'] = true;
257 } else if (('children' in node) && node['children'].length) {
258 node['children'][node['children'].length - 1]['last'] = true;
259 }
260 }
261 delete node['creatingChild'];
262 delete node['schemaChildren'];
263 }
264
265 create(activeSession, node, index) {
266 //console.trace();
267 let newNode = {};
268 newNode['new'] = true;
269 newNode['info'] = node['schemaChildren'][index];
270 if (node['path'] == '/') {
271 newNode['path'] = '/' + this.schemaName(node['info'], newNode['info']);
272 } else {
273 newNode['path'] = node['path'] + '/' + this.schemaName(node['info'], newNode['info']);
274 }
275 newNode['dirty'] = true;
276
277 if ('new' in node) {
278 if (!('children' in node)) {
279 node['children'] = []
280 }
281 node['children'].push(newNode)
282 } else {
283 if (!('newChildren' in node)) {
284 node['newChildren'] = [];
285 }
286 node['newChildren'].push(newNode);
287 }
288
289 switch(newNode['info']['type']) {
290 case 1: { /* container */
291 node['schemaChildren'].splice(index, 1);
292
293 newNode['children'] = [];
294 /* open creation dialog for nodes inside the created container */
295 this.sessionsService.childrenSchemas(activeSession.key, newNode['info']['path'], newNode).then(result => {
296 this.createOpen(result, newNode);
297 });
298 break;
299 }
300 case 4: { /* leaf */
301 node['schemaChildren'].splice(index, 1);
302
Radek Krejci62fec6a2018-02-06 10:24:09 +0100303 if ('default' in newNode['info']) {
304 newNode['value'] = newNode['info']['default'];
305 }
Radek Krejci9b41f5b2018-01-31 14:17:50 +0100306 this.setEdit(newNode, true)
307 break;
308 }
309 case 16: { /* list */
310 let search;
311 if ('new' in node) {
312 search = node['children'];
313 } else {
314 search = node['newChildren'];
315 }
316 let pos = 1;
317 if (search.length) {
318 for (let sibling of search) {
319 if (sibling['path'].substr(0, newNode['path'].length + 1) == newNode['path'] + '[') {
320 let n = parseInt(sibling['path'].substring(newNode['path'].length + 1));
321 if (n >= pos) {
322 pos = n + 1;
323 }
324 }
325 }
326 }
327 newNode['path'] = newNode['path'] + '[' + pos + ']';
328
329 newNode['children'] = [];
330 /* open creation dialog for nodes inside the created list */
331 this.sessionsService.childrenSchemas(activeSession.key, newNode['info']['path'], newNode).then(result => {
332 this.createOpen(result, newNode);
333
334 if (newNode['schemaChildren'].length) {
335 for (let i in newNode['schemaChildren']) {
336 if (!newNode['schemaChildren'][i]['key']) {
337 continue;
338 }
339 let newKey = {};
340 newKey['new'] = true;
341 newKey['info'] = newNode['schemaChildren'][i];
342 newKey['path'] = newNode['path'] + '/' + this.schemaName(newNode['info'], newKey['info']);
343 newKey['dirty'] = true;
344 this.setEdit(newKey, true)
345 newNode['children'].push(newKey)
346 newNode['schemaChildren'].splice(i, 1);
347 }
348 }
349 });
350
351 break;
352 }
353 }
354
355 if (!node['schemaChildren'].length) {
356 newNode['last'] = true;
357 this.createClose(node, 'success');
358 }
359
360 if (!('new' in node)) {
361 /* store the record about the newly created data */
362 let record = this.createModificationsRecord(activeSession, newNode['path']);
363 if (('type' in record) && record['type'] == 'delete') {
Radek Krejci62fec6a2018-02-06 10:24:09 +0100364 record['type'] = 'replace';
Radek Krejci9b41f5b2018-01-31 14:17:50 +0100365 delete record['original']['deleted'];
366 for (let i in node['children']) {
367 if (node['children'][i] == record['original']) {
368 node['children'].splice(i, 1);
369 break;
370 }
371 }
372 } else {
373 record['type'] = 'create';
Radek Krejci9b41f5b2018-01-31 14:17:50 +0100374 }
Radek Krejci62fec6a2018-02-06 10:24:09 +0100375 record['data'] = newNode;
Radek Krejci9b41f5b2018-01-31 14:17:50 +0100376 }
377 console.log(node)
378 }
379
380 cancelModification(activeSession, node = activeSession.data, recursion = true) {
381 if ('creatingChild' in node) {
382 delete node['creatingChild'];
383 }
384 if ('deleted' in node) {
385 node['dirty'] = false;
386 node['deleted'] = false;
387 }
388
389 if ('new' in node) {
390 /* removing newly created subtree */
391 let parent = this.nodeParent(activeSession, node);
392 if ('new' in parent) {
393 /* removing just a subtree of the created tree */
394 for (let i in parent['children']) {
395 if (parent['children'][i] == node) {
396 if (Number(i) > 0 && parent['children'][i]['last']) {
397 parent['children'][Number(i) - 1]['last'] = true;
398 }
399 parent['children'].splice(i, 1);
400 break;
401 }
402 }
403 } else {
404 this.removeModificationsRecord(activeSession, node['path']);
405 for (let i in parent['newChildren']) {
406 if (parent['newChildren'][i]['path'] == node['path']) {
407 if (Number(i) > 0 && parent['newChildren'][i]['last']) {
408 parent['newChildren'][Number(i) - 1]['last'] = true;
409 }
410 parent['newChildren'].splice(i, 1);
411 break;
412 }
413 }
414 if (!parent['newChildren'].length) {
415 delete parent['newChildren'];
416 }
417 }
418
419 if (node['info']['type'] == 1 || node['info']['type'] == 4) {
420 /* fix the list of nodes to create in parent */
421 let schemas;
422 if (!('schemaChildren' in parent)) {
423 schemas = [];
424 } else {
425 schemas = parent['schemaChildren'];
426 }
427 schemas.push(node['info']);
428 this.createOpen(schemas, parent)
429 }
430 } else if (activeSession.modifications) {
431 let record = this.getModificationsRecord(activeSession, node['path']);
432 if (record) {
433 node['dirty'] = false;
434 if (record['type'] == 'change') {
435 node['value'] = record['original'];
436 }
437 this.removeModificationsRecord(activeSession, node['path']);
438 if (!activeSession.modifications) {
439 return;
440 }
441 }
442 }
443
444 /* recursion */
445 if (recursion && 'children' in node) {
446 if ('newChildren' in node) {
447 for (let child of node['newChildren']) {
448 let record = this.getModificationsRecord(activeSession, child['path']);
449 if (record['type'] == 'change') {
450 if (node['children'].length) {
451 node['children'][node['children'].length - 1]['last'] = false;
452 }
453 record['original']['last'] = true;
454 node['children'].push(record['original'])
455 }
456 this.removeModificationsRecord(activeSession, child['path']);
457 }
458 delete node['newChildren'];
459 if (('children' in node) && node['children'].length) {
460 node['children'][node['children'].length - 1]['last'] = true;
461 }
462 }
463 let last;
464 for (let child of node['children']) {
465 this.cancelModification(activeSession, child);
466 delete child['last'];
467 last = child;
468 }
469 last['last'] = true;
470 }
471 }
Radek Krejci62fec6a2018-02-06 10:24:09 +0100472
473 private sortKeys(list) {
474 let key_index = 0;
475 for (let key of list['info']['keys']) {
476 for (let i in list['children']) {
477 if (list['children'][i]['info']['name'] == key && list['children'][i]['info']['module'] == list['info']['module']) {
478 let moved = list['children'].splice(i, 1);
479 list['children'].splice(key_index++, 0, moved[0]);
480 }
481 }
482 }
483 return key_index;
484 }
485
486 private resolveKeys(node, top = true): string {
487 if (node['info']['type'] == 16) {
488 if (!('children' in node) || !node['children'].length) {
489 return 'no key in ' + node['path'];
490 }
491 let count = this.sortKeys(node);
492 if (count != node['info']['keys'].length) {
493 return 'invalid number (' + count + ') of keys in ' + node['path'];
494 }
495 }
496 if (node['info']['type'] == 16 || node['info']['type'] == 1) {
497 for (let i in node['children']) {;
498 console.log(node['children'][i]);
499 if (node['children'][i]['info']['type'] == 4) {
500 /* leaf */
501 if (!('value' in node['children'][i])) {
502 if (node['children'][i]['info']['key']) {
503 return 'not confirmed value of the ' + node['children'][i]['path'] + ' key.';
504 }
505 console.log('not confirmed node ' + node['children'][i]['path'] + ', removing it');
506 node['children'].splice(i, 1);
507 }
508 } else {
509 /* recursion */
510 let msg = this.resolveKeys(node['children'][i], false);
511 if (msg) {
512 return msg;
513 }
514 }
515 }
516 }
517
518 /* update predicate in path */
519 if (node['info']['type'] == 16 && top) {
520 node['path'] = node['path'].slice(0, node['path'].lastIndexOf('['))
521 for (let i in node['info']['keys']) {
522 node['path'] = node['path'] + '[' + node['info']['keys'][i] + '=\'' + node['children'][i]['value'] + '\']'
523 }
524 }
525 return null;
526 }
527
528 applyModification(activeSession: Session) {
529 for (let mod in activeSession.modifications) {
530 //console.log(JSON.stringify(mod));
531 if (!('data' in activeSession.modifications[mod])) {
532 continue;
533 }
534 let err = this.resolveKeys(activeSession.modifications[mod]['data']);
535 if (err) {
536 console.log(err);
537 return new Promise((resolve, reject) => {resolve({'success':false,'error-msg': err})});
538 }
539 }
540 return this.sessionsService.commit(activeSession.key).then(result => {
541 if (result['success']) {
542 delete activeSession.modifications;
543 }
544 return result;
545 })
546 }
Radek Krejci9b41f5b2018-01-31 14:17:50 +0100547}