UNPKG

@eclipse-emfcloud/model-manager

Version:

Command-based model editing with undo/redo.

216 lines 7.53 kB
"use strict"; // ***************************************************************************** // Copyright (C) 2023-2024 STMicroelectronics. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0. // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse // Public License v. 2.0 are satisfied: MIT License which is // available at https://opensource.org/licenses/MIT. // // SPDX-License-Identifier: EPL-2.0 OR MIT // ***************************************************************************** Object.defineProperty(exports, "__esModule", { value: true }); exports.CoreModelManagerImpl = void 0; const lodash_1 = require("lodash"); const core_1 = require("../core"); const core_command_stack_impl_1 = require("./core-command-stack-impl"); class CoreModelManagerImpl { constructor(commandStackFactory = (workingCopyManager) => new core_command_stack_impl_1.CoreCommandStackImpl(workingCopyManager)) { this._subscriptions = new Map(); this._allSubscriptions = new Array(); this._modelStore = new ModelStore(this.changeOnCommandStack.bind(this)); this._commandStack = commandStackFactory(this._modelStore); } /** * The callback provided to the CoreCommandStackImpl to be able to notify * subscribers of any command executed (execute, executeAndAppend, undo, redo ) * * @param deltas the command result sent by 'execute, executeAndAppend, undo or redo' done in CoreCommandStackImpl */ changeOnCommandStack(deltas) { (0, core_1.groupByModelId)(deltas).forEach((operations, modelId) => { const model = this.getModel(modelId); if (model === undefined) { return; } for (const subscriber of this.subscriptions(modelId)) { if (subscriber.onModelChanged) { subscriber.onModelChanged(modelId, model, operations); } } }); } getModelId(model) { return this._modelStore.getModelId(model); } getModel(modelId) { return this._modelStore.getModel(modelId); } setModel(modelId, model) { this._modelStore.addModel(modelId, model); } getModelIds() { return this._modelStore.getModelIds(); } removeModel(modelId) { return this._modelStore.removeModel(modelId); } getCommandStack() { return this._commandStack; } addSubscription(modelId, subscription) { const existingSubscriptions = this._subscriptions.get(modelId) || []; existingSubscriptions.push(subscription); this._subscriptions.set(modelId, existingSubscriptions); } addAllSubscription(subscription) { this._allSubscriptions.push(subscription); } deleteSubscription(modelId, subscription) { const existingSubscriptions = this._subscriptions.get(modelId) || []; const index = existingSubscriptions.indexOf(subscription); if (index > -1) { existingSubscriptions.splice(index, 1); if (existingSubscriptions.length > 0) { this._subscriptions.set(modelId, existingSubscriptions); } else { this._subscriptions.delete(modelId); } } } deleteAllSubscription(subscription) { const index = this._allSubscriptions.indexOf(subscription); if (index > -1) { this._allSubscriptions.splice(index, 1); } } subscribe(modelId) { let subscription; if (modelId) { subscription = { close: () => this.deleteSubscription(modelId, subscription), }; this.addSubscription(modelId, subscription); } else { subscription = { close: () => this.deleteAllSubscription(subscription), }; this.addAllSubscription(subscription); } return subscription; } /** * Obtain an iterable over all subscriptions pertaining to the given * model ID. * * @param modelId a model ID for which to get subscriptions * @returns an iterator over all subscriptions specific to the `modelId` (if any) * followed by the general subscriptions */ *subscriptions(modelId) { const specific = this._subscriptions.get(modelId); if (specific) { for (const next of specific) { yield next; } } for (const next of this._allSubscriptions) { yield next; } } } exports.CoreModelManagerImpl = CoreModelManagerImpl; class ModelStore { constructor(notify) { this.notify = notify; /** Model storage. */ this._models = new Map(); /** Last known ID for each model. */ this._modelIds = new WeakMap(); this._workingCopies = new Map(); this._open = new Map(); } getModel(modelId) { return this._models.get(modelId); } getModelId(model) { return this._modelIds.get(model); } getModelIds() { return Array.from(this._models.keys()); } addModel(modelId, model) { if (this._models.get(modelId) !== undefined) { throw new Error(`Model ${modelId} is already registered.`); } this._models.set(modelId, model); this._modelIds.set(model, modelId); } removeModel(modelId) { const removedModel = this.getModel(modelId); if (removedModel !== undefined) { this._models.delete(modelId); // Let the generation info expire naturally with GC } return removedModel; } // // WorkingCopyManager protocol // open(modelIds) { if (this.isOpen(modelIds)) { throw new Error('Already open.'); } modelIds.forEach((modelId) => { this._open.set(modelId, true); this._workingCopies.delete(modelId); }); } isOpen(modelIds) { return modelIds.some((modelId) => this._open.get(modelId)); } getWorkingCopy(modelId) { if (!this.isOpen([modelId])) { throw new Error('Not open.'); } let result = this._workingCopies.get(modelId); if (result === undefined) { const model = this.getModel(modelId); if (model !== undefined) { result = (0, lodash_1.cloneDeep)(model); this._workingCopies.set(modelId, result); } } return result; } commit(result, modelIds) { try { for (const modelId of modelIds) { const workingCopy = this._workingCopies.get(modelId); if (workingCopy !== undefined) { this._models.set(modelId, workingCopy); } this._workingCopies.delete(modelId); } this.notify(result); } finally { for (const k of modelIds) { this._open.delete(k); } } } cancel(modelIds) { modelIds.forEach((modelId) => { this._workingCopies.delete(modelId); this._open.delete(modelId); }); } } //# sourceMappingURL=core-model-manager-impl.js.map