Skip to content

Commit

Permalink
Refactorings regarding source model loading and saving
Browse files Browse the repository at this point in the history
With this change, I'd like to consistently use the term source model and
avoid the term model source. This is to avoid the overlap with Sprotty's
concept of ModelSource and its unclear relation to a source model and
GModel.

I suggest to avoid Model Source altogether and instead introduce and
consistently use "source model" to refer to the underlying model that we
read and write. Thus we have in GLSP two types of models:
  * Source Model
  * Graph Model (GModel)

Thus, I did the following refactorings:
  * Rename ModelSourceLoader to SourceModelPersistence
  * Add saving to the SourceModelPersistence interface
  * Delegate from the SaveModelActionHandler to that implementation
  * Rename ModelSourceWatcher into SourceModelWatcher
  * Update javadoc and variable names where fit
  • Loading branch information
planger committed Mar 25, 2022
1 parent 7e41611 commit 1faaafb
Show file tree
Hide file tree
Showing 11 changed files with 108 additions and 103 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@

### Breaking Changes

- [server] Source model refactorings [#582](https://github.com/eclipse-glsp/glsp/issues/582) ([java: #154](https://github.com/eclipse-glsp/glsp-server/pull/154))
- `ModelSourceLoader``SourceModelPersistence`
- `ModelSourceWatcher``SourceModelWatcher`
- Added method to `SourceModelPersistence`
-


## [v0.9.0- 09/12/2021](https://github.com/eclipse-glsp/glsp/releases/tag/0.9.0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@
import org.eclipse.glsp.server.features.contextactions.ContextActionsProvider;
import org.eclipse.glsp.server.features.contextactions.RequestContextActionsHandler;
import org.eclipse.glsp.server.features.contextmenu.ContextMenuItemProvider;
import org.eclipse.glsp.server.features.core.model.JsonFileGModelLoader;
import org.eclipse.glsp.server.features.core.model.ModelSourceLoader;
import org.eclipse.glsp.server.features.core.model.JsonFileGModelStore;
import org.eclipse.glsp.server.features.core.model.SourceModelStorage;
import org.eclipse.glsp.server.features.directediting.ContextEditValidator;
import org.eclipse.glsp.server.features.directediting.LabelEditValidator;
import org.eclipse.glsp.server.features.modelsourcewatcher.FileWatcher;
import org.eclipse.glsp.server.features.modelsourcewatcher.ModelSourceWatcher;
import org.eclipse.glsp.server.features.modelsourcewatcher.SourceModelWatcher;
import org.eclipse.glsp.server.features.navigation.NavigationTargetProvider;
import org.eclipse.glsp.server.features.navigation.NavigationTargetResolver;
import org.eclipse.glsp.server.features.popup.PopupModelFactory;
Expand All @@ -69,12 +69,12 @@ protected Class<? extends DiagramConfiguration> bindDiagramConfiguration() {
}

@Override
protected Class<? extends ModelSourceLoader> bindSourceModelLoader() {
return JsonFileGModelLoader.class;
protected Class<? extends SourceModelStorage> bindSourceModelStorage() {
return JsonFileGModelStore.class;
}

@Override
protected Class<? extends ModelSourceWatcher> bindModelSourceWatcher() {
protected Class<? extends SourceModelWatcher> bindSourceModelWatcher() {
return FileWatcher.class;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2019-2021 EclipseSource and others.
* Copyright (c) 2019-2022 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
Expand All @@ -15,75 +15,36 @@
********************************************************************************/
package org.eclipse.glsp.server.actions;

import static org.eclipse.glsp.server.types.GLSPServerException.getOrThrow;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Optional;

import org.apache.log4j.Logger;
import org.eclipse.glsp.graph.GGraph;
import org.eclipse.glsp.server.features.modelsourcewatcher.ModelSourceWatcher;
import org.eclipse.glsp.server.gson.GraphGsonConfigurationFactory;
import org.eclipse.glsp.server.features.core.model.SourceModelStorage;
import org.eclipse.glsp.server.features.modelsourcewatcher.SourceModelWatcher;
import org.eclipse.glsp.server.model.GModelState;
import org.eclipse.glsp.server.types.GLSPServerException;
import org.eclipse.glsp.server.utils.ClientOptionsUtil;

import com.google.gson.Gson;
import com.google.inject.Inject;

public class SaveModelActionHandler extends AbstractActionHandler<SaveModelAction> {
private static final Logger LOG = Logger.getLogger(SaveModelActionHandler.class);

@Inject
protected GraphGsonConfigurationFactory gsonConfigurator;
protected Optional<SourceModelWatcher> modelSourceWatcher;

@Inject
protected Optional<ModelSourceWatcher> modelSourceWatcher;
protected GModelState modelState;

@Inject
protected GModelState modelState;
protected SourceModelStorage sourceModelStorage;

@Override
public List<Action> executeAction(final SaveModelAction action) {
modelSourceWatcher.ifPresent(watcher -> watcher.pauseWatching());
try {
saveModelState(action);
sourceModelStorage.saveSourceModel(action);
modelState.saveIsDone();
} finally {
modelSourceWatcher.ifPresent(watcher -> watcher.continueWatching());
}
return listOf(new SetDirtyStateAction(modelState.isDirty(), SetDirtyStateAction.Reason.SAVE));
}

protected void saveModelState(final SaveModelAction action) {
File file = convertToFile(action);
try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) {
Gson gson = gsonConfigurator.configureGson().setPrettyPrinting().create();
gson.toJson(modelState.getRoot(), GGraph.class, writer);
if (saveIsDone(action)) {
modelState.saveIsDone();
}
} catch (IOException e) {
LOG.error(e);
throw new GLSPServerException("An error occured during save process.", e);
}
}

protected boolean saveIsDone(final SaveModelAction action) {
String sourceUri = ClientOptionsUtil.adaptUri(modelState.getClientOptions().get(ClientOptionsUtil.SOURCE_URI));
return action.getFileUri().map(uri -> ClientOptionsUtil.adaptUri(uri).equals(sourceUri)).orElse(true);
}

protected File convertToFile(final SaveModelAction action) {
if (action.getFileUri().isPresent()) {
return ClientOptionsUtil.getAsFile(action.getFileUri().get());
}
return getOrThrow(ClientOptionsUtil.getSourceUriAsFile(modelState.getClientOptions()),
"Invalid file URI:" + ClientOptionsUtil.getSourceUri(modelState.getClientOptions()));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2021 EclipseSource and others.
* Copyright (c) 2021-2022 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
Expand Down Expand Up @@ -50,19 +50,19 @@
import org.eclipse.glsp.server.features.contextactions.SetContextActions;
import org.eclipse.glsp.server.features.contextmenu.ContextMenuItemProvider;
import org.eclipse.glsp.server.features.core.model.GModelFactory;
import org.eclipse.glsp.server.features.core.model.ModelSourceLoader;
import org.eclipse.glsp.server.features.core.model.RequestBoundsAction;
import org.eclipse.glsp.server.features.core.model.RequestModelActionHandler;
import org.eclipse.glsp.server.features.core.model.SetBoundsAction;
import org.eclipse.glsp.server.features.core.model.SetModelAction;
import org.eclipse.glsp.server.features.core.model.SourceModelStorage;
import org.eclipse.glsp.server.features.core.model.UpdateModelAction;
import org.eclipse.glsp.server.features.directediting.ContextEditValidator;
import org.eclipse.glsp.server.features.directediting.ContextEditValidatorRegistry;
import org.eclipse.glsp.server.features.directediting.LabelEditValidator;
import org.eclipse.glsp.server.features.directediting.RequestEditValidationHandler;
import org.eclipse.glsp.server.features.directediting.SetEditValidationResultAction;
import org.eclipse.glsp.server.features.modelsourcewatcher.ModelSourceChangedAction;
import org.eclipse.glsp.server.features.modelsourcewatcher.ModelSourceWatcher;
import org.eclipse.glsp.server.features.modelsourcewatcher.SourceModelWatcher;
import org.eclipse.glsp.server.features.navigation.NavigateToExternalTargetAction;
import org.eclipse.glsp.server.features.navigation.NavigateToTargetAction;
import org.eclipse.glsp.server.features.navigation.NavigationTargetProvider;
Expand Down Expand Up @@ -121,9 +121,9 @@
* <li>{@link DiagramConfiguration}
* <li>{@link ServerConfigurationContribution}
* <li>{@link GModelState}
* <li>{@link ModelSourceLoader}
* <li>{@link SourceModelStorage}
* <li>{@link GModelFactory}
* <li>{@link ModelSourceWatcher} as {@link Optional}
* <li>{@link SourceModelWatcher} as {@link Optional}
* <li>{@link GraphGsonConfigurationFactory}
* <li>{@link ModelValidator} as {@link Optional}
* <li>{@link LabelEditValidator} as {@link Optional}
Expand Down Expand Up @@ -167,9 +167,9 @@ protected void configureBase() {
bind(ServerConfigurationContribution.class).to(bindServerConfigurationContribution()).in(Singleton.class);
// Model-related bindings
configureGModelState(bindGModelState());
bind(ModelSourceLoader.class).to(bindSourceModelLoader());
bind(SourceModelStorage.class).to(bindSourceModelStorage());
bind(GModelFactory.class).to(bindGModelFactory());
bindOptionally(ModelSourceWatcher.class, bindModelSourceWatcher())
bindOptionally(SourceModelWatcher.class, bindSourceModelWatcher())
.ifPresent(binder -> binder.in(Singleton.class));
bind(GraphGsonConfigurationFactory.class).to(bindGraphGsonConfiguratorFactory()).in(Singleton.class);

Expand Down Expand Up @@ -234,11 +234,11 @@ protected Class<? extends GModelState> bindGModelState() {
return DefaultGModelState.class;
}

protected abstract Class<? extends ModelSourceLoader> bindSourceModelLoader();
protected abstract Class<? extends SourceModelStorage> bindSourceModelStorage();

protected abstract Class<? extends GModelFactory> bindGModelFactory();

protected Class<? extends ModelSourceWatcher> bindModelSourceWatcher() {
protected Class<? extends SourceModelWatcher> bindSourceModelWatcher() {
return null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2021 EclipseSource and others.
* Copyright (c) 2021-2022 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
Expand All @@ -20,8 +20,8 @@
import org.eclipse.glsp.server.features.clipboard.RequestClipboardDataActionHandler;
import org.eclipse.glsp.server.features.core.model.ComputedBoundsActionHandler;
import org.eclipse.glsp.server.features.core.model.GModelFactory;
import org.eclipse.glsp.server.features.core.model.JsonFileGModelLoader;
import org.eclipse.glsp.server.features.core.model.ModelSourceLoader;
import org.eclipse.glsp.server.features.core.model.JsonFileGModelStore;
import org.eclipse.glsp.server.features.core.model.SourceModelStorage;
import org.eclipse.glsp.server.features.directediting.ApplyLabelEditOperationHandler;
import org.eclipse.glsp.server.features.toolpalette.ToolPaletteItemProvider;
import org.eclipse.glsp.server.features.undoredo.UndoRedoActionHandler;
Expand All @@ -40,8 +40,8 @@
public abstract class GModelJsonDiagramModule extends DiagramModule {

@Override
protected Class<? extends ModelSourceLoader> bindSourceModelLoader() {
return JsonFileGModelLoader.class;
protected Class<? extends SourceModelStorage> bindSourceModelStorage() {
return JsonFileGModelStore.class;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019-2020 EclipseSource and others.
* Copyright (c) 2019-2022 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
Expand All @@ -26,7 +26,7 @@
* <p>
* The responsibility of a {@link GModelFactory} implementation is to define how a {@link GModelState} is to be
* translated into a {@link GModelRoot} that is sent to the client for rendering. Before a {@link GModelFactory}
* is invoked, the {@link ModelSourceLoader} has already been executed for loading the source model into the
* is invoked, the {@link SourceModelStorage} has already been executed for loading the source model into the
* {@link GModelState}. The {@link GModelFactory} then produces the {@link GModelRoot} from the source model in the
* {@link GModelState}. Implementations of {@link GModelFactory} are usually specific to the type of source model, as
* they need to understand the source model in order to translate it into a graph model.
Expand All @@ -40,11 +40,11 @@
* If an index is needed for mapping between the graph model and the source model, as is typically the case for
* {@link ActionHandler action handlers} and {@link OperationHandler operation handlers}, it is the responsibility of
* the graph model factory to create such an index while producing the graph model from the source model. The index
* shall be put into the model state too. Typically the {@link GModelIndex} is extended for a particular model source
* shall be put into the model state too. Typically the {@link GModelIndex} is extended for a particular source model
* type as well.
* </p>
*
* @see ModelSourceLoader
* @see SourceModelStorage
* @see GModelIndex
*/
public interface GModelFactory {
Expand All @@ -57,7 +57,7 @@ public interface GModelFactory {
void createGModel();

/**
* Graph model factory to be used if the graph model is already available from the model source.
* Graph model factory to be used if the graph model is already available from the source model.
*/
final class NullImpl implements GModelFactory {
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2019-2021 EclipseSource and others.
* Copyright (c) 2019-2022 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
Expand All @@ -19,17 +19,22 @@

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Optional;

import org.apache.log4j.Logger;
import org.eclipse.glsp.graph.DefaultTypes;
import org.eclipse.glsp.graph.GGraph;
import org.eclipse.glsp.graph.GModelRoot;
import org.eclipse.glsp.graph.GraphFactory;
import org.eclipse.glsp.server.actions.SaveModelAction;
import org.eclipse.glsp.server.gson.GraphGsonConfigurationFactory;
import org.eclipse.glsp.server.model.GModelState;
import org.eclipse.glsp.server.types.GLSPServerException;
Expand All @@ -39,36 +44,31 @@
import com.google.inject.Inject;

/**
* A source model loader that reads the graph model directly from a JSON file.
* A source model storage that reads and writes the graph model directly from and to a JSON file.
*/
public class JsonFileGModelLoader implements ModelSourceLoader {
public class JsonFileGModelStore implements SourceModelStorage {

private static Logger LOG = Logger.getLogger(JsonFileGModelLoader.class);
private static Logger LOG = Logger.getLogger(JsonFileGModelStore.class);
private static String EMPTY_ROOT_ID = "glsp-graph";

@Inject
private GraphGsonConfigurationFactory gsonConfiguratior;
private GraphGsonConfigurationFactory gsonConfigurator;

@Inject
protected GModelState modelState;

@Override
public void loadSourceModel(final RequestModelAction action) {
final File file = convertToFile(modelState);
final File file = convertToFile(action.getOptions());
loadSourceModel(file, modelState).ifPresent(root -> {
modelState.setRoot(root);
modelState.getRoot().setRevision(-1);
});
}

protected File convertToFile(final GModelState modelState) {
return getOrThrow(ClientOptionsUtil.getSourceUriAsFile(modelState.getClientOptions()),
"Invalid file URI:" + ClientOptionsUtil.getSourceUri(modelState.getClientOptions()));
}

protected Optional<GModelRoot> loadSourceModel(final File file, final GModelState modelState) {
try (Reader reader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) {
Gson gson = gsonConfiguratior.configureGson().create();
Gson gson = gsonConfigurator.configureGson().create();
GGraph root = gson.fromJson(reader, GGraph.class);
if (root == null) {
boolean isEmpty = file.length() == 0;
Expand All @@ -91,4 +91,28 @@ protected GModelRoot createNewEmptyRoot(final GModelState modelState) {
return root;
}

@Override
public void saveSourceModel(final SaveModelAction action) {
File file = convertToFile(action);
try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) {
Gson gson = gsonConfigurator.configureGson().setPrettyPrinting().create();
gson.toJson(modelState.getRoot(), GGraph.class, writer);
} catch (IOException e) {
LOG.error(e);
throw new GLSPServerException("An error occured during save process.", e);
}
}

protected File convertToFile(final SaveModelAction action) {
if (action.getFileUri().isPresent()) {
return ClientOptionsUtil.getAsFile(action.getFileUri().get());
}
return convertToFile(modelState.getClientOptions());
}

protected File convertToFile(final Map<String, String> clientOptions) {
return getOrThrow(ClientOptionsUtil.getSourceUriAsFile(clientOptions),
"Invalid file URI:" + ClientOptionsUtil.getSourceUri(clientOptions));
}

}
Loading

0 comments on commit 1faaafb

Please sign in to comment.