Skip to content

Commit d70a52b

Browse files
gdanielsbegaudeau
authored andcommitted
[5788] Add resource changes in the impact analysis tree
Bug: #5788 Signed-off-by: Gwendal Daniel <[email protected]>
1 parent 3da6d98 commit d70a52b

File tree

6 files changed

+176
-22
lines changed

6 files changed

+176
-22
lines changed

CHANGELOG.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ This will allow to integrate the command palette in other contexts than the work
193193
* Duplicate a representation
194194
As a result, some of those menu items are not contributed anymore on invalid use cases such as being able to create a new object in a read only library.
195195
- https://github.com/eclipse-sirius/sirius-web/issues/5702[#5702] [table] Improve the rendering of components used in table cells
196+
- https://github.com/eclipse-sirius/sirius-web/issues/5788[#5788] [sirius-web] Add resource changes in the impact analysis tree.
197+
196198

197199

198200
== 2025.10.0

packages/sirius-web/backend/sirius-web-application/src/main/java/org/eclipse/sirius/web/application/impactanalysis/services/DefaultChangeDescriptionDataTreeProvider.java

Lines changed: 149 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
import org.eclipse.emf.ecore.change.ChangeKind;
2828
import org.eclipse.emf.ecore.change.FeatureChange;
2929
import org.eclipse.emf.ecore.change.ListChange;
30+
import org.eclipse.emf.ecore.change.ResourceChange;
3031
import org.eclipse.emf.ecore.resource.Resource;
32+
import org.eclipse.emf.ecore.resource.ResourceSet;
3133
import org.eclipse.sirius.components.core.api.IContentService;
3234
import org.eclipse.sirius.components.core.api.IEditingContext;
3335
import org.eclipse.sirius.components.core.api.IIdentityService;
@@ -39,6 +41,7 @@
3941
import org.eclipse.sirius.components.datatree.DataTreeNode;
4042
import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext;
4143
import org.eclipse.sirius.web.application.impactanalysis.services.api.IDefaultChangeDescriptionDataTreeProvider;
44+
import org.eclipse.sirius.web.application.library.services.LibraryMetadataAdapter;
4245
import org.springframework.stereotype.Service;
4346

