Skip to content

Commit 544c7f3

Browse files
committed
[5788] Add resource changes in the impact analysis tree
Bug: #5788 Signed-off-by: Gwendal Daniel <[email protected]>
1 parent 8266669 commit 544c7f3

File tree

6 files changed

+178
-22
lines changed

6 files changed

+178
-22
lines changed

CHANGELOG.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ This will allow to integrate the command palette in other contexts than the work
168168
- https://github.com/eclipse-sirius/sirius-web/issues/5766[#5766] [diagram] Merge all frontend palette into one common component
169169
- https://github.com/eclipse-sirius/sirius-web/issues/5563[#5563] [diagram] Contribute rectangular, image and edge appearance sections with the corresponding contribution point
170170
- https://github.com/eclipse-sirius/sirius-web/issues/5050[#5050] [sirius-web] Keep the dependencies of a project during the download / upload.
171+
- https://github.com/eclipse-sirius/sirius-web/issues/5788[#5788] [sirius-web] Add resource changes in the impact analysis tree.
171172

172173

173174
== 2025.10.0

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

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

120-
private List<String> getIconURL(Object object) {
221+
private List<String> getIconURLs(Object object) {
121222
List<String> result = List.of();
122223
if (this.isFeatureChange(object)) {
123224
if (object instanceof FeatureAddition featureAddition) {
@@ -133,32 +234,49 @@ private List<String> getIconURL(Object object) {
133234
return result;
134235
}
135236

136-
public StyledString getStyledLabel(Object object) {
237+
private StyledString getStyledLabel(Object object) {
137238
final StyledString result;
138239
if (this.isFeatureChange(object)) {
139240
String label = "";
140-
String foregroundColor = "";
141241
if (object instanceof FeatureAddition featureAddition) {
142242
label = featureAddition.feature() + FEATURE_SEPARATOR + this.getFeatureObjectLabel(featureAddition.newValue());
143-
foregroundColor = "#48752C";
243+
result = this.getAdditionStyledString(label);
144244
} else if (object instanceof FeatureDeletion featureDeletion) {
145245
label = featureDeletion.feature() + FEATURE_SEPARATOR + this.getFeatureObjectLabel(featureDeletion.oldValue());
146-
foregroundColor = "#BB271A";
246+
result = this.getDeletionStyledString(label);
147247
} else if (object instanceof FeatureModification featureModification) {
148248
label = featureModification.feature() + FEATURE_SEPARATOR + this.getFeatureObjectLabel(featureModification.oldValue()) + " -> " + this.getFeatureObjectLabel(featureModification.newValue());
149-
foregroundColor = "#000000";
249+
result = this.getModificationStyledString(label);
250+
} else {
251+
result = this.getStyledString("", "#000000");
150252
}
151-
result = new StyledString(List.of(
152-
new StyledStringFragment(label, StyledStringFragmentStyle.newDefaultStyledStringFragmentStyle()
153-
.foregroundColor(foregroundColor)
154-
.build())
155-
));
156253
} else {
157254
result = this.labelService.getStyledLabel(object);
158255
}
159256
return result;
160257
}
161258

259+
private StyledString getAdditionStyledString(String label) {
260+
return this.getStyledString(label, "#48752C");
261+
}
262+
263+
private StyledString getDeletionStyledString(String label) {
264+
return this.getStyledString(label, "#BB271A");
265+
}
266+
267+
private StyledString getModificationStyledString(String label) {
268+
return this.getStyledString(label, "#000000");
269+
270+
}
271+
272+
private StyledString getStyledString(String label, String foregroundColor) {
273+
return new StyledString(List.of(
274+
new StyledStringFragment(label, StyledStringFragmentStyle.newDefaultStyledStringFragmentStyle()
275+
.foregroundColor(foregroundColor)
276+
.build())
277+
));
278+
}
279+
162280
private String getFeatureObjectLabel(Object object) {
163281
String result;
164282
if (object instanceof EObject) {
@@ -172,18 +290,30 @@ private String getFeatureObjectLabel(Object object) {
172290
private List<List<String>> getEndIconsURL(Object object, ChangeDescription changeDescription) {
173291
List<List<String>> result = new ArrayList<>();
174292
if (object instanceof FeatureAddition featureAddition) {
175-
result.add(List.of("/impact-analysis/FeatureAddition.svg"));
293+
result = this.getAdditionEndIconsURLs();
176294
} else if (object instanceof FeatureDeletion featureDeletion) {
177-
result.add(List.of("/impact-analysis/FeatureDeletion.svg"));
295+
result = this.getDeletionEndIconsURLs();
178296
} else if (object instanceof FeatureModification featureModification) {
179-
result.add(List.of("/impact-analysis/FeatureModification.svg"));
297+
result = this.getModificationEndIconsURLs();
180298
} else if (changeDescription.getObjectChanges().keySet().contains(object)) {
181299
result.add(List.of("/impact-analysis/ChangeMarker.svg"));
182300
}
183301
return result;
184302
}
185303

186-
private List<Object> getChildren(Object object, IEditingContext editingContext, ChangeDescription changeDescription) {
304+
private List<List<String>> getAdditionEndIconsURLs() {
305+
return List.of(List.of("/impact-analysis/FeatureAddition.svg"));
306+
}
307+
308+
private List<List<String>> getDeletionEndIconsURLs() {
309+
return List.of(List.of("/impact-analysis/FeatureDeletion.svg"));
310+
}
311+
312+
private List<List<String>> getModificationEndIconsURLs() {
313+
return List.of(List.of("/impact-analysis/FeatureModification.svg"));
314+
}
315+
316+
private List<Object> getChildren(Object object, ChangeDescription changeDescription) {
187317
List<Object> children = new ArrayList<>();
188318
for (Entry<EObject, EList<FeatureChange>> changes : changeDescription.getObjectChanges().entrySet()) {
189319
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
@@ -143,6 +143,13 @@ public void givenProjectWithDependenciesWhenImpactAnalysisReportIsRequestedForTh
143143
DataTree dataTree = JsonPath.parse(result, configuration).read("$.data.viewer.editingContext.representation.description.treeImpactAnalysisReport.impactTree", DataTree.class);
144144

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