| import { Injectable } from '@angular/core'; |
| |
| import { Session} from './session'; |
| import { SessionsService } from './sessions.service'; |
| |
| @Injectable() |
| export class ModificationsService { |
| |
| constructor(private sessionsService: SessionsService) {} |
| |
| createModificationsRecord(activeSession, path) { |
| if (!activeSession.modifications) { |
| activeSession.modifications = {}; |
| } |
| |
| if (!(path in activeSession.modifications)) { |
| activeSession.modifications[path] = {}; |
| } |
| return activeSession.modifications[path]; |
| } |
| |
| getModificationsRecord(activeSession, path) { |
| if (!activeSession.modifications) { |
| return null; |
| } |
| |
| if (!(path in activeSession.modifications)) { |
| return null; |
| } |
| return activeSession.modifications[path]; |
| } |
| |
| removeModificationsRecord(activeSession, path = null) { |
| if (!activeSession.modifications) { |
| return; |
| } |
| |
| if (path && (path in activeSession.modifications)) { |
| delete activeSession.modifications[path]; |
| } |
| |
| if (!Object.keys(activeSession.modifications).length) { |
| delete activeSession.modifications; |
| } |
| } |
| |
| setDirty(activeSession, node) { |
| if (!activeSession.modifications) { |
| return; |
| } |
| |
| if (node['path'] in activeSession.modifications) { |
| node['dirty'] = true; |
| if (activeSession.modifications[node['path']]['type'] == 'change') { |
| activeSession.modifications[node['path']]['original'] = node['value']; |
| } |
| node['value'] = activeSession.modifications[node['path']]['value']; |
| } |
| /* recursion */ |
| if ('children' in node) { |
| for (let child of node['children']) { |
| this.setDirty(activeSession, child); |
| } |
| } |
| } |
| |
| setEdit(node, set = true) { |
| if (set && node['info']['datatypebase'] == 'empty') { |
| node['value'] = ''; |
| return; |
| } |
| node['edit'] = set; |
| } |
| |
| setLast(list) { |
| let last; |
| for (let iter of list) { |
| delete iter['last']; |
| last = iter; |
| } |
| last['last'] = true; |
| } |
| |
| nodeParent(activeSession, node) { |
| if (node['path'] =='/') { |
| return null; |
| } |
| |
| let match = false; |
| let parent = null; |
| let children = activeSession.data['children']; |
| let newChildren; |
| |
| while (children || newChildren) { |
| match = false; |
| |
| if (children) { |
| for (let iter of children) { |
| if (node['path'] == iter['path']) { |
| match = true; |
| children = null; |
| newChildren = null; |
| break; |
| } else if (node['path'].startsWith(iter['path'] + '/')) { |
| match = true; |
| parent = iter; |
| children = iter['children']; |
| if (('new' in node) && ('newChildren' in iter)) { |
| newChildren = iter['newChildren']; |
| } else { |
| newChildren = null; |
| } |
| break; |
| } |
| } |
| if (!match) { |
| children = null; |
| } |
| } |
| if (match) { |
| continue; |
| } |
| if (newChildren) { |
| for (let iter of newChildren) { |
| if (node['path'] == iter['path']) { |
| match = true; |
| children = null; |
| newChildren = null; |
| break; |
| } else if (node['path'].startsWith(iter['path'] + '/')) { |
| match = true; |
| parent = iter; |
| children = iter['children']; |
| if (('new' in node) && ('newChildren' in iter)) { |
| newChildren = iter['newChildren']; |
| } else { |
| newChildren = null; |
| } |
| break; |
| } |
| } |
| if (!match) { |
| children = null; |
| } |
| } |
| } |
| |
| if (!parent) { |
| parent = activeSession.data; |
| } |
| return parent; |
| } |
| |
| schemaName(parent, child):string { |
| if (parent['module'] != child['module']) { |
| return child['module'] + ':' + child['name']; |
| } else { |
| return child['name']; |
| } |
| } |
| |
| delete(activeSession, node) { |
| if ('new' in node) { |
| /* removing newly created subtree */ |
| let parent = this.nodeParent(activeSession, node); |
| if ('new' in parent) { |
| /* removing just a subtree of the created tree */ |
| for (let i in parent['children']) { |
| if (parent['children'][i] == node) { |
| parent['children'].splice(i, 1); |
| break; |
| } |
| } |
| } else { |
| this.removeModificationsRecord(activeSession, node['path']); |
| for (let i in parent['newChildren']) { |
| if (parent['newChildren'][i]['path'] == node['path']) { |
| parent['newChildren'].splice(i, 1); |
| break; |
| } |
| } |
| if (!parent['newChildren'].length) { |
| delete parent['newChildren']; |
| } |
| } |
| } else { |
| let record = this.createModificationsRecord(activeSession, node['path']); |
| |
| if (!('type' in record)) { |
| /* new record */ |
| record['type'] = 'delete'; |
| record['original'] = node; |
| node['deleted'] = true; |
| node['dirty'] = true; |
| } else if (record['type'] == 'change') { |
| record['type'] = 'delete'; |
| node['value'] = record['original']; |
| delete record['original']; |
| delete record['value']; |
| node['deleted'] = true; |
| } |
| } |
| } |
| |
| change(activeSession, node, leafValue) { |
| if (!('new' in node)) { |
| let record = this.createModificationsRecord(activeSession, node['path']); |
| if (!('type' in record)) { |
| console.log(record); |
| /* new record */ |
| if (node['value'] == leafValue) { |
| /* no change to the original value */ |
| this.setEdit(node, false); |
| this.removeModificationsRecord(activeSession); |
| return; |
| } |
| record['type'] = 'change'; |
| record['original'] = node['value']; |
| record['value'] = leafValue; |
| node['dirty'] = true; |
| } else if (record['type'] == 'change' && record['original'] == leafValue) { |
| console.log(record); |
| /* change back to the original value, remove the change record */ |
| this.removeModificationsRecord(activeSession, node['path']); |
| node['dirty'] = false; |
| } else { |
| console.log(record); |
| /* another change of existing change record */ |
| record['value'] = leafValue; |
| node['dirty'] = true; |
| } |
| } |
| |
| node['value'] = leafValue; |
| this.setEdit(node, false); |
| } |
| |
| createOpen(schemas, node) { |
| //console.trace(); |
| node['schemaChildren'] = schemas; |
| node['creatingChild'] = {}; |
| |
| if (schemas.length) { |
| if (('newChildren' in node) && node['newChildren'].length) { |
| delete node['newChildren'][node['newChildren'].length - 1]['last'] |
| } else if (('children' in node) && node['children'].length) { |
| delete node['children'][node['children'].length - 1]['last']; |
| } |
| } |
| } |
| |
| createClose(node, reason='abort') { |
| //console.trace(); |
| if (reason == 'abort' && node['schemaChildren'].length) { |
| if (('newChildren' in node) && node['newChildren'].length) { |
| node['newChildren'][node['newChildren'].length - 1]['last'] = true; |
| } else if (('children' in node) && node['children'].length) { |
| node['children'][node['children'].length - 1]['last'] = true; |
| } |
| } |
| delete node['creatingChild']; |
| delete node['schemaChildren']; |
| } |
| |
| create(activeSession, node, index) { |
| //console.trace(); |
| let newNode = {}; |
| newNode['new'] = true; |
| newNode['info'] = node['schemaChildren'][index]; |
| if (node['path'] == '/') { |
| newNode['path'] = '/' + this.schemaName(node['info'], newNode['info']); |
| } else { |
| newNode['path'] = node['path'] + '/' + this.schemaName(node['info'], newNode['info']); |
| } |
| newNode['dirty'] = true; |
| |
| if ('new' in node) { |
| if (!('children' in node)) { |
| node['children'] = [] |
| } |
| node['children'].push(newNode) |
| } else { |
| if (!('newChildren' in node)) { |
| node['newChildren'] = []; |
| } |
| node['newChildren'].push(newNode); |
| } |
| |
| switch(newNode['info']['type']) { |
| case 1: { /* container */ |
| node['schemaChildren'].splice(index, 1); |
| |
| newNode['children'] = []; |
| /* open creation dialog for nodes inside the created container */ |
| this.sessionsService.childrenSchemas(activeSession.key, newNode['info']['path'], newNode).then(result => { |
| this.createOpen(result, newNode); |
| }); |
| break; |
| } |
| case 4: { /* leaf */ |
| node['schemaChildren'].splice(index, 1); |
| |
| if ('default' in newNode['info']) { |
| newNode['value'] = newNode['info']['default']; |
| } |
| this.setEdit(newNode, true) |
| break; |
| } |
| case 16: { /* list */ |
| let search; |
| if ('new' in node) { |
| search = node['children']; |
| } else { |
| search = node['newChildren']; |
| } |
| let pos = 1; |
| if (search.length) { |
| for (let sibling of search) { |
| if (sibling['path'].substr(0, newNode['path'].length + 1) == newNode['path'] + '[') { |
| let n = parseInt(sibling['path'].substring(newNode['path'].length + 1)); |
| if (n >= pos) { |
| pos = n + 1; |
| } |
| } |
| } |
| } |
| newNode['path'] = newNode['path'] + '[' + pos + ']'; |
| |
| newNode['children'] = []; |
| /* open creation dialog for nodes inside the created list */ |
| this.sessionsService.childrenSchemas(activeSession.key, newNode['info']['path'], newNode).then(result => { |
| this.createOpen(result, newNode); |
| |
| if (newNode['schemaChildren'].length) { |
| for (let i in newNode['schemaChildren']) { |
| if (!newNode['schemaChildren'][i]['key']) { |
| continue; |
| } |
| let newKey = {}; |
| newKey['new'] = true; |
| newKey['info'] = newNode['schemaChildren'][i]; |
| newKey['path'] = newNode['path'] + '/' + this.schemaName(newNode['info'], newKey['info']); |
| newKey['dirty'] = true; |
| this.setEdit(newKey, true) |
| newNode['children'].push(newKey) |
| newNode['schemaChildren'].splice(i, 1); |
| } |
| } |
| }); |
| |
| break; |
| } |
| } |
| |
| if (!node['schemaChildren'].length) { |
| newNode['last'] = true; |
| this.createClose(node, 'success'); |
| } |
| |
| if (!('new' in node)) { |
| /* store the record about the newly created data */ |
| let record = this.createModificationsRecord(activeSession, newNode['path']); |
| if (('type' in record) && record['type'] == 'delete') { |
| record['type'] = 'replace'; |
| delete record['original']['deleted']; |
| for (let i in node['children']) { |
| if (node['children'][i] == record['original']) { |
| node['children'].splice(i, 1); |
| break; |
| } |
| } |
| } else { |
| record['type'] = 'create'; |
| } |
| record['data'] = newNode; |
| } |
| console.log(node) |
| } |
| |
| cancelModification(activeSession, node = activeSession.data, recursion = true) { |
| if ('creatingChild' in node) { |
| delete node['creatingChild']; |
| } |
| if ('deleted' in node) { |
| node['dirty'] = false; |
| node['deleted'] = false; |
| } |
| |
| if ('new' in node) { |
| /* removing newly created subtree */ |
| let parent = this.nodeParent(activeSession, node); |
| if ('new' in parent) { |
| /* removing just a subtree of the created tree */ |
| for (let i in parent['children']) { |
| if (parent['children'][i] == node) { |
| if (Number(i) > 0 && parent['children'][i]['last']) { |
| parent['children'][Number(i) - 1]['last'] = true; |
| } |
| parent['children'].splice(i, 1); |
| break; |
| } |
| } |
| } else { |
| this.removeModificationsRecord(activeSession, node['path']); |
| for (let i in parent['newChildren']) { |
| if (parent['newChildren'][i]['path'] == node['path']) { |
| if (Number(i) > 0 && parent['newChildren'][i]['last']) { |
| parent['newChildren'][Number(i) - 1]['last'] = true; |
| } |
| parent['newChildren'].splice(i, 1); |
| break; |
| } |
| } |
| if (!parent['newChildren'].length) { |
| delete parent['newChildren']; |
| } |
| } |
| |
| if (node['info']['type'] == 1 || node['info']['type'] == 4) { |
| /* fix the list of nodes to create in parent */ |
| let schemas; |
| if (!('schemaChildren' in parent)) { |
| schemas = []; |
| } else { |
| schemas = parent['schemaChildren']; |
| } |
| schemas.push(node['info']); |
| this.createOpen(schemas, parent) |
| } |
| } else if (activeSession.modifications) { |
| let record = this.getModificationsRecord(activeSession, node['path']); |
| if (record) { |
| node['dirty'] = false; |
| if (record['type'] == 'change') { |
| node['value'] = record['original']; |
| } |
| this.removeModificationsRecord(activeSession, node['path']); |
| if (!activeSession.modifications) { |
| return; |
| } |
| } |
| } |
| |
| /* recursion */ |
| if (recursion && 'children' in node) { |
| if ('newChildren' in node) { |
| for (let child of node['newChildren']) { |
| let record = this.getModificationsRecord(activeSession, child['path']); |
| if (record['type'] == 'change') { |
| if (node['children'].length) { |
| node['children'][node['children'].length - 1]['last'] = false; |
| } |
| record['original']['last'] = true; |
| node['children'].push(record['original']) |
| } |
| this.removeModificationsRecord(activeSession, child['path']); |
| } |
| delete node['newChildren']; |
| if (('children' in node) && node['children'].length) { |
| node['children'][node['children'].length - 1]['last'] = true; |
| } |
| } |
| let last; |
| for (let child of node['children']) { |
| this.cancelModification(activeSession, child); |
| delete child['last']; |
| last = child; |
| } |
| last['last'] = true; |
| } |
| } |
| |
| private sortKeys(list) { |
| let key_index = 0; |
| for (let key of list['info']['keys']) { |
| for (let i in list['children']) { |
| if (list['children'][i]['info']['name'] == key && list['children'][i]['info']['module'] == list['info']['module']) { |
| let moved = list['children'].splice(i, 1); |
| list['children'].splice(key_index++, 0, moved[0]); |
| } |
| } |
| } |
| return key_index; |
| } |
| |
| private resolveKeys(node, top = true): string { |
| if (node['info']['type'] == 16) { |
| if (!('children' in node) || !node['children'].length) { |
| return 'no key in ' + node['path']; |
| } |
| let count = this.sortKeys(node); |
| if (count != node['info']['keys'].length) { |
| return 'invalid number (' + count + ') of keys in ' + node['path']; |
| } |
| } |
| if (node['info']['type'] == 16 || node['info']['type'] == 1) { |
| for (let i in node['children']) {; |
| console.log(node['children'][i]); |
| if (node['children'][i]['info']['type'] == 4) { |
| /* leaf */ |
| if (!('value' in node['children'][i])) { |
| if (node['children'][i]['info']['key']) { |
| return 'not confirmed value of the ' + node['children'][i]['path'] + ' key.'; |
| } |
| console.log('not confirmed node ' + node['children'][i]['path'] + ', removing it'); |
| node['children'].splice(i, 1); |
| } |
| } else { |
| /* recursion */ |
| let msg = this.resolveKeys(node['children'][i], false); |
| if (msg) { |
| return msg; |
| } |
| } |
| } |
| } |
| |
| /* update predicate in path */ |
| if (node['info']['type'] == 16 && top) { |
| node['path'] = node['path'].slice(0, node['path'].lastIndexOf('[')) |
| for (let i in node['info']['keys']) { |
| node['path'] = node['path'] + '[' + node['info']['keys'][i] + '=\'' + node['children'][i]['value'] + '\']' |
| } |
| } |
| return null; |
| } |
| |
| applyModification(activeSession: Session) { |
| for (let mod in activeSession.modifications) { |
| //console.log(JSON.stringify(mod)); |
| if (!('data' in activeSession.modifications[mod])) { |
| continue; |
| } |
| let err = this.resolveKeys(activeSession.modifications[mod]['data']); |
| if (err) { |
| console.log(err); |
| return new Promise((resolve, reject) => {resolve({'success':false,'error': [{'message':err}]})}); |
| } |
| } |
| return this.sessionsService.commit(activeSession.key).then(result => { |
| if (result['success']) { |
| delete activeSession.modifications; |
| } else { |
| console.log(result); |
| } |
| return result; |
| }) |
| } |
| } |