4447
/**
@@ -69,13 +72,14 @@ public Optional<DataTree> getDataTree(IEditingContext editingContext, ChangeDesc
6972
if (editingContext instanceof IEMFEditingContext emfEditingContext) {
7073
List<String> allImpactedObjectIds = this.getAllImpactedObjectIds(changeDescription);
7174
List<DataTreeNode> dataTreeNodes = new ArrayList<>();
75+
dataTreeNodes.addAll(this.createAllResourcesDataTreeNodes(emfEditingContext.getDomain().getResourceSet(), changeDescription.getResourceChanges()));
7276
for (Resource resource : emfEditingContext.getDomain().getResourceSet().getResources()) {
7377
String resourceId = this.getId(resource);
7478
if (allImpactedObjectIds.contains(resourceId)) {
75-
dataTreeNodes.add(new DataTreeNode(resourceId, null, this.getStyledLabel(resource), this.getIconURL(resource), List.of(List.of())));
76-
List<Object> children = this.getChildren(resource, editingContext, changeDescription);
79+
dataTreeNodes.add(new DataTreeNode(resourceId, null, this.getStyledLabel(resource), this.getIconURLs(resource), List.of(List.of())));
80+
List<Object> children = this.getChildren(resource, changeDescription);
7781
for (Object child : children) {
78-
dataTreeNodes.addAll(this.getDataTreeNode(child, resource, emfEditingContext, changeDescription, allImpactedObjectIds));
82+
dataTreeNodes.addAll(this.getObjectChangeDataTreeNode(child, resource, changeDescription, allImpactedObjectIds));
7983
}
8084
}
8185
}
@@ -85,15 +89,109 @@ public Optional<DataTree> getDataTree(IEditingContext editingContext, ChangeDesc
8589
return result;
8690
}
8791

88-
private List<DataTreeNode> getDataTreeNode(Object object, Object parent, IEditingContext editingContext, ChangeDescription changeDescription, List<String> allImpactObjectIds) {
92+
private List<DataTreeNode> createAllResourcesDataTreeNodes(ResourceSet resourceSet, List<ResourceChange> resourceChanges) {
93+
List<DataTreeNode> result = new ArrayList<>();
94+
for (ResourceChange resourceChange : resourceChanges) {
95+
for (ListChange listChange : resourceChange.getListChanges()) {
96+
if (listChange.getKind() == ChangeKind.ADD_LITERAL) {
97+
Resource changedResource = resourceChange.getResource();
98+
List<?> resourceContent = resourceChange.getValue();
99+
result.addAll(this.createResourceDataTreeNodes(changedResource, resourceContent, listChange.getKind()));
100+
resourceSet.getResources().stream()
101+
.filter(resource -> Objects.equals(resource.getURI(), changedResource.getURI()))
102+
.findFirst()
103+
.ifPresent(resourceWithSameURI -> {
104+
// There is a resource in the editing context with the same ID, this change is not
105+
// tracked by the change description.
106+
// This is for example the case when updating a library: the old version is removed, the
107+
// new one is added, but the change description will only contain the removal.
108+
result.addAll(this.createResourceDataTreeNodes(resourceWithSameURI, resourceWithSameURI.getContents(), ChangeKind.REMOVE_LITERAL));
109+
});
110+
} else if (listChange.getKind() == ChangeKind.REMOVE_LITERAL) {
111+
Resource changedResource = resourceChange.getResource();
112+
List<?> resourceContent = resourceChange.getValue();
113+
result.addAll(this.createResourceDataTreeNodes(changedResource, resourceContent, listChange.getKind()));
114+
} else if (listChange.getKind() == ChangeKind.MOVE_LITERAL) {
115+
// MOVE_LITERAL is not supported for now.
116+
}
117+
}
118+
}
119+
return result;
120+
}
121+
122+
private List<DataTreeNode> createResourceDataTreeNodes(Resource resource, List<?> resourceContent, ChangeKind changeKind) {
123+
List<DataTreeNode> result = new ArrayList<>();
124+
// We need to add a suffix to the ids to ensure they remain unique in case we display 2 resources with the same
125+
// ID. This is for example the case when updating a library, where both the old and new version have the
126+
// same ID.
127+
String idSuffix = "#" + changeKind.getName();
128+
String resourceId = this.getId(resource) + idSuffix;
129+
String resourceLabel = this.labelService.getStyledLabel(resource) + this.getLibraryLabelFragment(resource).orElse("");
130+
Optional<StyledString> optionalStyledString = this.getResourceNodeStyledString(resourceLabel, changeKind);
131+
Optional<List<List<String>>> optionalEndIconsURLs = this.getResourceNodeEndIconsURLs(changeKind);
132+
if (optionalStyledString.isPresent() && optionalEndIconsURLs.isPresent()) {
133+
result.add(new DataTreeNode(resourceId, null, optionalStyledString.get(), this.getIconURLs(resource), optionalEndIconsURLs.get()));
134+
for (Object resourceObject : resourceContent) {
135+
result.addAll(this.createResourceChangeContentDataTreeNode(resourceObject, resource, changeKind));
136+
}
137+
}
138+
return result;
139+
}
140+
141+
private List<DataTreeNode> createResourceChangeContentDataTreeNode(Object object, Object parent, ChangeKind changeKind) {
142+
List<DataTreeNode> result = new ArrayList<>();
143+
String idSuffix = "#" + changeKind.getName();
144+
String objectId = this.getId(object) + idSuffix;
145+
String objectLabel = this.labelService.getStyledLabel(object).toString();
146+
Optional<StyledString> optionalStyledString = this.getResourceNodeStyledString(objectLabel, changeKind);
147+
Optional<List<List<String>>> optionalEndIconsURLs = this.getResourceNodeEndIconsURLs(changeKind);
148+
if (optionalStyledString.isPresent() && optionalEndIconsURLs.isPresent()) {
149+
result.add(new DataTreeNode(objectId, this.getId(parent) + idSuffix, optionalStyledString.get(), this.getIconURLs(object), optionalEndIconsURLs.get()));
150+
List<Object> children = this.contentService.getContents(object);
151+
for (Object child : children) {
152+
result.addAll(this.createResourceChangeContentDataTreeNode(child, object, changeKind));
153+
}
154+
}
155+
return result;
156+
}
157+
158+
private Optional<StyledString> getResourceNodeStyledString(String label, ChangeKind changeKind) {
159+
Optional<StyledString> result = Optional.empty();
160+
if (changeKind == ChangeKind.ADD_LITERAL) {
161+
result = Optional.of(this.getDeletionStyledString(label));
162+
} else if (changeKind == ChangeKind.REMOVE_LITERAL) {
163+
result = Optional.of(this.getAdditionStyledString(label));
164+
}
165+
return result;
166+
}
167+
168+
private Optional<List<List<String>>> getResourceNodeEndIconsURLs(ChangeKind changeKind) {
169+
Optional<List<List<String>>> result = Optional.empty();
170+
if (changeKind == ChangeKind.ADD_LITERAL) {
171+
result = Optional.of(this.getDeletionEndIconsURLs());
172+
} else if (changeKind == ChangeKind.REMOVE_LITERAL) {
173+
result = Optional.of(this.getAdditionEndIconsURLs());
174+
}
175+
return result;
176+
}
177+
178+
private Optional<String> getLibraryLabelFragment(Resource resource) {
179+
return resource.eAdapters().stream()
180+
.filter(LibraryMetadataAdapter.class::isInstance)
181+
.map(LibraryMetadataAdapter.class::cast)
182+
.map(libraryMetadataAdapter -> " (" + libraryMetadataAdapter.getName() + "@" + libraryMetadataAdapter.getVersion() + ")")
183+
.findFirst();
184+
}
185+
186+
private List<DataTreeNode> getObjectChangeDataTreeNode(Object object, Object parent, ChangeDescription changeDescription, List<String> allImpactObjectIds) {
89187
List<DataTreeNode> result = new ArrayList<>();
90188
String objectId = this.getId(object);
91189
if (this.isFeatureChange(object)
92190
|| allImpactObjectIds.contains(objectId)) {
93-
result.add(new DataTreeNode(objectId, this.getId(parent), this.getStyledLabel(object), this.getIconURL(object), this.getEndIconsURL(object, changeDescription)));
94-
List<Object> children = this.getChildren(object, editingContext, changeDescription);
191+
result.add(new DataTreeNode(objectId, this.getId(parent), this.getStyledLabel(object), this.getIconURLs(object), this.getEndIconsURL(object, changeDescription)));
192+
List<Object> children = this.getChildren(object, changeDescription);
95193
for (Object child : children) {
96-
result.addAll(this.getDataTreeNode(child, object, editingContext, changeDescription, allImpactObjectIds));
194+
result.addAll(this.getObjectChangeDataTreeNode(child, object, changeDescription, allImpactObjectIds));
97195
}
98196
}
99197
return result;
@@ -117,7 +215,7 @@ private String getId(Object object) {
117215
return result;
118216
}
119217

120-
private List<String> getIconURL(Object object) {
218+
private List<String> getIconURLs(Object object) {
121219
List<String> result = List.of();
122220
if (this.isFeatureChange(object)) {
123221
if (object instanceof FeatureAddition featureAddition) {
@@ -133,32 +231,49 @@ private List<String> getIconURL(Object object) {
133231
return result;
134232
}
135233

136-
public StyledString getStyledLabel(Object object) {
234+
private StyledString getStyledLabel(Object object) {
137235
final StyledString result;
138236
if (this.isFeatureChange(object)) {
139237
String label = "";
140-
String foregroundColor = "";
141238
if (object instanceof FeatureAddition featureAddition) {
142239
label = featureAddition.feature() + FEATURE_SEPARATOR + this.getFeatureObjectLabel(featureAddition.newValue());
143-
foregroundColor = "#48752C";
240+
result = this.getAdditionStyledString(label);
144241
} else if (object instanceof FeatureDeletion featureDeletion) {
145242
label = featureDeletion.feature() + FEATURE_SEPARATOR + this.getFeatureObjectLabel(featureDeletion.oldValue());
146-
foregroundColor = "#BB271A";
243+
result = this.getDeletionStyledString(label);
147244
} else if (object instanceof FeatureModification featureModification) {
148245
label = featureModification.feature() + FEATURE_SEPARATOR + this.getFeatureObjectLabel(featureModification.oldValue()) + " -> " + this.getFeatureObjectLabel(featureModification.newValue());
149-
foregroundColor = "#000000";
246+
result = this.getModificationStyledString(label);
247+
} else {
248+
result = this.getStyledString("", "#000000");
150249
}
151-
result = new StyledString(List.of(
152-
new StyledStringFragment(label, StyledStringFragmentStyle.newDefaultStyledStringFragmentStyle()
153-
.foregroundColor(foregroundColor)
154-
.build())
155-
));
156250
} else {
157251
result = this.labelService.getStyledLabel(object);
158252
}
159253
return result;
160254
}
161255

256+
private StyledString getAdditionStyledString(String label) {
257+
return this.getStyledString(label, "#48752C");
258+
}
259+
260+
private StyledString getDeletionStyledString(String label) {
261+
return this.getStyledString(label, "#BB271A");
262+
}
263+
264+
private StyledString getModificationStyledString(String label) {
265+
return this.getStyledString(label, "#000000");
266+
267+
}
268+
269+
private StyledString getStyledString(String label, String foregroundColor) {
270+
return new StyledString(List.of(
271+
new StyledStringFragment(label, StyledStringFragmentStyle.newDefaultStyledStringFragmentStyle()
272+
.foregroundColor(foregroundColor)
273+
.build())
274+
));
275+
}
276+
162277
private String getFeatureObjectLabel(Object object) {
163278
String result;
164279
if (object instanceof EObject) {
@@ -172,18 +287,30 @@ private String getFeatureObjectLabel(Object object) {
172287
private List<List<String>> getEndIconsURL(Object object, ChangeDescription changeDescription) {
173288
List<List<String>> result = new ArrayList<>();
174289
if (object instanceof FeatureAddition featureAddition) {
175-
result.add(List.of("/impact-analysis/FeatureAddition.svg"));
290+
result = this.getAdditionEndIconsURLs();
176291
} else if (object instanceof FeatureDeletion featureDeletion) {
177-
result.add(List.of("/impact-analysis/FeatureDeletion.svg"));
292+
result = this.getDeletionEndIconsURLs();
178293
} else if (object instanceof FeatureModification featureModification) {
179-
result.add(List.of("/impact-analysis/FeatureModification.svg"));
294+
result = this.getModificationEndIconsURLs();
180295
} else if (changeDescription.getObjectChanges().keySet().contains(object)) {
181296
result.add(List.of("/impact-analysis/ChangeMarker.svg"));
182297
}
183298
return result;
184299
}
185300

186-
private List<Object> getChildren(Object object, IEditingContext editingContext, ChangeDescription changeDescription) {
301+
private List<List<String>> getAdditionEndIconsURLs() {
302+
return List.of(List.of("/impact-analysis/FeatureAddition.svg"));
303+
}
304+
305+
private List<List<String>> getDeletionEndIconsURLs() {
306+
return List.of(List.of("/impact-analysis/FeatureDeletion.svg"));
307+
}
308+
309+
private List<List<String>> getModificationEndIconsURLs() {
310+
return List.of(List.of("/impact-analysis/FeatureModification.svg"));
311+
}
312+
313+
private List<Object> getChildren(Object object, ChangeDescription changeDescription) {
187314
List<Object> children = new ArrayList<>();
188315
for (Entry<EObject, EList<FeatureChange>> changes : changeDescription.getObjectChanges().entrySet()) {
189316
if (changes.getKey().equals(object)) {

packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/impactanalysis/ImpactAnalysisDiagramToolControllerTests.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ public void givenDiagramWithToolWhenAskingForImpactAnalysisOnToolThenTheReportIs
165165
DataTree dataTree = JsonPath.parse(result, configuration).read("$.data.viewer.editingContext.representation.description.diagramImpactAnalysisReport.impactTree", DataTree.class);
166166

167167
assertThat(dataTree.id()).isEqualTo("impact_tree");
168+
assertThat(dataTree.nodes().stream().filter(node -> node.parentId() == null)).hasSize(1);
168169
assertThat(dataTree.nodes()).anySatisfy(node -> {
169170
assertThat(node.label().toString()).isEqualTo("elements: a");
170171
assertThat(node.endIconsURLs()).hasSize(1);

packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/impactanalysis/ImpactAnalysisTreeContextualMenuEntryControllerTests.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ public void givenADomainExplorerRepresentationWhenAnImpactAnalysisReportForATool
117117
DataTree dataTree = JsonPath.parse(result, configuration).read("$.data.viewer.editingContext.representation.description.treeImpactAnalysisReport.impactTree", DataTree.class);
118118

119119
assertThat(dataTree.id()).isEqualTo("impact_tree");
120+
121+
assertThat(dataTree.nodes().stream().filter(node -> node.parentId() == null).toList()).hasSize(1);
122+
120123
assertThat(dataTree.nodes()).anySatisfy(node -> {
121124
assertThat(node.label().toString()).isEqualTo("abstract: false -> true");
122125
assertThat(node.endIconsURLs()).hasSize(1);

packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/libraries/LibraryRemoveImpactAnalysisControllerIntegrationTest.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,13 @@ public void givenProjectWithDependenciesWhenImpactAnalysisReportIsRequestedForTh
144144
DataTree dataTree = JsonPath.parse(result, configuration).read("$.data.viewer.editingContext.representation.description.treeImpactAnalysisReport.impactTree", DataTree.class);
145145

146146
assertThat(dataTree.id()).isEqualTo("impact_tree");
147+
assertThat(dataTree.nodes()).anySatisfy(node -> {
148+
assertThat(node.label().toString()).isEqualTo("Sirius Web Tests Data ([email protected])");
149+
assertThat(node.parentId()).isNull();
150+
assertThat(node.endIconsURLs()).hasSize(1);
151+
List<String> endIconsURL = node.endIconsURLs().get(0);
152+
assertThat(endIconsURL).anyMatch(endIconURL -> endIconURL.contains("FeatureDeletion.svg"));
153+
});
147154
assertThat(dataTree.nodes()).anySatisfy(node -> {
148155
assertThat(node.label().toString()).isEqualTo("annotations: GivenSiriusWebServer");
149156
assertThat(node.endIconsURLs()).hasSize(1);

packages/sirius-web/backend/sirius-web/src/test/java/org/eclipse/sirius/web/application/controllers/libraries/LibraryUpdateImpactAnalysisControllerIntegrationTests.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,20 @@ public void givenProjectWithDependenciesWhenImpactAnalysisReportIsRequestedForTh
115115
DataTree dataTree = JsonPath.parse(result, configuration).read("$.data.viewer.editingContext.updateLibraryImpactAnalysisReport.impactTree", DataTree.class);
116116

117117
assertThat(dataTree.id()).isEqualTo("impact_tree");
118+
assertThat(dataTree.nodes()).anySatisfy(node -> {
119+
assertThat(node.label().toString()).isEqualTo("Sirius Web Tests Data ([email protected])");
120+
assertThat(node.parentId()).isNull();
121+
assertThat(node.endIconsURLs()).hasSize(1);
122+
List<String> endIconsURL = node.endIconsURLs().get(0);
123+
assertThat(endIconsURL).anyMatch(endIconURL -> endIconURL.contains("FeatureDeletion.svg"));
124+
});
125+
assertThat(dataTree.nodes()).anySatisfy(node -> {
126+
assertThat(node.label().toString()).isEqualTo("Sirius Web Tests Data ([email protected])");
127+
assertThat(node.parentId()).isNull();
128+
assertThat(node.endIconsURLs()).hasSize(1);
129+
List<String> endIconsURL = node.endIconsURLs().get(0);
130+
assertThat(endIconsURL).anyMatch(endIconURL -> endIconURL.contains("FeatureAddition.svg"));
131+
});
118132
assertThat(dataTree.nodes()).anySatisfy(node -> {
119133
assertThat(node.label().toString()).isEqualTo("annotations: GivenSiriusWebServer");
120134
assertThat(node.endIconsURLs()).hasSize(1);

0 commit comments

Comments
 (0)