From 983637f05f9d4b77436306f1eefccd22e4abd750 Mon Sep 17 00:00:00 2001 From: efilchenko Date: Mon, 8 Sep 2025 13:10:43 +0300 Subject: [PATCH 01/11] Duplicate definition: Case-insensitive FS on Windows and MacOS --- .../aspectmodel/resolver/fs/ModelsRoot.java | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java index 05154c295..87840395e 100644 --- a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java +++ b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java @@ -14,14 +14,23 @@ package org.eclipse.esmf.aspectmodel.resolver.fs; import java.io.File; +import java.io.IOException; import java.net.URI; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Objects; import java.util.stream.Stream; import org.eclipse.esmf.aspectmodel.urn.AspectModelUrn; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + public abstract class ModelsRoot { + + private static final Logger LOG = LoggerFactory.getLogger( ModelsRoot.class ); + private static final File EMPTY_FILE = new File( "" ); private final Path root; protected ModelsRoot( final Path root ) { @@ -43,6 +52,19 @@ public Stream paths() { public abstract Path directoryForNamespace( final AspectModelUrn urn ); public File determineAspectModelFile( final AspectModelUrn urn ) { - return directoryForNamespace( urn ).resolve( urn.getName() + ".ttl" ).toFile(); + Path path = directoryForNamespace( urn ).resolve( urn.getName() + ".ttl" ); + return resolveByCanonicalPath( path ); + } + + private static File resolveByCanonicalPath( Path path ) { + File file = path.toFile(); + try { + if ( Objects.equals( path.toString(), file.getCanonicalPath() ) ) { + return file; + } + } catch ( IOException exception ) { + LOG.error( "Error resolving canonical path for file: {}", file.getPath(), exception ); + } + return EMPTY_FILE; } } From 9a39a006f2672836efff9935e34f8c31286302aa Mon Sep 17 00:00:00 2001 From: efilchenko Date: Tue, 9 Sep 2025 12:10:39 +0300 Subject: [PATCH 02/11] add tests --- .../aspectmodel/resolver/fs/ModelsRoot.java | 2 +- .../resolver/fs/ModelsRootTest.java | 48 +++++++++++++++++++ .../src/test/resources/resolve/Aspect.ttl | 19 ++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java create mode 100644 core/esmf-aspect-meta-model-java/src/test/resources/resolve/Aspect.ttl diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java index 87840395e..eee2b27d4 100644 --- a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java +++ b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java @@ -56,7 +56,7 @@ public File determineAspectModelFile( final AspectModelUrn urn ) { return resolveByCanonicalPath( path ); } - private static File resolveByCanonicalPath( Path path ) { + private static File resolveByCanonicalPath( final Path path ) { File file = path.toFile(); try { if ( Objects.equals( path.toString(), file.getCanonicalPath() ) ) { diff --git a/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java b/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java new file mode 100644 index 000000000..6984ba9b9 --- /dev/null +++ b/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java @@ -0,0 +1,48 @@ +package org.eclipse.esmf.aspectmodel.resolver.fs; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.lang.reflect.Method; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; + +class ModelsRootTest { + + private static final File EMPTY_FILE = new File( "" ); + + @Test + void resolveByCanonicalPathShouldReturnFileWhenCanonicalPathMatches() throws Exception { + Path testPath = Paths.get( "src/test/resources/resolve", "Aspect.ttl" ).toAbsolutePath(); + + File result = invokeResolveByCanonicalPath( testPath ); + + assertThat( result ).isEqualTo( testPath.toFile() ); + } + + @Test + void resolveByCanonicalPathShouldReturnEmptyFileWhenCanonicalPathDoesNotMatch() throws Exception { + Path invalidPath = Paths.get( "src/test/resources/resolve", "aspect.ttl" ).toAbsolutePath(); + + File result = invokeResolveByCanonicalPath( invalidPath ); + + assertThat( result ).isEqualTo( EMPTY_FILE ); + } + + @Test + void resolveByCanonicalPathShouldReturnEmptyFileWhenIOExceptionOccurs() throws Exception { + Path pathCausingIOException = Paths.get( "pathCausingIOException" ); + + File result = invokeResolveByCanonicalPath( pathCausingIOException ); + + assertThat( result ).isEqualTo( EMPTY_FILE ); + } + + private static File invokeResolveByCanonicalPath( Path path ) throws Exception { + Method method = ModelsRoot.class.getDeclaredMethod( "resolveByCanonicalPath", Path.class ); + method.setAccessible( true ); + return (File) method.invoke( null, path ); + } +} \ No newline at end of file diff --git a/core/esmf-aspect-meta-model-java/src/test/resources/resolve/Aspect.ttl b/core/esmf-aspect-meta-model-java/src/test/resources/resolve/Aspect.ttl new file mode 100644 index 000000000..36a0750ac --- /dev/null +++ b/core/esmf-aspect-meta-model-java/src/test/resources/resolve/Aspect.ttl @@ -0,0 +1,19 @@ +# Copyright (c) 2021 Robert Bosch Manufacturing Solutions GmbH +# +# See the AUTHORS file(s) distributed with this work for additional +# information regarding authorship. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +# SPDX-License-Identifier: MPL-2.0 + +@prefix : . +@prefix samm: . + +:TestAspect a samm:Aspect ; + samm:name "TestAspect" ; + samm:preferredName "Test Aspect"@en ; + samm:properties ( ) ; + samm:operations ( ) . From 145190dabf7115a02f08fe097c7cc299b79143f8 Mon Sep 17 00:00:00 2001 From: efilchenko Date: Tue, 9 Sep 2025 17:11:04 +0300 Subject: [PATCH 03/11] fix --- .../esmf/aspectmodel/resolver/fs/ModelsRoot.java | 2 +- .../aspectmodel/resolver/fs/ModelsRootTest.java | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java index eee2b27d4..4f5eca965 100644 --- a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java +++ b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java @@ -59,7 +59,7 @@ public File determineAspectModelFile( final AspectModelUrn urn ) { private static File resolveByCanonicalPath( final Path path ) { File file = path.toFile(); try { - if ( Objects.equals( path.toString(), file.getCanonicalPath() ) ) { + if ( Objects.equals( path.normalize().toString(), file.getCanonicalPath() ) ) { return file; } } catch ( IOException exception ) { diff --git a/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java b/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java index 6984ba9b9..f2e2ba4d2 100644 --- a/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java +++ b/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java @@ -19,7 +19,20 @@ void resolveByCanonicalPathShouldReturnFileWhenCanonicalPathMatches() throws Exc File result = invokeResolveByCanonicalPath( testPath ); - assertThat( result ).isEqualTo( testPath.toFile() ); + assertThat( result ) + .matches( File::exists ) + .isEqualTo( testPath.toFile() ); + } + + @Test + void resolveByCanonicalPathShouldReturnFileWhenCanonicalPathMatchesForSpecificPath() throws Exception { + Path testPath = Paths.get( "src/test/resources/resolve_empty/../resolve", "Aspect.ttl" ).toAbsolutePath(); + + File result = invokeResolveByCanonicalPath( testPath ); + + assertThat( result ) + .matches( File::exists ) + .isEqualTo( testPath.toFile() ); } @Test From 32aa1104cd7b0dcc19aa529cd1c93b28b5a4d337 Mon Sep 17 00:00:00 2001 From: efilchenko Date: Wed, 10 Sep 2025 10:55:49 +0300 Subject: [PATCH 04/11] fix copyright --- .../esmf/aspectmodel/resolver/fs/ModelsRoot.java | 2 +- .../aspectmodel/resolver/fs/ModelsRootTest.java | 15 ++++++++++++++- .../src/test/resources/resolve/Aspect.ttl | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java index 4f5eca965..70b00ea02 100644 --- a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java +++ b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH + * Copyright (c) 2025 Robert Bosch Manufacturing Solutions GmbH * * See the AUTHORS file(s) distributed with this work for additional * information regarding authorship. diff --git a/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java b/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java index f2e2ba4d2..be1e5ac46 100644 --- a/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java +++ b/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java @@ -1,3 +1,16 @@ +/* + * Copyright (c) 2025 Robert Bosch Manufacturing Solutions GmbH + * + * See the AUTHORS file(s) distributed with this work for additional + * information regarding authorship. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + package org.eclipse.esmf.aspectmodel.resolver.fs; import static org.assertj.core.api.Assertions.assertThat; @@ -26,7 +39,7 @@ void resolveByCanonicalPathShouldReturnFileWhenCanonicalPathMatches() throws Exc @Test void resolveByCanonicalPathShouldReturnFileWhenCanonicalPathMatchesForSpecificPath() throws Exception { - Path testPath = Paths.get( "src/test/resources/resolve_empty/../resolve", "Aspect.ttl" ).toAbsolutePath(); + Path testPath = Paths.get( "src/test/resources/../resources/resolve", "Aspect.ttl" ).toAbsolutePath(); File result = invokeResolveByCanonicalPath( testPath ); diff --git a/core/esmf-aspect-meta-model-java/src/test/resources/resolve/Aspect.ttl b/core/esmf-aspect-meta-model-java/src/test/resources/resolve/Aspect.ttl index 36a0750ac..8bc3b7d3a 100644 --- a/core/esmf-aspect-meta-model-java/src/test/resources/resolve/Aspect.ttl +++ b/core/esmf-aspect-meta-model-java/src/test/resources/resolve/Aspect.ttl @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Robert Bosch Manufacturing Solutions GmbH +# Copyright (c) 2025 Robert Bosch Manufacturing Solutions GmbH # # See the AUTHORS file(s) distributed with this work for additional # information regarding authorship. From cc994bbc86c8cc052ceb37269cb4073854bc1f03 Mon Sep 17 00:00:00 2001 From: efilchenko Date: Tue, 16 Sep 2025 15:46:38 +0300 Subject: [PATCH 05/11] fix checkstyle --- .../esmf/aspectmodel/resolver/fs/ModelsRoot.java | 11 +++++++++++ .../esmf/aspectmodel/resolver/fs/ModelsRootTest.java | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java index 70b00ea02..07fcc3fc9 100644 --- a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java +++ b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java @@ -51,6 +51,17 @@ public Stream paths() { public abstract Path directoryForNamespace( final AspectModelUrn urn ); + /** + * Determines the aspect model file for the given {@link AspectModelUrn}. + * + *

Constructs the file path by resolving the namespace directory. + * Validates the file using its canonical path. + * + *

Returns an empty file if the resolution fails. + * + * @param urn the {@link AspectModelUrn} representing the aspect model. + * @return the resolved {@link File}, or an empty file if resolution fails. + */ public File determineAspectModelFile( final AspectModelUrn urn ) { Path path = directoryForNamespace( urn ).resolve( urn.getName() + ".ttl" ); return resolveByCanonicalPath( path ); diff --git a/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java b/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java index be1e5ac46..315e2dd93 100644 --- a/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java +++ b/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java @@ -58,7 +58,7 @@ void resolveByCanonicalPathShouldReturnEmptyFileWhenCanonicalPathDoesNotMatch() } @Test - void resolveByCanonicalPathShouldReturnEmptyFileWhenIOExceptionOccurs() throws Exception { + void resolveByCanonicalPathShouldReturnEmptyFileWhenIoExceptionOccurs() throws Exception { Path pathCausingIOException = Paths.get( "pathCausingIOException" ); File result = invokeResolveByCanonicalPath( pathCausingIOException ); From 75da4eb40a9d7b6c4568f5dcd3692ab6f57cd6fd Mon Sep 17 00:00:00 2001 From: efilchenko Date: Tue, 16 Sep 2025 17:38:40 +0300 Subject: [PATCH 06/11] add proposal changes --- .../aspectmodel/resolver/FileSystemStrategy.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/FileSystemStrategy.java b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/FileSystemStrategy.java index 72e9fa1df..650f81977 100644 --- a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/FileSystemStrategy.java +++ b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/FileSystemStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH + * Copyright (c) 2025 Robert Bosch Manufacturing Solutions GmbH * * See the AUTHORS file(s) distributed with this work for additional * information regarding authorship. @@ -33,14 +33,12 @@ import io.vavr.control.Try; import org.apache.jena.rdf.model.Model; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.jena.riot.RiotException; /** * Resolution strategy for Aspect model URNs that finds Aspect model files in the local file system. */ public class FileSystemStrategy implements ResolutionStrategy { - private static final Logger LOG = LoggerFactory.getLogger( FileSystemStrategy.class ); protected final ModelsRoot modelsRoot; /** @@ -79,7 +77,7 @@ public FileSystemStrategy( final ModelsRoot modelsRoot ) { * * @param aspectModelUrn The model URN * @return The model on success, {@link IllegalArgumentException} if the model file can not be read, - * {@link org.apache.jena.riot.RiotException} on parser error, {@link MalformedURLException} if the AspectModelUrn is invalid, + * {@link RiotException} on parser error, {@link MalformedURLException} if the AspectModelUrn is invalid, * {@link FileNotFoundException} if no file containing the element was found */ @Override @@ -94,7 +92,10 @@ public AspectModelFile apply( final AspectModelUrn aspectModelUrn, final Resolut new ModelResolutionException.LoadingFailure( aspectModelUrn, namedResourceFile.getAbsolutePath(), tryFile.getCause().getMessage(), tryFile.getCause() ) ); } - return tryFile.get(); + final RawAspectModelFile loadedFile = tryFile.get(); + if ( resolutionStrategySupport.containsDefinition( loadedFile, aspectModelUrn ) ) { + return loadedFile; + } } else { checkedLocations.add( new ModelResolutionException.LoadingFailure( aspectModelUrn, namedResourceFile.getAbsolutePath(), "File does not exist" ) ); From 6326c43ef529c8fc1d60afb58786e46d29c4cefc Mon Sep 17 00:00:00 2001 From: efilchenko Date: Wed, 17 Sep 2025 16:38:01 +0300 Subject: [PATCH 07/11] add proposal changes --- .../eclipse/esmf/aspectmodel/resolver/FileSystemStrategy.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/FileSystemStrategy.java b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/FileSystemStrategy.java index 650f81977..e73e2f0f7 100644 --- a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/FileSystemStrategy.java +++ b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/FileSystemStrategy.java @@ -95,6 +95,9 @@ public AspectModelFile apply( final AspectModelUrn aspectModelUrn, final Resolut final RawAspectModelFile loadedFile = tryFile.get(); if ( resolutionStrategySupport.containsDefinition( loadedFile, aspectModelUrn ) ) { return loadedFile; + } else { + checkedLocations.add( new ModelResolutionException.LoadingFailure( aspectModelUrn, namedResourceFile.getAbsolutePath(), + "File does not contain the element definition" ) ); } } else { checkedLocations.add( new ModelResolutionException.LoadingFailure( aspectModelUrn, namedResourceFile.getAbsolutePath(), From aaa1914d8f4454ed3d6b2c0f7e354672deeec021 Mon Sep 17 00:00:00 2001 From: efilchenko Date: Wed, 17 Sep 2025 17:15:47 +0300 Subject: [PATCH 08/11] checkstyle fix --- .../eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java b/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java index 315e2dd93..65a5285bf 100644 --- a/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java +++ b/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java @@ -59,9 +59,9 @@ void resolveByCanonicalPathShouldReturnEmptyFileWhenCanonicalPathDoesNotMatch() @Test void resolveByCanonicalPathShouldReturnEmptyFileWhenIoExceptionOccurs() throws Exception { - Path pathCausingIOException = Paths.get( "pathCausingIOException" ); + Path pathCausingIoException = Paths.get( "pathCausingIoException" ); - File result = invokeResolveByCanonicalPath( pathCausingIOException ); + File result = invokeResolveByCanonicalPath( pathCausingIoException ); assertThat( result ).isEqualTo( EMPTY_FILE ); } From 893129876a835eae15953a987f0012384c50d8f5 Mon Sep 17 00:00:00 2001 From: efilchenko Date: Thu, 18 Sep 2025 12:03:42 +0300 Subject: [PATCH 09/11] fix --- .../aspectmodel/loader/AspectModelLoader.java | 24 ++++++++++++------- .../aspectmodel/resolver/fs/ModelsRoot.java | 2 +- .../resolver/fs/ModelsRootTest.java | 9 ------- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/loader/AspectModelLoader.java b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/loader/AspectModelLoader.java index 827966d15..52d9e5391 100644 --- a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/loader/AspectModelLoader.java +++ b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/loader/AspectModelLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH + * Copyright (c) 2025 Robert Bosch Manufacturing Solutions GmbH * * See the AUTHORS file(s) distributed with this work for additional * information regarding authorship. @@ -17,6 +17,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.JarURLConnection; import java.net.URI; import java.nio.file.Path; import java.util.ArrayDeque; @@ -121,7 +122,7 @@ public AspectModelLoader( final ResolutionStrategy resolutionStrategy ) { */ public AspectModelLoader( final List resolutionStrategies ) { TurtleLoader.init(); - if ( resolutionStrategies.size() == 1 ) { + if ( 1 == resolutionStrategies.size() ) { resolutionStrategy = resolutionStrategies.get( 0 ); } else if ( resolutionStrategies.isEmpty() ) { resolutionStrategy = DEFAULT_STRATEGY.get(); @@ -416,7 +417,7 @@ public AspectModel loadNamespacePackage( final byte[] binaryContent, final URI l * {@code https://example.com/package.zip}, the files in the package will have a location URI such as * {@code jar:file:/some/path/package.zip!/com.example.namespace/1.0.0/AspectModel.ttl} or * {@code jar:https://example.com/package.zip!/com.example.namespace/1.0.0/AspectModel.ttl}, respectively, as described in - * the JavaDoc for {@link java.net.JarURLConnection}. + * the JavaDoc for {@link JarURLConnection}. * * @param location the source location * @param inputStream the input stream to load the ZIP content from @@ -473,7 +474,8 @@ private String replaceLegacyBammUrn( final String urn ) { private boolean containsType( final Model model, final String urn ) { if ( model.contains( model.createResource( urn ), RDF.type, (RDFNode) null ) ) { return true; - } else if ( urn.startsWith( AspectModelUrn.PROTOCOL_AND_NAMESPACE_PREFIX ) ) { + } + if ( urn.startsWith( AspectModelUrn.PROTOCOL_AND_NAMESPACE_PREFIX ) ) { // when deriving a URN from file (via "fileToUrn" method - mainly in samm-cli scenarios), // we assume new "samm" format, but could actually have been the old "bamm" return model.contains( model.createResource( toLegacyBammUrn( urn ) ), RDF.type, (RDFNode) null ); @@ -495,7 +497,7 @@ private Optional applyResolutionStrategy( final String urn ) { try { final AspectModelUrn aspectModelUrn = AspectModelUrn.fromUrn( replaceLegacyBammUrn( urn ) ); - if ( aspectModelUrn.getElementType() != ElementType.NONE ) { + if ( ElementType.NONE != aspectModelUrn.getElementType() ) { return Optional.empty(); } final AspectModelFile resolutionResult = resolutionStrategy.apply( aspectModelUrn, this ); @@ -586,14 +588,18 @@ private void resolve( final List inputFiles, final LoaderContex @Override public boolean containsDefinition( final AspectModelFile aspectModelFile, final AspectModelUrn urn ) { final Model model = aspectModelFile.sourceModel(); + boolean result = model.contains( model.createResource( urn.toString() ), RDF.type, (RDFNode) null ); + if ( result ) { + LOG.debug( "Checking if model contains {}: {}", urn, result ); + return result; + } if ( model.getNsPrefixMap().values().stream().anyMatch( prefixUri -> prefixUri.startsWith( "urn:bamm:" ) ) ) { - final boolean result = model.contains( + result = model.contains( model.createResource( urn.toString().replace( AspectModelUrn.PROTOCOL_AND_NAMESPACE_PREFIX, "urn:bamm:" ) ), RDF.type, (RDFNode) null ); LOG.debug( "Checking if model contains {}: {}", urn, result ); return result; } - final boolean result = model.contains( model.createResource( urn.toString() ), RDF.type, (RDFNode) null ); LOG.debug( "Checking if model contains {}: {}", urn, result ); return result; } @@ -674,7 +680,7 @@ public AspectModel loadAspectModelFiles( final Collection input .findFirst() .ifPresent( aspect -> mergedModel.setNsPrefix( "", aspect.urn().getUrnPrefix() ) ); for ( final AspectModelFile file : files ) { - if ( file.aspects().size() > 1 ) { + if ( 1 < file.aspects().size() ) { throw new AspectLoadingException( "Aspect Model file " + file.humanReadableLocation() + " contains " + file.aspects().size() + " aspects, but may only contain one." ); @@ -709,7 +715,7 @@ private void setNamespaces( final Collection files, final Colle MetaModelBaseAttributes namespaceDefinition = null; AspectModelFile fileContainingNamespaceDefinition = null; final List elementsForUrn = elementsGroupedByNamespaceUrn.get( namespaceUrn ); - if ( elementsForUrn != null ) { + if ( null != elementsForUrn ) { for ( final ModelElement element : elementsForUrn ) { final AspectModelFile elementFile = element.getSourceFile(); if ( elementFile.sourceModel().contains( null, RDF.type, SammNs.SAMM.Namespace() ) ) { diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java index 07fcc3fc9..e9ad67734 100644 --- a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java +++ b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java @@ -70,7 +70,7 @@ public File determineAspectModelFile( final AspectModelUrn urn ) { private static File resolveByCanonicalPath( final Path path ) { File file = path.toFile(); try { - if ( Objects.equals( path.normalize().toString(), file.getCanonicalPath() ) ) { + if ( file.exists() && Objects.equals( path.normalize().toString(), file.getCanonicalPath() ) ) { return file; } } catch ( IOException exception ) { diff --git a/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java b/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java index 65a5285bf..b6c5187bf 100644 --- a/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java +++ b/core/esmf-aspect-meta-model-java/src/test/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRootTest.java @@ -57,15 +57,6 @@ void resolveByCanonicalPathShouldReturnEmptyFileWhenCanonicalPathDoesNotMatch() assertThat( result ).isEqualTo( EMPTY_FILE ); } - @Test - void resolveByCanonicalPathShouldReturnEmptyFileWhenIoExceptionOccurs() throws Exception { - Path pathCausingIoException = Paths.get( "pathCausingIoException" ); - - File result = invokeResolveByCanonicalPath( pathCausingIoException ); - - assertThat( result ).isEqualTo( EMPTY_FILE ); - } - private static File invokeResolveByCanonicalPath( Path path ) throws Exception { Method method = ModelsRoot.class.getDeclaredMethod( "resolveByCanonicalPath", Path.class ); method.setAccessible( true ); From 0febb31ce92f8c6b82e4503617e7313b058e7f81 Mon Sep 17 00:00:00 2001 From: efilchenko Date: Thu, 18 Sep 2025 22:38:37 +0300 Subject: [PATCH 10/11] fix --- .../resolver/FileSystemStrategy.java | 2 +- .../aspectmodel/resolver/fs/ModelsRoot.java | 16 +++++++++--- .../esmf/aas/to/AasToAspectCommand.java | 25 ++++++++++++++----- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/FileSystemStrategy.java b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/FileSystemStrategy.java index e73e2f0f7..9453424ca 100644 --- a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/FileSystemStrategy.java +++ b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/FileSystemStrategy.java @@ -84,7 +84,7 @@ public FileSystemStrategy( final ModelsRoot modelsRoot ) { public AspectModelFile apply( final AspectModelUrn aspectModelUrn, final ResolutionStrategySupport resolutionStrategySupport ) { final List checkedLocations = new ArrayList<>(); - final File namedResourceFile = modelsRoot.determineAspectModelFile( aspectModelUrn ); + final File namedResourceFile = modelsRoot.resolveAspectModelFile( aspectModelUrn ); if ( namedResourceFile.exists() ) { final Try tryFile = Try.of( () -> AspectModelFileLoader.load( namedResourceFile ) ); if ( tryFile.isFailure() ) { diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java index e9ad67734..b117758a3 100644 --- a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java +++ b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/fs/ModelsRoot.java @@ -51,22 +51,30 @@ public Stream paths() { public abstract Path directoryForNamespace( final AspectModelUrn urn ); + public File determineAspectModelFile( final AspectModelUrn urn ) { + return constructAspectModelFilePath( urn ).toFile(); + } + /** - * Determines the aspect model file for the given {@link AspectModelUrn}. + * Resolve the aspect model file for the given {@link AspectModelUrn}. * *

Constructs the file path by resolving the namespace directory. * Validates the file using its canonical path. * - *

Returns an empty file if the resolution fails. + *

Returns an empty file if the resolution fails.s * * @param urn the {@link AspectModelUrn} representing the aspect model. * @return the resolved {@link File}, or an empty file if resolution fails. */ - public File determineAspectModelFile( final AspectModelUrn urn ) { - Path path = directoryForNamespace( urn ).resolve( urn.getName() + ".ttl" ); + public File resolveAspectModelFile( final AspectModelUrn urn ) { + Path path = constructAspectModelFilePath( urn ); return resolveByCanonicalPath( path ); } + private Path constructAspectModelFilePath( final AspectModelUrn urn ) { + return directoryForNamespace( urn ).resolve( urn.getName() + ".ttl" ); + } + private static File resolveByCanonicalPath( final Path path ) { File file = path.toFile(); try { diff --git a/tools/samm-cli/src/main/java/org/eclipse/esmf/aas/to/AasToAspectCommand.java b/tools/samm-cli/src/main/java/org/eclipse/esmf/aas/to/AasToAspectCommand.java index ed61989bf..644caaef5 100644 --- a/tools/samm-cli/src/main/java/org/eclipse/esmf/aas/to/AasToAspectCommand.java +++ b/tools/samm-cli/src/main/java/org/eclipse/esmf/aas/to/AasToAspectCommand.java @@ -1,3 +1,16 @@ +/* + * Copyright (c) 2025 Robert Bosch Manufacturing Solutions GmbH + * + * See the AUTHORS file(s) distributed with this work for additional + * information regarding authorship. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + package org.eclipse.esmf.aas.to; import java.io.BufferedWriter; @@ -43,13 +56,13 @@ public class AasToAspectCommand extends AbstractCommand { names = { "--output-directory", "-d" }, description = "Output directory to write files to" ) - private String outputPath = "."; + private final String outputPath = "."; @CommandLine.Option( names = { "--submodel-template", "-s" }, description = "Select the submodel template(s) to include, as returned by the aas list command" ) - private List selectedOptions = new ArrayList<>(); + private final List selectedOptions = new ArrayList<>(); @CommandLine.Mixin private LoggingMixin loggingMixin; @@ -58,7 +71,7 @@ public class AasToAspectCommand extends AbstractCommand { public void run() { final String path = parentCommand.parentCommand.getInput(); final String extension = FilenameUtils.getExtension( path ); - if ( !extension.equals( "xml" ) && !extension.equals( "json" ) && !extension.equals( "aasx" ) ) { + if ( !"xml".equals( extension ) && !"json".equals( extension ) && !"aasx".equals( extension ) ) { throw new CommandException( "Input file name must be an .xml, .aasx or .json file" ); } generateAspects( AasToAspectModelGenerator.fromFile( new File( path ) ) ); @@ -76,12 +89,12 @@ private void generateAspects( final AasToAspectModelGenerator generator ) { for ( final Aspect aspect : filteredAspects ) { final String aspectString = AspectSerializer.INSTANCE.aspectToString( aspect ); - final File targetFile = modelsRoot.determineAspectModelFile( aspect.urn() ); - LOG.info( "Writing {}", targetFile.getAbsolutePath() ); - final File directory = targetFile.getParentFile(); + final File directory = modelsRoot.directoryForNamespace( aspect.urn() ).toFile(); if ( !directory.exists() && !directory.mkdirs() ) { throw new CommandException( "Could not create directory: " + directory.getAbsolutePath() ); } + final File targetFile = modelsRoot.determineAspectModelFile( aspect.urn() ); + LOG.info( "Writing {}", targetFile.getAbsolutePath() ); try ( final Writer writer = new BufferedWriter( new FileWriter( targetFile ) ) ) { writer.write( aspectString ); } catch ( final IOException exception ) { From 128db8b64affe421dd42c270384a17b14ad2d5f8 Mon Sep 17 00:00:00 2001 From: "Textor Andreas (BCI/ESW17)" Date: Fri, 19 Sep 2025 07:47:46 +0200 Subject: [PATCH 11/11] Make argument fields in AasToAspectCommand non-final --- .../main/java/org/eclipse/esmf/aas/to/AasToAspectCommand.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/samm-cli/src/main/java/org/eclipse/esmf/aas/to/AasToAspectCommand.java b/tools/samm-cli/src/main/java/org/eclipse/esmf/aas/to/AasToAspectCommand.java index 644caaef5..173b44dbc 100644 --- a/tools/samm-cli/src/main/java/org/eclipse/esmf/aas/to/AasToAspectCommand.java +++ b/tools/samm-cli/src/main/java/org/eclipse/esmf/aas/to/AasToAspectCommand.java @@ -56,13 +56,13 @@ public class AasToAspectCommand extends AbstractCommand { names = { "--output-directory", "-d" }, description = "Output directory to write files to" ) - private final String outputPath = "."; + private String outputPath = "."; @CommandLine.Option( names = { "--submodel-template", "-s" }, description = "Select the submodel template(s) to include, as returned by the aas list command" ) - private final List selectedOptions = new ArrayList<>(); + private List selectedOptions = new ArrayList<>(); @CommandLine.Mixin private LoggingMixin loggingMixin;