Skip to content

Commit

Permalink
[3333] Add IMigrationParticipant
Browse files Browse the repository at this point in the history
Bug: #3333
Signed-off-by: Michaël Charfadi <michael.charfadi@obeosoft.com>
  • Loading branch information
mcharfadi committed Apr 16, 2024
1 parent 05bc084 commit 5ba64f9
Show file tree
Hide file tree
Showing 18 changed files with 889 additions and 21 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ image:doc/screenshots/inside_outside_labels.png[Isinde outside label example, 70
- https://github.com/eclipse-sirius/sirius-web/issues/3371[#3371] [view] Add new expressions in `NodeDescription` to specify whether a node is hidden/faded/normal by default.
- https://github.com/eclipse-sirius/sirius-web/issues/3371[#3371] [diagram] Add new predicates in `NodeDescription` to specify whether a node is hidden/faded/normal by default.
- https://github.com/eclipse-sirius/sirius-web/issues/3372[#3372] [diagram] Add services in `DiagramServices` to hide, reveal, fade, and unfade nodes from AQL expressions and Java services.
- https://github.com/eclipse-sirius/sirius-web/issues/3333[#3333] [sirius-web] Added IMigrationParticipant interface, contribute implemention to migrate models


=== Improvements
Expand All @@ -110,6 +111,7 @@ They still support returning an `java.time.Instant` object directly.
- https://github.com/eclipse-sirius/sirius-web/issues/3376[#3376] [diagram] Prevent `getToolSection` to be called during multi-selection
- https://github.com/eclipse-sirius/sirius-web/issues/3377[#3377] [diagram] Only synchronize selection with explorer at the end of the process
- https://github.com/eclipse-sirius/sirius-web/issues/3359[#3359] Add report on document upload
- https://github.com/eclipse-sirius/sirius-web/issues/3333[#3333] [sirius-web] Store the last migration performed in a document


== v2024.3.0
Expand Down
2 changes: 1 addition & 1 deletion packages/emf/backend/sirius-components-emf/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
<dependency>
<groupId>org.eclipse.sirius.emfjson</groupId>
<artifactId>org.eclipse.sirius.emfjson</artifactId>
<version>2.3.7-SNAPSHOT</version>
<version>2.3.8-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019, 2023 Obeo.
* Copyright (c) 2019, 2024 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
Expand All @@ -17,6 +17,7 @@
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.sirius.components.emf.migration.api.MigrationData;

/**
* An EMF adapter used to store some metadata related to EMF Resources.
Expand All @@ -33,6 +34,8 @@
public class ResourceMetadataAdapter implements Adapter {
private String name;

private MigrationData migrationData;

private Notifier notifier;

public ResourceMetadataAdapter(String name) {
Expand All @@ -47,6 +50,14 @@ public void setName(String name) {
this.name = name;
}

public MigrationData getMigrationData() {
return migrationData;
}

public void setMigrationData(MigrationData migrationData) {
this.migrationData = migrationData;
}

@Override
public void notifyChanged(Notification notification) {
// do nothing
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/*******************************************************************************
* Copyright (c) 2024 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.components.emf.migration;

import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.BasicExtendedMetaData;
import org.eclipse.sirius.components.emf.migration.api.IMigrationParticipant;
import org.eclipse.sirius.components.emf.migration.api.MigrationData;
import org.eclipse.sirius.components.emf.ResourceMetadataAdapter;
import org.eclipse.sirius.emfjson.resource.JsonResource;

import com.google.gson.Gson;
import com.google.gson.JsonObject;

/**
* Specialized BasicExtendedMetaData.
*
* @author mcharfadi
*/
public class MigrationService extends BasicExtendedMetaData implements JsonResource.IJsonResourceProcessor {
private MigrationData documentMigrationData;

private final List<IMigrationParticipant> migrationParticipants;

public MigrationService(List<IMigrationParticipant> migrationParticipants) {
this.migrationParticipants = Objects.requireNonNull(migrationParticipants);
this.documentMigrationData = new MigrationData("none", "0");
}

private boolean isCandidateVersion(IMigrationParticipant migrationParticipant) {
return migrationParticipant.getVersion().compareTo(this.documentMigrationData.migrationVersion()) > 0;
}

@Override
public EStructuralFeature getElement(EClass eClass, String namespace, String eStructuralFeatureName) {
EStructuralFeature structuralFeature = eClass.getEStructuralFeature(eStructuralFeatureName);
var migrationParticipantsCandidate = migrationParticipants.stream()
.filter(this::isCandidateVersion)
.sorted(Comparator.comparing(IMigrationParticipant::getVersion))
.toList();
for (IMigrationParticipant migrationParticipant : migrationParticipantsCandidate) {
EStructuralFeature newStructuralFeature = migrationParticipant.getEStructuralFeature(eClass, eStructuralFeatureName);
if (newStructuralFeature != null) {
structuralFeature = newStructuralFeature;
}
}
return structuralFeature;
}

@Override
public EClassifier getType(EPackage ePackage, String typeName) {
EClassifier eClassifier = ePackage.getEClassifier(typeName);
var migrationParticipantsCandidate = migrationParticipants.stream()
.filter(this::isCandidateVersion)
.sorted(Comparator.comparing(IMigrationParticipant::getVersion))
.toList();
for (IMigrationParticipant migrationParticipant : migrationParticipantsCandidate) {
EClassifier newEClassifier = migrationParticipant.getEClassifier(ePackage, typeName);
if (newEClassifier != null) {
eClassifier = newEClassifier;
}
}
return eClassifier;
}

@Override
public EPackage getPackage(String nsURI) {
EPackage ePackage = super.getPackage(nsURI);
var migrationParticipantsCandidate = migrationParticipants.stream()
.filter(this::isCandidateVersion)
.sorted(Comparator.comparing(IMigrationParticipant::getVersion))
.toList();
for (IMigrationParticipant migrationParticipant : migrationParticipantsCandidate) {
EPackage newEPackage = migrationParticipant.getPackage(nsURI);
if (newEPackage != null) {
ePackage = newEPackage;
}
}
return ePackage;
}

@Override
public void preDeserialization(JsonResource resource, JsonObject jsonObject) {
this.setMigrationDataFromDocumentContent(jsonObject);
this.setResourceMetadataAdapterMigrationData(resource, this.documentMigrationData);
var migrationParticipantsCandidates = migrationParticipants.stream()
.filter(this::isCandidateVersion)
.sorted(Comparator.comparing(IMigrationParticipant::getVersion))
.toList();
for (IMigrationParticipant migrationParticipant : migrationParticipantsCandidates) {
migrationParticipant.preDeserialization(resource, jsonObject);
setResourceMetadataAdapterMigrationData(resource, new MigrationData(migrationParticipant.getClass().getSimpleName(), migrationParticipant.getVersion()));
}
}

@Override
public void postSerialization(JsonResource resource, JsonObject jsonObject) {
getOptionalResourceMetadataAdapter(resource).ifPresent(resourceMetadataAdapter -> {
if (resourceMetadataAdapter.getMigrationData() != null) {
var rootMigration = new Gson().toJsonTree(resourceMetadataAdapter.getMigrationData(), MigrationData.class);
if (rootMigration != null) {
jsonObject.add(MigrationData.JSON_OBJECT_ROOT, rootMigration);
}
}
});
var migrationParticipantsCandidates = migrationParticipants.stream()
.filter(this::isCandidateVersion)
.sorted(Comparator.comparing(IMigrationParticipant::getVersion))
.toList();
for (IMigrationParticipant migrationParticipant : migrationParticipantsCandidates) {
migrationParticipant.postSerialization(resource, jsonObject);
}
}

@Override
public void postObjectLoading(EObject eObject, JsonObject jsonObject, boolean isTopObject) {
var migrationParticipantsCandidates = migrationParticipants.stream()
.filter(this::isCandidateVersion)
.sorted(Comparator.comparing(IMigrationParticipant::getVersion))
.toList();
for (IMigrationParticipant migrationParticipant : migrationParticipantsCandidates) {
migrationParticipant.postObjectLoading(eObject, jsonObject);
}
}

@Override
public Object getValue(EObject object, EStructuralFeature feature, Object value) {
Object returnValue = value;
var migrationParticipantsCandidates = migrationParticipants.stream()
.filter(this::isCandidateVersion)
.sorted(Comparator.comparing(IMigrationParticipant::getVersion))
.toList();
for (IMigrationParticipant migrationParticipant : migrationParticipantsCandidates) {
Object newValue = migrationParticipant.getValue(object, feature, value);
if (newValue != null) {
returnValue = newValue;
}
}
return returnValue;
}

private Optional<ResourceMetadataAdapter> getOptionalResourceMetadataAdapter(JsonResource resource) {
return resource.eAdapters().stream()
.filter(ResourceMetadataAdapter.class::isInstance)
.map(ResourceMetadataAdapter.class::cast)
.findFirst();
}

private void setResourceMetadataAdapterMigrationData(JsonResource resource, MigrationData migrationData) {
getOptionalResourceMetadataAdapter(resource).ifPresent(resourceMetadataAdapter -> resourceMetadataAdapter.setMigrationData(migrationData));
}

private void setMigrationDataFromDocumentContent(JsonObject jsonObject) {
var optionalMigrationData = Optional.ofNullable(jsonObject.getAsJsonObject(MigrationData.JSON_OBJECT_ROOT))
.map(migrationRootElement -> new Gson().fromJson(migrationRootElement, MigrationData.class)).stream().findFirst();
optionalMigrationData.ifPresent(migrationData -> this.documentMigrationData = migrationData);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*******************************************************************************
* Copyright (c) 2024 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.components.emf.migration.api;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.sirius.emfjson.resource.JsonResource;

import com.google.gson.JsonObject;

/**
* Interface of MigrationParticipant.
*
* @author mcharfadi
*/
public interface IMigrationParticipant {
String getVersion();

/**
* Called before a JsonResource is serialized.
*
* @param resource
* The JsonResource that is serialized
* @param jsonObject
* The root jsonObject
*/
default void preDeserialization(JsonResource resource, JsonObject jsonObject) {

}

/**
* Called after a JsonResource is serialized.
*
* @param resource
* The JsonResource that is serialized
* @param jsonObject
* The root jsonObject
*/
default void postSerialization(JsonResource resource, JsonObject jsonObject) {

}

/**
* Called during the parsing of JsonResources after loading an eObject. As such the eObject will have all his
* features set. The jsonObject is the one that was used to set the features and can be used to retrieve values
* of unknown features.
*
* @param eObject
* the eObject that have been loaded.
* @param jsonObject
* the jsonObject used to load the eObject.
*/
default void postObjectLoading(EObject eObject, JsonObject jsonObject) {

}

/**
* Called during the parsing of JsonResources (at loading time). If a feature value has changed since a previous
* version, use this method to return the correct expected value or null if it did not change.
*
* @param eObject
* the object containing the feature.
* @param feature
* the feature to set value.
* @param value
* the initial serialized value.
* @return the new value, or null otherwise.
*/
default Object getValue(EObject eObject, EStructuralFeature feature, Object value) {
return null;
}

/**
* Called during the parsing of JsonResources (at loading time). It allows to
* retrieve a renamed EStructuralFeature from its old name. For example, if a
* feature 'aaa' has been renamed to 'bbb', then your MigrationParticipant
* should return the 'bbb' feature when given the 'aaa' name.
*
* @param eClass
* the given eClass.
* @param eStructuralFeatureName
* the feature name before migration.
* @return the new structural feature or null if not found. The attribute
* corresponding to given old name into given eClass.
*/
default EStructuralFeature getEStructuralFeature(EClass eClass, String eStructuralFeatureName) {
return eClass.getEStructuralFeature(eStructuralFeatureName);
}

/**
* Called during the parsing of JsonResources (at loading time). If an
* EClassifier name has changed, then you should return the correct one.
*
* @param ePackage
* the package where looking for classifier.
* @param typeName
* the old classifier name before migration.
* @return the new classifier corresponding to the old given name into given
* ePackage or null if not found.
*/
default EClassifier getEClassifier(EPackage ePackage, String typeName) {
return ePackage.getEClassifier(typeName);
}

/**
* Return the EPackage to use for the given namespace.
*
* @param nsURI
* the nsURI of the package we are looking for.
* @return an EPackage if some new mapping exists, null otherwise.
*/
default EPackage getPackage(String nsURI) {
return null;
}
}

0 comments on commit 5ba64f9

Please sign in to comment.