Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

- Add support for `ViewUsages` and `ViewDefinitions`

=== Architectural decision records

- Support representation only through `ViewUsage`

=== Breaking changes

=== Dependency update
Expand Down Expand Up @@ -35,6 +39,9 @@ When end-users click on _New Object_ on a semantic element, and select a `ViewUs
=== New features

- https://github.com/eclipse-syson/syson/issues/1237[#1237] [general-view] Add `ViewUsage` graphical node in the General View diagram.
- https://github.com/eclipse-syson/syson/issues/1270[#1270] [explorer-view] Change explorer to link representation behavior to `ViewUsage`:
* Open associated representation when a `ViewUsage` is selected
* Hide representation in the explorer

== v2025.4.0

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2024 Obeo.
* Copyright (c) 2024, 2025 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 Down Expand Up @@ -67,7 +67,7 @@ private TreeDescription build() {
.preconditionExpression("aql:false")
.selectableExpression("aql:self.isSelectable()")
.titleExpression(SYSON_EXPLORER)
.treeItemIdExpression("aql:self.getTreeItemId()")
.treeItemIdExpression("aql:self.getTreeItemId(editingContext)")
.treeItemObjectExpression("aql:id.getTreeItemObject(editingContext)")
.treeItemLabelDescriptions(this.createDefaultStyle())
.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2024 Obeo.
* Copyright (c) 2024, 2025 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 Down Expand Up @@ -42,10 +42,10 @@ public ComposedSysONExplorerService(List<ISysONExplorerServiceDelegate> explorer
}

@Override
public String getTreeItemId(Object self) {
public String getTreeItemId(Object self, IEditingContext editingContext) {
return this.getDelegate(self)
.map(delegate -> delegate.getTreeItemId(self))
.orElseGet(() -> this.defaultExplorerService.getTreeItemId(self));
.orElseGet(() -> this.defaultExplorerService.getTreeItemId(self, editingContext));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,25 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.sirius.components.core.api.IContentService;
import org.eclipse.sirius.components.core.api.IEditingContext;
import org.eclipse.sirius.components.core.api.IIdentityService;
import org.eclipse.sirius.components.core.api.IObjectService;
import org.eclipse.sirius.web.application.UUIDParser;
import org.eclipse.sirius.web.application.editingcontext.EditingContext;
import org.eclipse.sirius.web.application.views.explorer.services.api.IExplorerServices;
import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.RepresentationMetadata;
import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.services.api.IRepresentationMetadataSearchService;
import org.eclipse.syson.services.UtilService;
import org.eclipse.syson.services.api.ISysONResourceService;
import org.eclipse.syson.sysml.Element;
import org.eclipse.syson.sysml.Namespace;
import org.eclipse.syson.sysml.ViewUsage;
import org.eclipse.syson.sysml.util.ElementUtil;
import org.eclipse.syson.tree.explorer.view.fragments.LibrariesDirectory;
import org.eclipse.syson.tree.explorer.view.services.api.ISysONDefaultExplorerService;
Expand Down Expand Up @@ -57,17 +61,20 @@ public class SysONDefaultExplorerServices implements ISysONDefaultExplorerServic
private final ISysONExplorerFilterService filterService;

private final UtilService utilService = new UtilService();

private final ISysONResourceService sysONResourceService;

private final IObjectService objectService;

public SysONDefaultExplorerServices(IIdentityService identityService, IContentService contentService, IRepresentationMetadataSearchService representationMetadataSearchService, IExplorerServices explorerServices,
ISysONExplorerFilterService filterService, final ISysONResourceService sysONResourceService) {
ISysONExplorerFilterService filterService, final ISysONResourceService sysONResourceService, IObjectService objectService) {
this.identityService = Objects.requireNonNull(identityService);
this.contentService = Objects.requireNonNull(contentService);
this.representationMetadataSearchService = Objects.requireNonNull(representationMetadataSearchService);
this.explorerServices = Objects.requireNonNull(explorerServices);
this.filterService = Objects.requireNonNull(filterService);
this.sysONResourceService = Objects.requireNonNull(sysONResourceService);
this.objectService = Objects.requireNonNull(objectService);
}

@Override
Expand All @@ -89,10 +96,22 @@ public List<Object> getElements(IEditingContext editingContext, List<String> act
}

@Override
public String getTreeItemId(Object self) {
public String getTreeItemId(Object self, IEditingContext editingContext) {
String id = null;
if (self instanceof ISysONExplorerFragment fragment) {
id = fragment.getId();
} else if (self instanceof ViewUsage viewUsage) {
var optionalSemanticDataId = new UUIDParser().parse(editingContext.getId());
if (optionalSemanticDataId.isPresent()) {
String viewUsageId = this.identityService.getId(viewUsage);
id = this.representationMetadataSearchService.findAllRepresentationMetadataBySemanticDataAndTargetObjectId(AggregateReference.to(optionalSemanticDataId.get()), viewUsageId)
.stream()
.map(RepresentationMetadata::getId)
.filter(Objects::nonNull)
.map(UUID::toString)
.findAny()
.orElse(viewUsageId);
}
} else {
id = this.explorerServices.getTreeItemId(self);
}
Expand Down Expand Up @@ -135,33 +154,27 @@ public boolean hasChildren(Object self, IEditingContext editingContext, List<Str
hasChildren = !this.filterService.applyFilters(resource.getContents(), activeFilterIds).isEmpty();
} else if (self instanceof Element element) {
List<Object> contents = this.filterService.applyFilters(this.contentService.getContents(self), activeFilterIds);
hasChildren = !contents.isEmpty() && contents.stream().anyMatch(e -> !(e instanceof EAnnotation))
|| this.hasRepresentation(element, editingContext);
} else {
hasChildren = explorerServices.hasChildren(self, editingContext);
hasChildren = !contents.isEmpty() && contents.stream().anyMatch(e -> !(e instanceof EAnnotation));
} else if (self instanceof EObject eObject) {
hasChildren = !eObject.eContents().isEmpty();
}
return hasChildren;
}

private boolean hasRepresentation(EObject self, IEditingContext editingContext) {
var optionalSemanticDataId = new UUIDParser().parse(editingContext.getId());
if (optionalSemanticDataId.isPresent()) {
String id = this.identityService.getId(self);
return this.representationMetadataSearchService.existAnyRepresentationMetadataForSemanticDataAndTargetObjectId(AggregateReference.to(optionalSemanticDataId.get()), id);
}
return false;
}

@Override
public List<Object> getChildren(Object self, IEditingContext editingContext, List<String> expandedIds, List<String> activeFilterIds) {
List<Object> result = new ArrayList<>();
String id = this.getTreeItemId(self);
String id = this.getTreeItemId(self, editingContext);
if (self instanceof ISysONExplorerFragment fragment) {
if (expandedIds.contains(id)) {
result.addAll(fragment.getChildren(editingContext, expandedIds, activeFilterIds));
}
} else if (self instanceof ViewUsage) {
if (expandedIds.contains(id)) {
result.addAll(this.objectService.getContents(self));
}
} else {
result.addAll(this.explorerServices.getDefaultChildren(self, editingContext, expandedIds));
result.addAll(this.explorerServices.getDefaultChildren(self, editingContext, expandedIds).stream().filter(child -> !(child instanceof RepresentationMetadata)).toList());
}

result = this.filterService.applyFilters(result, activeFilterIds);
Expand Down Expand Up @@ -201,10 +214,10 @@ public boolean isEditable(Object self) {
} else if (self instanceof Resource resource) {
result = !(this.filterService.isUserLibrary(resource))
&& resource.getContents().stream()
.filter(Namespace.class::isInstance)
.map(Namespace.class::cast)
.flatMap(namespace -> namespace.getOwnedElement().stream())
.noneMatch(ElementUtil::isFromStandardLibrary);
.filter(Namespace.class::isInstance)
.map(Namespace.class::cast)
.flatMap(namespace -> namespace.getOwnedElement().stream())
.noneMatch(ElementUtil::isFromStandardLibrary);
}
return result;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2024 Obeo.
* Copyright (c) 2024, 2025 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 @@ -23,7 +23,7 @@
*/
public interface ISysONDefaultExplorerService {

String getTreeItemId(Object self);
String getTreeItemId(Object self, IEditingContext editingContext);

String getKind(Object self);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2024 Obeo.
* Copyright (c) 2024, 2025 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 @@ -23,7 +23,7 @@
*/
public interface ISysONExplorerService {

String getTreeItemId(Object self);
String getTreeItemId(Object self, IEditingContext editingContext);

String getKind(Object self);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,14 @@

/**
* Tests the {@link SysONExplorerFilterService} class.
*
*
* @author dvojtise
*/
public class SysONDefaultExplorerServicesTest {

private final ISysONResourceService sysONResourceService = new SysONResourceService();
private static EditingContext editingContext;

private SysONDefaultExplorerServices sysONDefaultExplorerServices;

private final ISysONResourceService sysONResourceService = new SysONResourceService();

@BeforeAll
static void createEditingContext() {
Expand Down Expand Up @@ -99,9 +97,9 @@ public void hasChildrenCanHandleNonSysmlContent() {
EAttribute c1a2 = EcoreFactory.eINSTANCE.createEAttribute();
c1.getEStructuralFeatures().add(c1a2);

assertThat(sysONDefaultExplorerServices.hasChildren(ePackage, editingContext, List.of(), List.of())).isTrue();
assertThat(sysONDefaultExplorerServices.hasChildren(c1, editingContext, List.of(), List.of())).isTrue();
assertThat(sysONDefaultExplorerServices.hasChildren(c1a1, editingContext, List.of(), List.of())).isFalse();
assertThat(this.sysONDefaultExplorerServices.hasChildren(ePackage, editingContext, List.of(), List.of())).isTrue();
assertThat(this.sysONDefaultExplorerServices.hasChildren(c1, editingContext, List.of(), List.of())).isTrue();
assertThat(this.sysONDefaultExplorerServices.hasChildren(c1a1, editingContext, List.of(), List.of())).isFalse();
}

/**
Expand Down Expand Up @@ -163,7 +161,8 @@ public boolean test(Object arg0) {

ISysONExplorerFilterService filterService = new SysONExplorerFilterService(this.sysONResourceService);

sysONDefaultExplorerServices = new SysONDefaultExplorerServices(identityService, contentService, representationMetadataSearchService, explorerServices, filterService, this.sysONResourceService);
this.sysONDefaultExplorerServices = new SysONDefaultExplorerServices(identityService, contentService, representationMetadataSearchService, explorerServices, filterService,
this.sysONResourceService, new IObjectService.NoOp());
}

}
25 changes: 25 additions & 0 deletions doc/adrs/002_support_representation_only_on_viewusage.Adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
= ADR-002 - Support representation only through ViewUsage

== Context

In the SysMLv2 specification, the `ViewDefinition` concept allows defining how to render elements (textual, graphical...) and `ViewUsage` is an instance of a `ViewDefinition`.

== Decision

* Explorer in SysON should not provide representation anymore.
Change the behavior of the Sysml custom explorer defined in `SysONExplorerTreeDescriptionProvider` to filter representation from children.

* `ViewUsage` should open child representation.
A `ViewUsage` can only manage exactly one representation.

NOTE: The limitation of a single representation per `ViewUsage` should be ensured by the representation creation mechanism that automatically creates a `ViewUsage` as parent.
In the same way, the creation of a `ViewUsage` must automatically create a representation as its child.

To open a representation, the `selection` must be set to the representation id.
Change the behavior in `SysONExplorerTreeDescriptionProvider` to return by the `treeItemIdExpression` the representation id for `ViewUsage` elements.
The problem with changing only the `treeId` so that it points to the representation id instead of the semantic object id is that all other mechanisms (like the tree's
`getChildren` or `dropElementFromExplorer`, for example) no longer work.
All these mechanisms need to be customized to manage `ViewUsage`.

NOTE: When selecting a `ViewUsage`, the detail views only display properties from the representation.

Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ Publishing a project with a dependency to a library now correctly produces a lib

image::view-usage-nodes.png[ViewUsage nodes, width=65%,height=65%]

- Diagram representations like _General View_ are no longer directly available in the tree explorer.
They are now managed through dedicated `ViewUsage` element.
To open a diagram, you should now select the `ViewUsage` in the explorer.


== Improvements

Expand Down
Loading