diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 15cb0d1bb..9eabec92c 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -12,10 +12,10 @@ - [cleanup] The definition of the tools specific to the requirements table has been moved to the backend. As a result, the following GraphQL mutations have been removed `exposeRequirements` and `createRequirement`. - [test] Reduce our call to `DiagramNavigator#nodeWithTargetObjectLabel` which has been deprecated for removal. -+ The API of `ToolTester#invokeTool` and `NodeCreationTestsService#createNode` has been updated accordingly to take a `targetObjectId` instead of a `label`. +The API of `ToolTester#invokeTool` and `NodeCreationTestsService#createNode` has been updated accordingly to take a `targetObjectId` instead of a `label`. - https://github.com/eclipse-syson/syson/issues/2045[#2045] [diagrams] The service `ViewNodeService#revealCompartment` has been changed to reveal a compartment only when it is needed. -+ Only reveal a compartment of a node when none of the revealed compartment can display the `targetElement`. -+ If many compartment candidates exist, choose the first free form compartment. +Only reveal a compartment of a node when none of the revealed compartment can display the `targetElement`. +If many compartment candidates exist, choose the first free form compartment. - [services] As part of the migration to the new diagram service architecture, the following methods have been moved out of `ViewNodeService` (which as been deleted): + ** `List getExposedElements(Element self, EClass domainType, List ancestors, IEditingContext editingContext, DiagramContext diagramContext)` to `DiagramQueryAQLService.getExposedElements(Element self, EClass domainType, List ancestors, IEditingContext editingContext, DiagramContext diagramContext)` @@ -34,6 +34,8 @@ As a result, the following GraphQL mutations have been removed `exposeRequiremen ** `org.eclipse.syson.diagram.common.view.services.RevealCompartmentSwitch` to `org.eclipse.syson.diagram.services.utils.RevealCompartmentSwitch` + Also, a new helper method `DiagramQueryAQLService.isNotAncestorOf(...)` has been added to support edge preconditions previously written as `aql:not graphicalEdgeSource.isAncestorOf(graphicalEdgeTarget, cache)`. +- https://github.com/eclipse-syson/syson/issues/2143[#2143] [configuration] The `relativePathToDirectoryContainingLibraryFiles` field in `SysONLibraryLoadingDefinition` now expects a `String` instead of a `java.nio.file.Path`. +This change ensures compatibility with classpath resource resolution, which requires forward slashes (`/`) as path separators. === Dependency update @@ -53,6 +55,7 @@ Also, a new helper method `DiagramQueryAQLService.isNotAncestorOf(...)` has been - https://github.com/eclipse-syson/syson/issues/2045[#2045] [diagrams] In Interconnection View diagrams, fix an issue where the `parts` compartment of a `PartDefinition` graphical node was incorrectly revealed when creating a `PartUsage` from the `PartDefinition` graphical node, even if the `interconnection` compartment was already visible. - https://github.com/eclipse-syson/syson/issues/2094[#2094] [libraries] Ensure library documents are read-only. +- https://github.com/eclipse-syson/syson/issues/2143[#2143] [configuration] Fixed classpath resource resolution on Windows by enforcing forward slashes (`/`) in library paths. === Improvements diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/configuration/SysONDefaultLibrariesConfiguration.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/configuration/SysONDefaultLibrariesConfiguration.java index 21c8283bc..70cf6bb93 100644 --- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/configuration/SysONDefaultLibrariesConfiguration.java +++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/configuration/SysONDefaultLibrariesConfiguration.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024, 2025 Obeo. + * Copyright (c) 2024, 2026 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 @@ -12,7 +12,6 @@ *******************************************************************************/ package org.eclipse.syson.application.configuration; -import java.nio.file.Paths; import java.time.Duration; import java.time.Instant; import java.util.Collections; @@ -47,14 +46,14 @@ public class SysONDefaultLibrariesConfiguration { public static final SysONLibraryLoadingDefinition KERML = new SysONLibraryLoadingDefinition( "KerML Standard Library", ElementUtil.KERML_LIBRARY_SCHEME, - Paths.get("kerml.libraries"), + "kerml.libraries", Collections.singletonList(JsonResourceFactoryImpl.EXTENSION), new JSONResourceFactory()::createResource); public static final SysONLibraryLoadingDefinition SYSML = new SysONLibraryLoadingDefinition( "SysML Standard Library", ElementUtil.SYSML_LIBRARY_SCHEME, - Paths.get("sysml.libraries"), + "sysml.libraries", Collections.singletonList(JsonResourceFactoryImpl.EXTENSION), new JSONResourceFactory()::createResource); diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/libraries/SysONLibraryLoader.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/libraries/SysONLibraryLoader.java index 46a789917..7d394265c 100644 --- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/libraries/SysONLibraryLoader.java +++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/libraries/SysONLibraryLoader.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2025 Obeo. + * Copyright (c) 2025, 2026 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 @@ -132,7 +132,7 @@ public List findAllLibraryResourcesFromApplicationClasspath(f final List allResourcesForLibraryLoadingDefinition = new ArrayList<>(); final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); for (final String fileExtension : definition.fileExtensions()) { - final String locationPattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + definition.relativePathToDirectoryContainingLibraryFiles().toString() + "/*." + fileExtension; + final String locationPattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + definition.relativePathToDirectoryContainingLibraryFiles() + "/*." + fileExtension; final List resources = new ArrayList<>(); try { @@ -141,7 +141,7 @@ public List findAllLibraryResourcesFromApplicationClasspath(f LOGGER.error("There was an unexpected issue while attempting to retrieve %s resources at location: %s".formatted(definition.familyName(), locationPattern), ioException); } - resources.stream().map(resource -> new ClassPathResource(definition.relativePathToDirectoryContainingLibraryFiles().resolve(resource.getFilename()).toString())) + resources.stream().map(resource -> new ClassPathResource(definition.relativePathToDirectoryContainingLibraryFiles() + "/" + resource.getFilename())) .forEach(allResourcesForLibraryLoadingDefinition::add); } diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/libraries/SysONLibraryLoadingDefinition.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/libraries/SysONLibraryLoadingDefinition.java index 6e9a8d95c..8b9d83762 100644 --- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/libraries/SysONLibraryLoadingDefinition.java +++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/libraries/SysONLibraryLoadingDefinition.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2025 Obeo. + * Copyright (c) 2025, 2026 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 @@ -12,7 +12,6 @@ *******************************************************************************/ package org.eclipse.syson.application.libraries; -import java.nio.file.Path; import java.util.List; import java.util.Objects; import java.util.function.Function; @@ -24,7 +23,9 @@ /** * Defines how to identify, retrieve and load the different files constituting a SysON library. * - * Note that the resources must be in the classpath of the application. + * The resources must be in the classpath of the application. + * The {@code relativePathToDirectoryContainingLibraryFiles} must use forward slashes ("/") as separators, + * as it is intended to be used in the classpath (e.g., JAR files or classpath directories). * * @author flatombe * @see SysONDefaultLibrariesConfiguration#KERML @@ -33,7 +34,7 @@ public record SysONLibraryLoadingDefinition( String familyName, String scheme, - Path relativePathToDirectoryContainingLibraryFiles, + String relativePathToDirectoryContainingLibraryFiles, List fileExtensions, Function resourceLoadingBehavior) { @@ -45,9 +46,7 @@ public record SysONLibraryLoadingDefinition( * @param scheme * the (non-{@code null}) scheme to use for the EMF {@link URI}s used to load resources of this library. * @param relativePathToDirectoryContainingLibraryFiles - * the (non-{@code null}) relative {@link Path} to the directory containing the library files. If - * set to {@code foo/bar} then the library files are expected to be in directory - * {@code src/main/resources/foo/bar}. + * the (non-{@code null}) relative path (using forward slashes "/") to the directory containing the library files in the classpath. * @param fileExtensions * the (non-{@code null}) {@link List} of file extensions to look for in * {@code relativePathToDirectoryContainingLibraryFiles}. @@ -61,11 +60,15 @@ public record SysONLibraryLoadingDefinition( Objects.requireNonNull(fileExtensions); Objects.requireNonNull(resourceLoadingBehavior); - if (relativePathToDirectoryContainingLibraryFiles.isAbsolute()) { - throw new IllegalArgumentException("Path '%s' is absolute, but a relative one was expected.".formatted(relativePathToDirectoryContainingLibraryFiles.toString())); + if (relativePathToDirectoryContainingLibraryFiles.startsWith("/")) { + throw new IllegalArgumentException("Path '%s' is absolute, but a relative one was expected.".formatted(relativePathToDirectoryContainingLibraryFiles)); } - if (relativePathToDirectoryContainingLibraryFiles.toFile().exists() && !relativePathToDirectoryContainingLibraryFiles.toFile().isDirectory()) { - throw new IllegalArgumentException("Path '%s' exists but is not a directory.".formatted(relativePathToDirectoryContainingLibraryFiles.toString())); + + // Validate that the path does not contain Windows-style separators + if (relativePathToDirectoryContainingLibraryFiles.contains("\\")) { + throw new IllegalArgumentException( + "Path '%s' contains Windows-style separators ('\\'). Only forward slashes ('/') are allowed for classpath resources.".formatted(relativePathToDirectoryContainingLibraryFiles) + ); } } diff --git a/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/configuration/SysONLibraryLoadingDefinitionTest.java b/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/configuration/SysONLibraryLoadingDefinitionTest.java new file mode 100644 index 000000000..8ac79efb2 --- /dev/null +++ b/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/configuration/SysONLibraryLoadingDefinitionTest.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2026 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.syson.application.configuration; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; + +import java.util.List; +import java.util.function.Function; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.syson.application.libraries.SysONLibraryLoadingDefinition; +import org.junit.jupiter.api.Test; + +/** + * Test about the library loading definition. + * + * @author frouene + */ +public class SysONLibraryLoadingDefinitionTest { + + private static final String VALID_FAMILY_NAME = "TestLibrary"; + private static final String VALID_SCHEME = "test"; + private static final String VALID_RELATIVE_PATH = "name/library"; + private static final List VALID_EXTENSIONS = List.of("json", "xml"); + private static final Function VALID_LOADING_BEHAVIOR = mock(Function.class); + + @Test + void testValidDefinition() { + SysONLibraryLoadingDefinition definition = new SysONLibraryLoadingDefinition( + VALID_FAMILY_NAME, + VALID_SCHEME, + VALID_RELATIVE_PATH, + VALID_EXTENSIONS, + VALID_LOADING_BEHAVIOR + ); + + assertNotNull(definition); + assertEquals(VALID_FAMILY_NAME, definition.familyName()); + assertEquals(VALID_SCHEME, definition.scheme()); + assertEquals(VALID_RELATIVE_PATH, definition.relativePathToDirectoryContainingLibraryFiles()); + assertEquals(VALID_EXTENSIONS, definition.fileExtensions()); + assertEquals(VALID_LOADING_BEHAVIOR, definition.resourceLoadingBehavior()); + } + + @Test + void testAbsolutePath() { + assertThrows(IllegalArgumentException.class, () -> + new SysONLibraryLoadingDefinition( + VALID_FAMILY_NAME, + VALID_SCHEME, + "/name/library", + VALID_EXTENSIONS, + VALID_LOADING_BEHAVIOR + ) + ); + } + + @Test + void testWindowsSeparators() { + assertThrows(IllegalArgumentException.class, () -> + new SysONLibraryLoadingDefinition( + VALID_FAMILY_NAME, + VALID_SCHEME, + "name\\library", + VALID_EXTENSIONS, + VALID_LOADING_BEHAVIOR + ) + ); + } + +}