11/*******************************************************************************
2- * Copyright (c) 2025 Obeo.
2+ * Copyright (c) 2025, 2026 Obeo.
33 * This program and the accompanying materials
44 * are made available under the terms of the Eclipse Public License v2.0
55 * which accompanies this distribution, and is available at
1212 *******************************************************************************/
1313package org .eclipse .syson .application .publication ;
1414
15- import java .util .ArrayList ;
16- import java .util .Collection ;
1715import java .util .List ;
1816import java .util .Objects ;
19- import java .util .Optional ;
20- import java .util .Set ;
21- import java .util .UUID ;
22- import java .util .stream .Collectors ;
2317
24- import org .eclipse .emf .ecore .EObject ;
25- import org .eclipse .emf .ecore .resource .Resource ;
26- import org .eclipse .emf .ecore .resource .ResourceSet ;
2718import org .eclipse .sirius .components .core .api .ErrorPayload ;
2819import org .eclipse .sirius .components .core .api .IEditingContextSearchService ;
2920import org .eclipse .sirius .components .core .api .IPayload ;
30- import org .eclipse .sirius .components .core .api .SuccessPayload ;
31- import org .eclipse .sirius .components .emf .ResourceMetadataAdapter ;
32- import org .eclipse .sirius .components .emf .services .api .IEMFEditingContext ;
33- import org .eclipse .sirius .components .events .ICause ;
3421import org .eclipse .sirius .components .representations .Message ;
3522import org .eclipse .sirius .components .representations .MessageLevel ;
36- import org .eclipse .sirius .web .application .editingcontext .services .DocumentData ;
37- import org .eclipse .sirius .web .application .editingcontext .services .EPackageEntry ;
38- import org .eclipse .sirius .web .application .editingcontext .services .api .IResourceToDocumentService ;
3923import org .eclipse .sirius .web .application .library .dto .PublishLibrariesInput ;
40- import org .eclipse .sirius .web .application .library .services .LibraryMetadataAdapter ;
24+ import org .eclipse .sirius .web .application .library .services .api . ILibraryApplicationService ;
4125import org .eclipse .sirius .web .application .library .services .api .ILibraryPublicationHandler ;
42- import org .eclipse .sirius .web .application .studio .services .library .api .DependencyGraph ;
43- import org .eclipse .sirius .web .domain .boundedcontexts .library .Library ;
44- import org .eclipse .sirius .web .domain .boundedcontexts .library .services .api .ILibrarySearchService ;
45- import org .eclipse .sirius .web .domain .boundedcontexts .project .Project ;
4626import org .eclipse .sirius .web .domain .boundedcontexts .project .services .api .IProjectSearchService ;
4727import org .eclipse .sirius .web .domain .boundedcontexts .projectsemanticdata .services .api .IProjectSemanticDataSearchService ;
48- import org .eclipse .sirius .web .domain .boundedcontexts .semanticdata .Document ;
49- import org .eclipse .sirius .web .domain .boundedcontexts .semanticdata .SemanticData ;
50- import org .eclipse .sirius .web .domain .boundedcontexts .semanticdata .services .api .ISemanticDataCreationService ;
51- import org .eclipse .sirius .web .domain .services .IResult ;
52- import org .eclipse .sirius .web .domain .services .Success ;
53- import org .eclipse .syson .application .publication .api .ISysONLibraryDependencyCollector ;
54- import org .eclipse .syson .sysml .SysmlPackage ;
55- import org .eclipse .syson .sysml .util .ElementUtil ;
56- import org .slf4j .Logger ;
57- import org .slf4j .LoggerFactory ;
28+ import org .eclipse .syson .application .publication .api .ISysMLLibraryPublisher ;
5829import org .springframework .data .jdbc .core .mapping .AggregateReference ;
5930import org .springframework .stereotype .Service ;
6031
6132/**
6233 * {@link ILibraryPublicationHandler} for publishing libraries in SysON.
6334 *
35+ * @see ILibraryApplicationService
6436 * @see SysONLibraryPublicationListener
6537 * @author flatombe
6638 */
6739@ Service
6840public class SysONLibraryPublicationHandler implements ILibraryPublicationHandler {
69- private static final Logger LOGGER = LoggerFactory .getLogger (SysONLibraryPublicationHandler .class );
70-
7141 private final IEditingContextSearchService editingContextSearchService ;
7242
73- private final ISemanticDataCreationService semanticDataCreationService ;
74-
75- private final IResourceToDocumentService resourceToDocumentService ;
76-
7743 private final IProjectSemanticDataSearchService projectSemanticDataSearchService ;
7844
7945 private final IProjectSearchService projectSearchService ;
8046
81- private final ILibrarySearchService librarySearchService ;
82-
83- private final ISysONLibraryDependencyCollector sysonLibraryDependencyCollector ;
47+ private final ISysMLLibraryPublisher sysONSysMLLibraryPublisher ;
8448
8549 public SysONLibraryPublicationHandler (final IEditingContextSearchService editingContextSearchService ,
86- final ISemanticDataCreationService semanticDataCreationService ,
87- final IResourceToDocumentService resourceToDocumentService ,
88- final IProjectSemanticDataSearchService projectSemanticDataSearchService , final IProjectSearchService projectSearchService ,
89- final ILibrarySearchService librarySearchService ,
90- final ISysONLibraryDependencyCollector sysonLibraryDependencyCollector ) {
91- this .projectSearchService = Objects .requireNonNull (projectSearchService );
50+ final IProjectSemanticDataSearchService projectSemanticDataSearchService ,
51+ final IProjectSearchService projectSearchService ,
52+ final ISysMLLibraryPublisher sysONSysMLLibraryPublisher ) {
9253 this .editingContextSearchService = Objects .requireNonNull (editingContextSearchService );
93- this .semanticDataCreationService = Objects .requireNonNull (semanticDataCreationService );
94- this .resourceToDocumentService = Objects .requireNonNull (resourceToDocumentService );
9554 this .projectSemanticDataSearchService = Objects .requireNonNull (projectSemanticDataSearchService );
96- this .librarySearchService = Objects .requireNonNull (librarySearchService );
97- this .sysonLibraryDependencyCollector = Objects .requireNonNull (sysonLibraryDependencyCollector );
55+ this .projectSearchService = Objects .requireNonNull (projectSearchService );
56+ this .sysONSysMLLibraryPublisher = Objects .requireNonNull (sysONSysMLLibraryPublisher );
9857 }
9958
10059 @ Override
@@ -107,147 +66,14 @@ public IPayload handle(final PublishLibrariesInput input) {
10766 return this .projectSearchService .findById (input .projectId ())
10867 .map (project -> this .projectSemanticDataSearchService .findByProjectId (AggregateReference .to (input .projectId ()))
10968 .flatMap (projectSemanticData -> this .editingContextSearchService .findById (projectSemanticData .getSemanticData ().getId ().toString ()))
110- .filter (IEMFEditingContext .class ::isInstance )
111- .map (IEMFEditingContext .class ::cast )
112- .map (editingContext -> this .handle (input , project , editingContext .getDomain ().getResourceSet ()))
113- .orElseGet (() -> new ErrorPayload (input .id (),
114- List .of (new Message ("Could not find the editing context of project '%s'." .formatted (project .getName ()), MessageLevel .ERROR )))))
69+ .map (editingContext -> this .sysONSysMLLibraryPublisher .publish (
70+ input ,
71+ editingContext ,
72+ project .getId (),
73+ project .getName (),
74+ input .version (),
75+ input .description ()))
76+ .orElseGet (() -> new ErrorPayload (input .id (), List .of (new Message ("Could not find the editing context of project '%s'." .formatted (project .getName ()), MessageLevel .ERROR )))))
11577 .orElseGet (() -> new ErrorPayload (input .id (), List .of (new Message ("Could not find project with ID '%s'." .formatted (input .projectId ()), MessageLevel .ERROR ))));
11678 }
117-
118- protected IPayload handle (final PublishLibrariesInput input , final Project project , final ResourceSet resourceSet ) {
119- final IPayload result ;
120-
121- final String libraryName = project .getName ();
122- final String libraryVersion = input .version ();
123-
124- if (this .librarySearchService .findByNamespaceAndNameAndVersion (project .getId (), libraryName , libraryVersion ).isPresent ()) {
125- // There is already a Library from our namespace with that name and version.
126- result = new ErrorPayload (input .id (),
127- List .of (new Message ("Library '%s' (version '%s') already exists in namespace '%s'." .formatted (libraryName , libraryVersion , project .getId ()), MessageLevel .ERROR )));
128- } else {
129- final Set <Resource > resourcesToPublish = this .getProperSysMLRootContents (resourceSet );
130- if (!resourcesToPublish .isEmpty ()) {
131- DependencyGraph <Resource > dependencyGraph = this .sysonLibraryDependencyCollector .collectDependencies (resourceSet );
132-
133- List <AggregateReference <SemanticData , UUID >> dependencies = this .getDependencies (dependencyGraph , resourcesToPublish );
134-
135- final Optional <SemanticData > maybePublishedLibrarySemanticData = this .publishAsLibrary (input , resourcesToPublish , libraryName , libraryVersion , dependencies );
136- // After this transaction is done, SysONLibraryPublicationListener reacts by also creating the
137- // associated Library metadata.
138-
139- result = maybePublishedLibrarySemanticData
140- .map (publishedLibrary -> (IPayload ) new SuccessPayload (input .id (),
141- List .of (new Message (
142- "Successfully published the SysML contents of project '%s' as version '%s' of library '%s'." .formatted (project .getName (),
143- libraryVersion ,
144- libraryName ),
145- MessageLevel .SUCCESS ))))
146- .orElseGet (
147- () -> new ErrorPayload (input .id (),
148- List .of (new Message ("Failed to publish the SysML contents of project '%s' as a library." .formatted (project .getName (), project .getId ()),
149- MessageLevel .ERROR ))));
150- } else {
151- result = new ErrorPayload (input .id (),
152- List .of (new Message ("There are no SysML contents in project '%s' to publish as library." .formatted (project .getName ()), MessageLevel .ERROR )));
153- }
154- }
155- return result ;
156- }
157-
158- private List <AggregateReference <SemanticData , UUID >> getDependencies (DependencyGraph <Resource > dependencyGraph , final Set <Resource > resourcesToPublish ) {
159- List <AggregateReference <SemanticData , UUID >> dependencies = new ArrayList <>();
160-
161- for (Resource resourceToPublish : resourcesToPublish ) {
162- for (Resource dependencyCandidate : dependencyGraph .getDependencies (resourceToPublish )) {
163- Optional <LibraryMetadataAdapter > optionalLibraryMetadata = dependencyCandidate .eAdapters ().stream ()
164- .filter (LibraryMetadataAdapter .class ::isInstance )
165- .map (LibraryMetadataAdapter .class ::cast )
166- .findFirst ();
167- if (optionalLibraryMetadata .isPresent ()) {
168- LibraryMetadataAdapter libraryMetadataAdapter = optionalLibraryMetadata .get ();
169- this .librarySearchService .findByNamespaceAndNameAndVersion (libraryMetadataAdapter .getNamespace (), libraryMetadataAdapter .getName (), libraryMetadataAdapter .getVersion ())
170- .map (Library ::getSemanticData )
171- .ifPresentOrElse (dependencies ::add , () -> LOGGER .warn ("Cannot retrieve library {}:{}:{}" , libraryMetadataAdapter .getNamespace (), libraryMetadataAdapter .getName (),
172- libraryMetadataAdapter .getVersion ()));
173- }
174- // Ignore the resource if it isn't a library: all non-library resources are published in a single
175- // library in SysON.
176- }
177- }
178- return dependencies .stream ()
179- .distinct ()
180- .toList ();
181- }
182-
183- protected Optional <SemanticData > publishAsLibrary (final ICause parentCause , final Set <Resource > resources , final String name , final String version ,
184- final List <AggregateReference <SemanticData , UUID >> dependencies ) {
185- final ICause cause = new SysONPublishedLibrarySemanticDataCreationRequested (parentCause , name );
186- // Remove the imported flag on the resource: the resource is now a library, and its read-only/read-write nature
187- // should be determined by its import kind (reference or copy), and not whether it was imported from a textual
188- // SysML file in the first place.
189- resources .forEach (resource -> ElementUtil .setIsImported (resource , false ));
190- return this .createSemanticData (cause , resources , dependencies );
191- }
192-
193- protected Optional <SemanticData > createSemanticData (final ICause event , final Collection <Resource > resources , final List <AggregateReference <SemanticData , UUID >> dependencies ) {
194- final List <DocumentData > documentDatas = resources .stream ()
195- .map (resource -> this .resourceToDocumentService .toDocument (resource , false ))
196- .filter (Optional ::isPresent )
197- .map (Optional ::get )
198- .toList ();
199- final List <Document > documents = documentDatas .stream ()
200- .map (DocumentData ::document )
201- .toList ();
202- final List <String > domains = documentDatas .stream ()
203- .map (DocumentData ::ePackageEntries )
204- .flatMap (List ::stream )
205- .map (EPackageEntry ::nsURI )
206- .distinct ()
207- .toList ();
208-
209- final IResult <SemanticData > creationResult = this .semanticDataCreationService .create (event , documents , domains , dependencies );
210- if (creationResult instanceof Success <SemanticData > creationSuccess ) {
211- return Optional .ofNullable (creationSuccess .data ());
212- } else {
213- return Optional .empty ();
214- }
215- }
216-
217- protected Set <Resource > getProperSysMLRootContents (final ResourceSet resourceSet ) {
218- Objects .requireNonNull (resourceSet );
219-
220- final List <Resource > dependencies = this .getDependenciesToPublishedLibraries (resourceSet );
221- return resourceSet .getResources ().stream ()
222- .filter (resource -> !dependencies .contains (resource ))
223- .collect (Collectors .toSet ());
224- }
225-
226- protected List <Resource > getDependenciesToPublishedLibraries (ResourceSet resourceSet ) {
227- return resourceSet .getResources ().stream ()
228- .filter (resource -> this .getLibraryMetadata (resource ).isPresent ()
229- || resource .getURI ().scheme ().equals (ElementUtil .KERML_LIBRARY_SCHEME )
230- || resource .getURI ().scheme ().equals (ElementUtil .SYSML_LIBRARY_SCHEME ))
231- .toList ();
232- }
233-
234- protected Optional <LibraryMetadataAdapter > getLibraryMetadata (final Resource resource ) {
235- return resource .eAdapters ().stream ()
236- .filter (LibraryMetadataAdapter .class ::isInstance )
237- .map (LibraryMetadataAdapter .class ::cast )
238- .findFirst ();
239- }
240-
241- protected boolean isSysmlContent (final EObject rootEObject ) {
242- return rootEObject .eClass ().getEPackage () == SysmlPackage .eINSTANCE ;
243- }
244-
245- protected String getResourceName (final Resource resource ) {
246- return resource .eAdapters ().stream ()
247- .filter (ResourceMetadataAdapter .class ::isInstance )
248- .map (ResourceMetadataAdapter .class ::cast )
249- .map (ResourceMetadataAdapter ::getName )
250- .findFirst ()
251- .orElse (null );
252- }
25379}
0 commit comments