From 371a23950610786ea3f72a1d3c175ecf6b6818a4 Mon Sep 17 00:00:00 2001 From: Andreas Hell Date: Fri, 5 Apr 2024 14:19:17 +0200 Subject: [PATCH 1/3] Extended workflow example by injecting non-default --- .../src/common/workflow-diagram-module.ts | 12 ++++ .../src/common/workflow-gmodel-factory.ts | 50 +++++++++++++++ .../src/common/workflow-model-state.ts | 36 +++++++++++ .../src/common/workflow-model-storage.ts | 62 +++++++++++++++++++ examples/workflow-server/src/node/app.ts | 8 ++- 5 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 examples/workflow-server/src/common/workflow-gmodel-factory.ts create mode 100644 examples/workflow-server/src/common/workflow-model-state.ts create mode 100644 examples/workflow-server/src/common/workflow-model-storage.ts diff --git a/examples/workflow-server/src/common/workflow-diagram-module.ts b/examples/workflow-server/src/common/workflow-diagram-module.ts index 8cfdc82..9f9f602 100644 --- a/examples/workflow-server/src/common/workflow-diagram-module.ts +++ b/examples/workflow-server/src/common/workflow-diagram-module.ts @@ -23,8 +23,10 @@ import { EdgeCreationChecker, GLSPServer, GModelDiagramModule, + GModelFactory, InstanceMultiBinding, LabelEditValidator, + ModelState, ModelValidator, MultiBinding, NavigationTargetProvider, @@ -59,6 +61,8 @@ import { WorkflowDiagramConfiguration } from './workflow-diagram-configuration'; import { WorkflowEdgeCreationChecker } from './workflow-edge-creation-checker'; import { WorkflowGLSPServer } from './workflow-glsp-server'; import { WorkflowPopupFactory } from './workflow-popup-factory'; +import { WorkflowModelFactory } from './workflow-gmodel-factory'; +import { WorkflowModelState } from './workflow-model-state'; @injectable() export class WorkflowServerModule extends ServerModule { @@ -91,6 +95,14 @@ export class WorkflowDiagramModule extends GModelDiagramModule { binding.add(EditTaskOperationHandler); } + protected override bindModelState(): BindingTarget { + return { service: WorkflowModelState }; + } + + protected override bindGModelFactory(): BindingTarget { + return WorkflowModelFactory; + } + protected bindDiagramConfiguration(): BindingTarget { return WorkflowDiagramConfiguration; } diff --git a/examples/workflow-server/src/common/workflow-gmodel-factory.ts b/examples/workflow-server/src/common/workflow-gmodel-factory.ts new file mode 100644 index 0000000..ea091a2 --- /dev/null +++ b/examples/workflow-server/src/common/workflow-gmodel-factory.ts @@ -0,0 +1,50 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * 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: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +import { GModelFactory, GModelSerializer } from '@eclipse-glsp/server'; +import { inject, injectable } from 'inversify'; +import { WorkflowModelState } from './workflow-model-state'; + +/** + * This class provides the method to transform a source model into a GModel, updating the {@link GModelRoot}. + * + * This is, however, only relevant in cases where the source model is not already a valid GModel. + * + * Here, this custom implementation only serves to provide an entrypoint, but for a more extensive example + * look to {@link https://eclipse.dev/glsp/documentation/gmodel/#graphical-model-factory}. + */ +@injectable() +export class WorkflowModelFactory implements GModelFactory { + @inject(GModelSerializer) + protected modelSerializer: GModelSerializer; + + @inject(WorkflowModelState) + protected modelState: WorkflowModelState; + + /** + * Since this is an example using Workflow, which inherently already uses a GModel format, no difference + * exists between the underlying source model and `root`. Therefore all handlers directly update the + * root, making this method detrimental after first model creation. + * + * If other handlers are instead written to update `modelState.model`, then the root has to be updated + * after every change. + */ + createModel(): void { + if (this.modelState.root == null) { + const root = this.modelSerializer.createRoot(this.modelState.model); + this.modelState.updateRoot(root); + } + } +} diff --git a/examples/workflow-server/src/common/workflow-model-state.ts b/examples/workflow-server/src/common/workflow-model-state.ts new file mode 100644 index 0000000..4256e71 --- /dev/null +++ b/examples/workflow-server/src/common/workflow-model-state.ts @@ -0,0 +1,36 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * 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: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +import { DefaultModelState, GModelElementSchema } from '@eclipse-glsp/server'; +import { injectable } from 'inversify'; + +/** + * This model state serves to demonstrate how to extend or create a custom model state. + * + * While this may not be necessary when handling JSON formatted graphs that already + * correspond to a GModel (as the Workflow example does), since {@link DefaultModelState} + * is sufficient, it nonetheless provides an adequte example for custom formats. + */ +@injectable() +export class WorkflowModelState extends DefaultModelState { + /** + * The source model that needs to be transformed into a GModel and its {@link GModelRoot}. + * It is saved in the {@link ModelState} in order to later be available in the + * corresponding {@link GModelFactory}. + * + * Its type solely depends on the used source model. + */ + model: GModelElementSchema; +} diff --git a/examples/workflow-server/src/common/workflow-model-storage.ts b/examples/workflow-server/src/common/workflow-model-storage.ts new file mode 100644 index 0000000..22f0648 --- /dev/null +++ b/examples/workflow-server/src/common/workflow-model-storage.ts @@ -0,0 +1,62 @@ +/******************************************************************************** + * Copyright (c) 2023 EclipseSource and others. + * + * 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: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +import { + GModelElementSchema, + GModelSerializer, + MaybePromise, + RequestModelAction, + SaveModelAction, + SourceModelStorage +} from '@eclipse-glsp/server'; +import * as fs from 'fs'; +import { inject, injectable } from 'inversify'; +import { WorkflowModelState } from './workflow-model-state'; + +/** + * This {@link SourceModelStorage} serves as a naive implementation similar to the default {@link GModelStorage}. + * The main difference being that the source model is not directly instantiated as GModel, which works + * in the Workflow example (since its data format is already a valid GModel), but not generally. Therefore, + * this example is more easily applicable to custom model formats. + * + * The model saved here is later on transformed in {@link WorkflowModelFactory} and has to be updated in the handlers, + * if source model and GModel are different. + */ +@injectable() +export class WorkflowSourceModelStorage implements SourceModelStorage { + @inject(GModelSerializer) + protected modelSerializer: GModelSerializer; + + @inject(WorkflowModelState) + protected modelState: WorkflowModelState; + + loadSourceModel(action: RequestModelAction): MaybePromise { + const sourceUri = action.options!['sourceUri'] as string; + const model = this.loadJsonFile(sourceUri); + this.modelState.model = model; + this.modelState.set('sourceUri', sourceUri); + } + + saveSourceModel(action: SaveModelAction): MaybePromise { + const fileUri = this.modelState.get('sourceUri') as string; + const schema = this.modelSerializer.createSchema(this.modelState.root); + fs.writeFileSync(fileUri, JSON.stringify(schema)); + } + + protected loadJsonFile(path: string): GModelElementSchema { + const data = fs.readFileSync(path, { encoding: 'utf8' }); + return JSON.parse(data); + } +} diff --git a/examples/workflow-server/src/node/app.ts b/examples/workflow-server/src/node/app.ts index 6300ca3..0984eb1 100644 --- a/examples/workflow-server/src/node/app.ts +++ b/examples/workflow-server/src/node/app.ts @@ -16,12 +16,13 @@ import 'reflect-metadata'; import { configureELKLayoutModule } from '@eclipse-glsp/layout-elk'; -import { createAppModule, GModelStorage, SocketServerLauncher, WebSocketServerLauncher } from '@eclipse-glsp/server/node'; +import { createAppModule, SocketServerLauncher, WebSocketServerLauncher } from '@eclipse-glsp/server/node'; import { Container } from 'inversify'; import { WorkflowLayoutConfigurator } from '../common/layout/workflow-layout-configurator'; import { WorkflowDiagramModule, WorkflowServerModule } from '../common/workflow-diagram-module'; import { createWorkflowCliParser } from './workflow-cli-parser'; +import { WorkflowSourceModelStorage } from '../common/workflow-model-storage'; async function launch(argv?: string[]): Promise { const options = createWorkflowCliParser().parse(argv); @@ -29,7 +30,10 @@ async function launch(argv?: string[]): Promise { appContainer.load(createAppModule(options)); const elkLayoutModule = configureELKLayoutModule({ algorithms: ['layered'], layoutConfigurator: WorkflowLayoutConfigurator }); - const serverModule = new WorkflowServerModule().configureDiagramModule(new WorkflowDiagramModule(() => GModelStorage), elkLayoutModule); + const serverModule = new WorkflowServerModule().configureDiagramModule( + new WorkflowDiagramModule(() => WorkflowSourceModelStorage), + elkLayoutModule + ); if (options.webSocket) { const launcher = appContainer.resolve(WebSocketServerLauncher); From 5af37cd1a61f80b3b9b7740d2f6b3d24fda76d5e Mon Sep 17 00:00:00 2001 From: Andreas Hell Date: Fri, 5 Apr 2024 14:50:16 +0200 Subject: [PATCH 2/3] Fixed linting --- examples/workflow-server/src/common/workflow-gmodel-factory.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/workflow-server/src/common/workflow-gmodel-factory.ts b/examples/workflow-server/src/common/workflow-gmodel-factory.ts index ea091a2..42ccc0d 100644 --- a/examples/workflow-server/src/common/workflow-gmodel-factory.ts +++ b/examples/workflow-server/src/common/workflow-gmodel-factory.ts @@ -42,7 +42,7 @@ export class WorkflowModelFactory implements GModelFactory { * after every change. */ createModel(): void { - if (this.modelState.root == null) { + if (!this.modelState.root) { const root = this.modelSerializer.createRoot(this.modelState.model); this.modelState.updateRoot(root); } From 7ba3aba8bdb261fb21f8cf06302ea455a88c6324 Mon Sep 17 00:00:00 2001 From: Andreas Hell Date: Sat, 6 Apr 2024 20:32:43 +0200 Subject: [PATCH 3/3] Added check for loading windows file --- examples/workflow-server/src/common/workflow-model-storage.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/workflow-server/src/common/workflow-model-storage.ts b/examples/workflow-server/src/common/workflow-model-storage.ts index 22f0648..6276c10 100644 --- a/examples/workflow-server/src/common/workflow-model-storage.ts +++ b/examples/workflow-server/src/common/workflow-model-storage.ts @@ -23,6 +23,7 @@ import { } from '@eclipse-glsp/server'; import * as fs from 'fs'; import { inject, injectable } from 'inversify'; +import * as os from 'os'; import { WorkflowModelState } from './workflow-model-state'; /** @@ -56,6 +57,9 @@ export class WorkflowSourceModelStorage implements SourceModelStorage { } protected loadJsonFile(path: string): GModelElementSchema { + if (os.platform() === 'win32') { + path = path.replace(/^\//, ''); + } const data = fs.readFileSync(path, { encoding: 'utf8' }); return JSON.parse(data); }