From 9dfbe60337711ae97ac6fd6a288f596192bab3f3 Mon Sep 17 00:00:00 2001 From: "Textor Andreas (BCI/ESW17)" Date: Thu, 28 Nov 2024 15:46:31 +0100 Subject: [PATCH 1/7] Address use of deprecated APIs --- .../AspectModelJsonPayloadGeneratorTest.java | 9 ++++-- .../esmf/nativefeatures/LogbackFeature.java | 1 + .../esmf/substitution/DummyLogger.java | 1 + .../examples/GenerateDiagrams.java | 28 ++++++++----------- .../pages/java-aspect-tooling.adoc | 9 +++--- .../GenerateAspectImplementationStub.java | 1 + .../esmf/MainClassProcessLauncher.java | 1 + 7 files changed, 27 insertions(+), 23 deletions(-) diff --git a/core/esmf-aspect-model-document-generators/src/test/java/org/eclipse/esmf/aspectmodel/generator/json/AspectModelJsonPayloadGeneratorTest.java b/core/esmf-aspect-model-document-generators/src/test/java/org/eclipse/esmf/aspectmodel/generator/json/AspectModelJsonPayloadGeneratorTest.java index 9178cf895..4c6d35a32 100644 --- a/core/esmf-aspect-model-document-generators/src/test/java/org/eclipse/esmf/aspectmodel/generator/json/AspectModelJsonPayloadGeneratorTest.java +++ b/core/esmf-aspect-model-document-generators/src/test/java/org/eclipse/esmf/aspectmodel/generator/json/AspectModelJsonPayloadGeneratorTest.java @@ -548,11 +548,16 @@ void testGeneratedNumbersAreWithinRange( final RDFDatatype numericModelType, fin final Aspect dynamicAspect = createAspectWithDynamicNumericProperty( numericType, boundKind.orElse( null ), randomRange ); + final AspectModelJsonPayloadGenerator randomGenerator = new AspectModelJsonPayloadGenerator( dynamicAspect ); final AspectModelJsonPayloadGenerator minGenerator = new AspectModelJsonPayloadGenerator( dynamicAspect, - new MinValueRandomStrategy() ); + JsonPayloadGenerationConfigBuilder.builder() + .randomStrategy( new MinValueRandomStrategy() ) + .build() ); final AspectModelJsonPayloadGenerator maxGenerator = new AspectModelJsonPayloadGenerator( dynamicAspect, - new MaxValueRandomStrategy() ); + JsonPayloadGenerationConfigBuilder.builder() + .randomStrategy( new MaxValueRandomStrategy() ) + .build() ); try { final String generatedJson = randomGenerator.generateJson(); final String minValue = minGenerator.generateJson(); diff --git a/core/esmf-native-support/src/main/java/org/eclipse/esmf/nativefeatures/LogbackFeature.java b/core/esmf-native-support/src/main/java/org/eclipse/esmf/nativefeatures/LogbackFeature.java index b10936713..d887cfaa9 100644 --- a/core/esmf-native-support/src/main/java/org/eclipse/esmf/nativefeatures/LogbackFeature.java +++ b/core/esmf-native-support/src/main/java/org/eclipse/esmf/nativefeatures/LogbackFeature.java @@ -48,6 +48,7 @@ public void beforeAnalysis( final Feature.BeforeAnalysisAccess access ) { Native.forClass( ch.qos.logback.core.util.Loader.class ) .initializeAtBuildTime() .registerEverythingForReflection(); + //noinspection deprecation Native.forClass( ch.qos.logback.core.util.StatusPrinter.class ) .initializeAtBuildTime() .registerEverythingForReflection(); diff --git a/core/esmf-native-support/src/main/java/org/eclipse/esmf/substitution/DummyLogger.java b/core/esmf-native-support/src/main/java/org/eclipse/esmf/substitution/DummyLogger.java index 40e993d0a..30c09fbb1 100644 --- a/core/esmf-native-support/src/main/java/org/eclipse/esmf/substitution/DummyLogger.java +++ b/core/esmf-native-support/src/main/java/org/eclipse/esmf/substitution/DummyLogger.java @@ -23,6 +23,7 @@ import org.apache.logging.log4j.util.MessageSupplier; import org.apache.logging.log4j.util.Supplier; +@SuppressWarnings( "deprecation" ) // Used by original Logger interface public class DummyLogger implements Logger { @Override public void catching( final Level level, final Throwable throwable ) { diff --git a/documentation/developer-guide/modules/tooling-guide/examples/GenerateDiagrams.java b/documentation/developer-guide/modules/tooling-guide/examples/GenerateDiagrams.java index 5edc18b3f..14d12f01e 100644 --- a/documentation/developer-guide/modules/tooling-guide/examples/GenerateDiagrams.java +++ b/documentation/developer-guide/modules/tooling-guide/examples/GenerateDiagrams.java @@ -13,24 +13,23 @@ package examples; -// tag::imports[] -import java.io.OutputStream; +import java.io.File; +import java.io.IOException; import java.util.Locale; -import java.util.Set; +// tag::imports[] import org.eclipse.esmf.aspectmodel.generator.diagram.AspectModelDiagramGenerator; -import org.eclipse.esmf.aspectmodel.generator.diagram.AspectModelDiagramGenerator.Format; +import org.eclipse.esmf.aspectmodel.generator.diagram.DiagramGenerationConfig; +import org.eclipse.esmf.aspectmodel.generator.diagram.DiagramGenerationConfigBuilder; import org.eclipse.esmf.aspectmodel.loader.AspectModelLoader; import org.eclipse.esmf.metamodel.AspectModel; // end::imports[] -import java.io.File; -import java.io.IOException; import org.junit.jupiter.api.Test; public class GenerateDiagrams extends AbstractGenerator { @Test - public void generateDiagram() throws IOException { + public void generateDiagram() { // tag::generate[] // AspectModel as returned by the AspectModelLoader final AspectModel aspectModel = // ... @@ -39,15 +38,12 @@ public void generateDiagram() throws IOException { new File( "aspect-models/org.eclipse.esmf.examples.movement/1.0.0/Movement.ttl" ) ); // tag::generate[] - final AspectModelDiagramGenerator generator = new AspectModelDiagramGenerator( aspectModel.aspect() ); // <1> - - // Variant 1: Generate a diagram in SVG format using @en descriptions and preferredNames from the model - final OutputStream output = outputStreamForName( "diagram.svg" ); - generator.generateDiagram( Format.SVG, Locale.ENGLISH, output ); // <2> - output.close(); - - // Variant 2: Generate diagrams in multiple formats, for all languages that are present in the model. - generator.generateDiagrams( Set.of( Format.PNG, Format.SVG ), this::outputStreamForName ); // <3> + final DiagramGenerationConfig config = DiagramGenerationConfigBuilder.builder() + .format( DiagramGenerationConfig.Format.SVG ) + .language( Locale.ENGLISH ) + .build(); + new AspectModelDiagramGenerator( aspectModel.aspect(), config ) // <1> + .generate( this::outputStreamForName ); // <2> // end::generate[] } } diff --git a/documentation/developer-guide/modules/tooling-guide/pages/java-aspect-tooling.adoc b/documentation/developer-guide/modules/tooling-guide/pages/java-aspect-tooling.adoc index 29e523410..6a06625ad 100644 --- a/documentation/developer-guide/modules/tooling-guide/pages/java-aspect-tooling.adoc +++ b/documentation/developer-guide/modules/tooling-guide/pages/java-aspect-tooling.adoc @@ -359,11 +359,10 @@ include::example$GenerateDiagrams.java[tags=imports] include::example$GenerateDiagrams.java[tags=generate] ---- -<1> The diagram generator is initialized with the loaded model. -<2> The simple call for one output format and one language (i.e., descriptions and preferredNames of one locale) takes -one output stream to write the image to. -<3> It is also possible to generate multiple diagrams, one for each combination of output format and language. For that, -the set of target formats is given as well as a mapping function. +<1> The diagram generator is initialized with the loaded model and the +generation configuration. +<2> The generation takes the output stream for the output file name as input and +writes the generated diagram to the output stream. [[generating-html-documentation]] === Generating HTML Documentation diff --git a/tools/esmf-aspect-model-maven-plugin/src/main/java/org/eclipse/esmf/aspectmodel/GenerateAspectImplementationStub.java b/tools/esmf-aspect-model-maven-plugin/src/main/java/org/eclipse/esmf/aspectmodel/GenerateAspectImplementationStub.java index 6e555d81e..fc8bc7eb1 100644 --- a/tools/esmf-aspect-model-maven-plugin/src/main/java/org/eclipse/esmf/aspectmodel/GenerateAspectImplementationStub.java +++ b/tools/esmf-aspect-model-maven-plugin/src/main/java/org/eclipse/esmf/aspectmodel/GenerateAspectImplementationStub.java @@ -82,6 +82,7 @@ public class GenerateAspectImplementationStub extends CodeGenerationMojo { private MavenProject mavenProject; @Component + @SuppressWarnings( "deprecation" ) // @Component annotation is required for successful injection of dependency with no alternative private BuildPluginManager pluginManager; @Parameter( required = true ) diff --git a/tools/samm-cli/src/test/java/org/eclipse/esmf/MainClassProcessLauncher.java b/tools/samm-cli/src/test/java/org/eclipse/esmf/MainClassProcessLauncher.java index 01cb458b0..ab6478dcf 100644 --- a/tools/samm-cli/src/test/java/org/eclipse/esmf/MainClassProcessLauncher.java +++ b/tools/samm-cli/src/test/java/org/eclipse/esmf/MainClassProcessLauncher.java @@ -37,6 +37,7 @@ * replacement mechanism for capturing System.exit() has been defined yet. * Therefore, there is currently no other way than using the deprecated class. */ +@SuppressWarnings( "removal" ) public class MainClassProcessLauncher extends ProcessLauncher { private static final Logger LOG = LoggerFactory.getLogger( MainClassProcessLauncher.class ); private final Class mainClass; From b2f8ac1c2573e22b8890f95872c65dd86330a9e0 Mon Sep 17 00:00:00 2001 From: "Textor Andreas (BCI/ESW17)" Date: Fri, 29 Nov 2024 14:21:49 +0100 Subject: [PATCH 2/7] Use generator infrastructure in AasToAspectModelGenerator --- .../aas/AasToAspectModelGenerator.java | 45 ++++++++++++----- .../aas/AspectGenerationConfig.java | 23 +++++++++ core/esmf-aspect-model-generator/pom.xml | 4 ++ .../aspectmodel/generator/AspectArtifact.java | 48 +++++++++++++++++++ .../esmf/aspectmodel/generator/Generator.java | 5 ++ .../esmf/aas/AasListSubmodelsCommand.java | 11 ++++- 6 files changed, 124 insertions(+), 12 deletions(-) create mode 100644 core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AspectGenerationConfig.java create mode 100644 core/esmf-aspect-model-generator/src/main/java/org/eclipse/esmf/aspectmodel/generator/AspectArtifact.java diff --git a/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGenerator.java b/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGenerator.java index 826135b00..85bae73ac 100644 --- a/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGenerator.java +++ b/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGenerator.java @@ -30,6 +30,8 @@ import java.util.stream.Stream; import org.eclipse.esmf.aspectmodel.VersionNumber; +import org.eclipse.esmf.aspectmodel.generator.AspectArtifact; +import org.eclipse.esmf.aspectmodel.generator.Generator; import org.eclipse.esmf.aspectmodel.loader.MetaModelBaseAttributes; import org.eclipse.esmf.aspectmodel.loader.ValueInstantiator; import org.eclipse.esmf.aspectmodel.urn.AspectModelUrn; @@ -98,17 +100,19 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class AasToAspectModelGenerator { +public class AasToAspectModelGenerator extends Generator { + public static final AspectGenerationConfig DEFAULT_CONFIG = AspectGenerationConfigBuilder.builder().build(); private static final Logger LOG = LoggerFactory.getLogger( AasToAspectModelGenerator.class ); private final Environment aasEnvironment; - private final Map properties = new HashMap<>(); + private final Map properties = new HashMap<>(); private final ValueInstantiator valueInstantiator = new ValueInstantiator(); private AspectModelUrn aspectUrn; private record ElementName( String name, boolean isSynthetic ) {} private AasToAspectModelGenerator( final Environment aasEnvironment ) { - this.aasEnvironment = aasEnvironment; + super( aasEnvironment, DEFAULT_CONFIG ); + this.aasEnvironment = focus; } public static AasToAspectModelGenerator fromAasXml( final InputStream inputStream ) { @@ -155,14 +159,33 @@ public static AasToAspectModelGenerator fromFile( final java.io.File file ) { } } - public List generateAspects() { + @Override + public Stream generate() { return aasEnvironment.getSubmodels() .stream() .filter( submodel -> submodel.getKind().equals( ModellingKind.TEMPLATE ) ) .map( this::submodelToAspect ) - .toList(); + .map( aspect -> new AspectArtifact( aspect.urn(), aspect ) ); + } + + /** + * Generates the list of Aspects for the input AAS environment + * + * @return the list of Aspects + * @deprecated Use {@link #generate()} instead + */ + @Deprecated( forRemoval = true ) + public List generateAspects() { + return generate().map( AspectArtifact::getContent ).toList(); } + /** + * Lists the names of submodel templates contained in the input AAS environment + * + * @return the idShorts of the submodel templates + * @deprecated Will be removed without replacement; this is out-of-the-box functionality of AAS4J. + */ + @Deprecated( forRemoval = true ) public List getSubmodelNames() { return aasEnvironment.getSubmodels() .stream() @@ -175,12 +198,12 @@ private String iriToReversedHostNameNotation( final IRI iri ) { final URI uri; try { uri = URI.create( iri.toString().contains( "://" ) ? iri.toString() : "https://" + iri ); - } catch ( IllegalArgumentException e ) { - throw new IllegalArgumentException( "Incorrect IRI: " + iri, e ); + } catch ( final IllegalArgumentException exception ) { + throw new AspectModelGenerationException( "Incorrect IRI: " + iri, exception ); } if ( uri.getHost() == null ) { - throw new IllegalArgumentException( "URI doesn't contain host: " + uri ); + throw new AspectModelGenerationException( "URI doesn't contain host: " + uri ); } final String[] hostParts = uri.getHost().split( "\\." ); @@ -193,7 +216,7 @@ private String iriToReversedHostNameNotation( final IRI iri ) { .filter( StringUtils::isNotBlank ) .collect( Collectors.joining( "." ) ); - return reversedHost + (path.isEmpty() ? "" : "." + path); + return reversedHost + ( path.isEmpty() ? "" : "." + path ); } private Optional iri( final String lexicalRepresentation ) { @@ -327,8 +350,8 @@ private List createOperations( final Submodel submodel ) { private List createEvents( final Submodel submodel ) { return submodel.getSubmodelElements().stream() - .filter( org.eclipse.digitaltwin.aas4j.v3.model.EventElement.class::isInstance ) - .map( org.eclipse.digitaltwin.aas4j.v3.model.EventElement.class::cast ) + .filter( EventElement.class::isInstance ) + .map( EventElement.class::cast ) .map( this::createEvent ) .toList(); } diff --git a/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AspectGenerationConfig.java b/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AspectGenerationConfig.java new file mode 100644 index 000000000..d120c1d41 --- /dev/null +++ b/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AspectGenerationConfig.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 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.aas; + +import org.eclipse.esmf.aspectmodel.generator.GenerationConfig; + +import io.soabase.recordbuilder.core.RecordBuilder; + +@RecordBuilder +public record AspectGenerationConfig( +) implements GenerationConfig { +} diff --git a/core/esmf-aspect-model-generator/pom.xml b/core/esmf-aspect-model-generator/pom.xml index fa3f07c5f..9bb3e1da1 100644 --- a/core/esmf-aspect-model-generator/pom.xml +++ b/core/esmf-aspect-model-generator/pom.xml @@ -31,6 +31,10 @@ org.eclipse.esmf esmf-aspect-meta-model-java + + org.eclipse.esmf + esmf-aspect-model-serializer + org.apache.velocity velocity-engine-core diff --git a/core/esmf-aspect-model-generator/src/main/java/org/eclipse/esmf/aspectmodel/generator/AspectArtifact.java b/core/esmf-aspect-model-generator/src/main/java/org/eclipse/esmf/aspectmodel/generator/AspectArtifact.java new file mode 100644 index 000000000..642ada00d --- /dev/null +++ b/core/esmf-aspect-model-generator/src/main/java/org/eclipse/esmf/aspectmodel/generator/AspectArtifact.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024 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.generator; + +import java.nio.charset.StandardCharsets; + +import org.eclipse.esmf.aspectmodel.serializer.AspectSerializer; +import org.eclipse.esmf.aspectmodel.urn.AspectModelUrn; +import org.eclipse.esmf.metamodel.Aspect; + +/** + * Result of a generation process that generates an Aspect + */ +public class AspectArtifact implements Artifact { + private final AspectModelUrn urn; + private final Aspect aspect; + + public AspectArtifact( final AspectModelUrn urn, final Aspect aspect ) { + this.urn = urn; + this.aspect = aspect; + } + + @Override + public AspectModelUrn getId() { + return urn; + } + + @Override + public Aspect getContent() { + return aspect; + } + + @Override + public byte[] serialize() { + return AspectSerializer.INSTANCE.aspectToString( aspect ).getBytes( StandardCharsets.UTF_8 ); + } +} diff --git a/core/esmf-aspect-model-generator/src/main/java/org/eclipse/esmf/aspectmodel/generator/Generator.java b/core/esmf-aspect-model-generator/src/main/java/org/eclipse/esmf/aspectmodel/generator/Generator.java index db65fbf28..2f9855432 100644 --- a/core/esmf-aspect-model-generator/src/main/java/org/eclipse/esmf/aspectmodel/generator/Generator.java +++ b/core/esmf-aspect-model-generator/src/main/java/org/eclipse/esmf/aspectmodel/generator/Generator.java @@ -37,6 +37,11 @@ */ public abstract class Generator> { private static final Logger LOG = LoggerFactory.getLogger( Generator.class ); + + /** + * The "focus" element, i.e., the main "input" for the generation process + */ + @Getter protected final F focus; @Getter diff --git a/tools/samm-cli/src/main/java/org/eclipse/esmf/aas/AasListSubmodelsCommand.java b/tools/samm-cli/src/main/java/org/eclipse/esmf/aas/AasListSubmodelsCommand.java index 66a8c5af5..a3b3936d9 100644 --- a/tools/samm-cli/src/main/java/org/eclipse/esmf/aas/AasListSubmodelsCommand.java +++ b/tools/samm-cli/src/main/java/org/eclipse/esmf/aas/AasListSubmodelsCommand.java @@ -3,6 +3,7 @@ import java.io.File; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; import org.eclipse.esmf.AbstractCommand; import org.eclipse.esmf.LoggingMixin; @@ -11,6 +12,8 @@ import org.eclipse.esmf.exception.CommandException; import org.apache.commons.io.FilenameUtils; +import org.eclipse.digitaltwin.aas4j.v3.model.ModellingKind; +import org.eclipse.digitaltwin.aas4j.v3.model.Referable; import picocli.CommandLine; @CommandLine.Command( name = AasListSubmodelsCommand.COMMAND_NAME, description = "Get list of submodel templates of AAS input", @@ -37,7 +40,13 @@ public void run() { throw new CommandException( "Input file name must be one of " + AasFileFormat.allValues() ); } - final List submodelNames = AasToAspectModelGenerator.fromFile( new File( path ) ).getSubmodelNames(); + final List submodelNames = AasToAspectModelGenerator.fromFile( new File( path ) ) + .getFocus() + .getSubmodels() + .stream() + .filter( submodel -> submodel.getKind().equals( ModellingKind.TEMPLATE ) ) + .map( Referable::getIdShort ) + .toList(); for ( final String submodelName : submodelNames ) { final int index = submodelNames.indexOf( submodelName ); From f60f8c635d09c7dbfd13769c19e89aefc7ee9dc8 Mon Sep 17 00:00:00 2001 From: "Textor Andreas (BCI/ESW17)" Date: Mon, 29 Jul 2024 10:30:40 +0200 Subject: [PATCH 3/7] Add systematic tests for IDTA-published AASX to SAMM translation --- .../loader/AttributeValueRetriever.java | 2 +- core/esmf-aspect-model-aas-generator/pom.xml | 5 ++ .../aas/AasToAspectModelGenerator.java | 31 +++++-- .../aas/AasToAspectModelGeneratorTest.java | 84 +++++++++++++++---- 4 files changed, 97 insertions(+), 25 deletions(-) diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/loader/AttributeValueRetriever.java b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/loader/AttributeValueRetriever.java index 2c6bc764d..9391f61c1 100644 --- a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/loader/AttributeValueRetriever.java +++ b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/loader/AttributeValueRetriever.java @@ -71,7 +71,7 @@ private boolean isRdfList( final Resource resource ) { /** * Returns the values of n-ary attributes on a model element (or its super elements), or if a given attribute is an rdf:List, the list - * elements. The list will be ordered by precedence, e.g., if a Property is present on both the current element and it's superelement, + * elements. The list will be ordered by precedence, e.g., if a Property is present on both the current element and its superelement, * the assertion on the current element will be on a lower list index. Duplicate attribute assertions are removed and only the assertion * with the highest precedence will be returned (bottom-most in the inheritance tree), this includes multiple assertions for the same * attribute with rdf:langString values with the same language tag. For example: diff --git a/core/esmf-aspect-model-aas-generator/pom.xml b/core/esmf-aspect-model-aas-generator/pom.xml index 9d8a6acb3..7d3cd97c4 100644 --- a/core/esmf-aspect-model-aas-generator/pom.xml +++ b/core/esmf-aspect-model-aas-generator/pom.xml @@ -67,6 +67,11 @@ assertj-core test + + org.eclipse.esmf + esmf-aspect-model-validator + test + diff --git a/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGenerator.java b/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGenerator.java index 85bae73ac..ee3a7b36d 100644 --- a/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGenerator.java +++ b/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGenerator.java @@ -335,8 +335,8 @@ private Aspect submodelToAspect( final Submodel submodel ) { private List createProperties( final Submodel submodel ) { return submodel.getSubmodelElements().stream() .filter( submodelElement -> - !submodelElement.getClass().isAssignableFrom( org.eclipse.digitaltwin.aas4j.v3.model.Operation.class ) - && !submodelElement.getClass().isAssignableFrom( EventElement.class ) ) + !org.eclipse.digitaltwin.aas4j.v3.model.Operation.class.isAssignableFrom( submodelElement.getClass() ) + && !EventElement.class.isAssignableFrom( submodelElement.getClass() ) ) .map( this::createProperty ).toList(); } @@ -500,15 +500,30 @@ private static Set langStringSet( final List validIrdiOrUri( final String input ) { - return Irdi.from( input ).map( irdi -> "urn:irdi:" + irdi ) - .or( () -> { - if ( input.startsWith( "http:" ) || input.startsWith( "https:" ) ) { - return Optional.of( input ); - } else if ( input.startsWith( "www." ) ) { - return Optional.of( "https://" + input ); + final Optional irdi = Irdi.from( input ).map( i -> "urn:irdi:" + i ); + final Optional uri = Optional.of( input ) + .map( String::trim ) + .flatMap( inputUri -> { + if ( inputUri.startsWith( "http:" ) || inputUri.startsWith( "https:" ) ) { + return Optional.of( inputUri ); + } else if ( inputUri.startsWith( "www." ) ) { + return Optional.of( "https://" + inputUri ); } return Optional.empty(); + } ) + .flatMap( inputUri -> { + try { + return Optional.of( URI.create( inputUri ) ); + } catch ( final IllegalArgumentException exception ) { + return Optional.empty(); + } } ); + + final Optional result = irdi.or( () -> uri.map( URI::toString ) ); + if ( result.isEmpty() ) { + LOG.warn( "Neither valid IRDI or valid URI: " + input ); + } + return result; } private Characteristic createCharacteristicFromRelationShipElement( final RelationshipElement relationshipElement, diff --git a/core/esmf-aspect-model-aas-generator/src/test/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGeneratorTest.java b/core/esmf-aspect-model-aas-generator/src/test/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGeneratorTest.java index 3204bfd34..25d795efe 100644 --- a/core/esmf-aspect-model-aas-generator/src/test/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGeneratorTest.java +++ b/core/esmf-aspect-model-aas-generator/src/test/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGeneratorTest.java @@ -18,13 +18,27 @@ import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.URISyntaxException; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.List; import java.util.function.Consumer; - +import java.util.stream.Stream; + +import org.eclipse.esmf.aspectmodel.generator.AspectArtifact; +import org.eclipse.esmf.aspectmodel.loader.AspectModelLoader; +import org.eclipse.esmf.aspectmodel.serializer.AspectSerializer; +import org.eclipse.esmf.aspectmodel.shacl.violation.Violation; +import org.eclipse.esmf.aspectmodel.validation.services.AspectModelValidator; +import org.eclipse.esmf.aspectmodel.validation.services.ViolationFormatter; import org.eclipse.esmf.metamodel.Aspect; +import org.eclipse.esmf.metamodel.AspectModel; import org.eclipse.esmf.test.TestAspect; import org.eclipse.esmf.test.TestResources; @@ -32,17 +46,55 @@ import org.eclipse.digitaltwin.aas4j.v3.dataformat.json.JsonDeserializer; import org.eclipse.digitaltwin.aas4j.v3.dataformat.xml.XmlDeserializer; import org.eclipse.digitaltwin.aas4j.v3.model.Environment; +import org.junit.jupiter.api.Named; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.MethodSource; class AasToAspectModelGeneratorTest { - @Test - void testTranslateDigitalNameplate() { - final InputStream aasx = AasToAspectModelGeneratorTest.class.getClassLoader() - .getResourceAsStream( "idta/Sample_ZVEI_Digital_Nameplate_V10.aasx" ); - final AasToAspectModelGenerator aspectModelGenerator = AasToAspectModelGenerator.fromAasx( aasx ); - assertThatCode( aspectModelGenerator::generateAspects ).doesNotThrowAnyException(); + @ParameterizedTest + @MethodSource( "idtaSubmodelFiles" ) + void testIdtaAasxFilesCanBeTranslated( final File aasxFile ) { + try ( final InputStream input = new FileInputStream( aasxFile ) ) { + final AasToAspectModelGenerator aspectModelGenerator = AasToAspectModelGenerator.fromAasx( input ); + final List aspects = aspectModelGenerator.generate().map( AspectArtifact::getContent ).toList(); + final String result = AspectSerializer.INSTANCE.aspectToString( aspects.iterator().next() ); + final AspectModel aspectModel = new AspectModelLoader().load( new ByteArrayInputStream( result.getBytes() ) ); + final List violations = new AspectModelValidator().validateModel( aspectModel ); + if ( !violations.isEmpty() ) { + final String report = new ViolationFormatter().apply( violations ); + System.out.println( report ); + System.out.println( "====" ); + System.out.println( result ); + fail(); + } + } catch ( final IOException exception ) { + fail( exception ); + } catch ( final AspectModelGenerationException aspectModelGenerationException ) { + if ( aspectModelGenerationException.getCause() instanceof final DeserializationException cause ) { + System.err.println( "Could not load AASX file: " + aasxFile.getName() + ". Consider reporting to IDTA or AAS4J project." ); + } else { + fail( aspectModelGenerationException ); + } + } + } + + protected static Stream idtaSubmodelFiles() throws URISyntaxException, IOException { + final URL resource = AasToAspectModelGeneratorTest.class.getResource( "/submodel-templates" ); + try ( final Stream stream = Files.walk( Paths.get( resource.toURI() ) ) ) { + final List list = stream.filter( Files::isRegularFile ) + .filter( file -> file.getFileName().toString().endsWith( ".aasx" ) ) + .map( Path::toFile ) + .map( file -> Arguments.of( Named.of( file.getName(), file ) ) ) + .toList(); + if ( list.isEmpty() ) { + fail( "IDTA AASX files not found. Please make sure they are available; in the project root run: git submodule update --init " + + "--recursive" ); + } + return list.stream(); + } } @Test @@ -50,9 +102,9 @@ void testSeeReferences() { final InputStream inputStream = AasToAspectModelGeneratorTest.class.getClassLoader().getResourceAsStream( "idta/IDTA 02022-1-0_Template_Wireless Communication.aasx" ); final AasToAspectModelGenerator aspectModelGenerator = AasToAspectModelGenerator.fromAasx( inputStream ); - final List aspects = aspectModelGenerator.generateAspects(); + final List aspects = aspectModelGenerator.generate().map( AspectArtifact::getContent ).toList(); - assertThatCode( aspectModelGenerator::generateAspects ).doesNotThrowAnyException(); + assertThatCode( aspectModelGenerator::generate ).doesNotThrowAnyException(); aspects.stream() .flatMap( aspect -> aspect.getProperties().stream() ) @@ -68,7 +120,7 @@ void testRoundtripConversion( final TestAspect testAspect ) throws Deserializati final Aspect aspect = TestResources.load( testAspect ).aspect(); final Consumer assertForValidator = aspectModelGenerator -> assertThatCode( () -> { - final List aspects = aspectModelGenerator.generateAspects(); + final List aspects = aspectModelGenerator.generate().map( AspectArtifact::getContent ).toList(); assertThat( aspects ).singleElement().satisfies( generatedAspect -> assertThat( generatedAspect.urn() ).isEqualTo( aspect.urn() ) ); } ).doesNotThrowAnyException(); @@ -90,7 +142,7 @@ void testGetAspectModelUrnFromSubmodelIdentifier() { // Submodel has an Aspect Model URN as identifier final Environment aasEnvironment = loadEnvironment( "SMTWithAspectModelUrnId.aas.xml" ); final AasToAspectModelGenerator aspectModelGenerator = AasToAspectModelGenerator.fromEnvironment( aasEnvironment ); - assertThat( aspectModelGenerator.generateAspects() ).singleElement().satisfies( aspect -> + assertThat( aspectModelGenerator.generate() ).map( AspectArtifact::getContent ).singleElement().satisfies( aspect -> assertThat( aspect.urn().toString() ).isEqualTo( "urn:samm:com.example:1.0.0#Submodel1" ) ); } @@ -99,7 +151,7 @@ void testGetAspectModelUrnFromConceptDescription() { // Submodel has a Concept Description that points to an Aspect Model URN final Environment aasEnvironment = loadEnvironment( "SMTWithAspectModelUrnInConceptDescription.aas.xml" ); final AasToAspectModelGenerator aspectModelGenerator = AasToAspectModelGenerator.fromEnvironment( aasEnvironment ); - assertThat( aspectModelGenerator.generateAspects() ).singleElement().satisfies( aspect -> + assertThat( aspectModelGenerator.generate() ).map( AspectArtifact::getContent ).singleElement().satisfies( aspect -> assertThat( aspect.urn().toString() ).isEqualTo( "urn:samm:com.example:1.0.0#Submodel1" ) ); } @@ -109,7 +161,7 @@ void testConstructAspectModelUrn1() { // It has a version and an IRI identifier and an idShort final Environment aasEnvironment = loadEnvironment( "SMTAspectModelUrnInConstruction1.aas.xml" ); final AasToAspectModelGenerator aspectModelGenerator = AasToAspectModelGenerator.fromEnvironment( aasEnvironment ); - assertThat( aspectModelGenerator.generateAspects() ).singleElement().satisfies( aspect -> + assertThat( aspectModelGenerator.generate() ).map( AspectArtifact::getContent ).singleElement().satisfies( aspect -> assertThat( aspect.urn().toString() ).isEqualTo( "urn:samm:com.example.www:1.2.3#Submodel1" ) ); } @@ -119,7 +171,7 @@ void testConstructAspectModelUrn2() { // It has a version and an IRDI identifier and an idShort final Environment aasEnvironment = loadEnvironment( "SMTAspectModelUrnInConstruction2.aas.xml" ); final AasToAspectModelGenerator aspectModelGenerator = AasToAspectModelGenerator.fromEnvironment( aasEnvironment ); - assertThat( aspectModelGenerator.generateAspects() ).singleElement().satisfies( aspect -> + assertThat( aspectModelGenerator.generate() ).map( AspectArtifact::getContent ).singleElement().satisfies( aspect -> assertThat( aspect.urn().toString() ).isEqualTo( "urn:samm:com.example:1.2.3#Submodel1" ) ); } @@ -129,7 +181,7 @@ void testConstructAspectModelUrn3() { // It has an IRDI identifier and an idShort, but no version final Environment aasEnvironment = loadEnvironment( "SMTAspectModelUrnInConstruction3.aas.xml" ); final AasToAspectModelGenerator aspectModelGenerator = AasToAspectModelGenerator.fromEnvironment( aasEnvironment ); - assertThat( aspectModelGenerator.generateAspects() ).singleElement().satisfies( aspect -> + assertThat( aspectModelGenerator.generate() ).map( AspectArtifact::getContent ).singleElement().satisfies( aspect -> assertThat( aspect.urn().toString() ).isEqualTo( "urn:samm:com.example:1.0.0#Submodel1" ) ); } @@ -139,7 +191,7 @@ void testConstructAspectModelUrn4() { // It has an IRDI identifier, but no idShort and no version final Environment aasEnvironment = loadEnvironment( "SMTAspectModelUrnInConstruction4.aas.xml" ); final AasToAspectModelGenerator aspectModelGenerator = AasToAspectModelGenerator.fromEnvironment( aasEnvironment ); - assertThat( aspectModelGenerator.generateAspects() ).singleElement().satisfies( aspect -> + assertThat( aspectModelGenerator.generate() ).map( AspectArtifact::getContent ).singleElement().satisfies( aspect -> assertThat( aspect.urn().toString() ).isEqualTo( "urn:samm:com.example:1.0.0#AAAAAA000abf2fd07" ) ); } From f20bbbba2b891b3f335743ea43dd0e1cdcb81f50 Mon Sep 17 00:00:00 2001 From: "Textor Andreas (BCI/ESW17)" Date: Fri, 6 Dec 2024 15:04:06 +0100 Subject: [PATCH 4/7] Add IDTA submodel-templates as git submodule --- .github/workflows/pull-request-check.yml | 2 + .github/workflows/release-workflow.yml | 10 ++++- .gitmodules | 4 ++ .../aas/AasToAspectModelGeneratorTest.java | 38 +++++++++++------- ...2-1-0_Template_Wireless Communication.aasx | Bin 9326 -> 0 bytes .../Sample_ZVEI_Digital_Nameplate_V10.aasx | Bin 7845 -> 0 bytes .../src/test/resources/submodel-templates | 1 + 7 files changed, 40 insertions(+), 15 deletions(-) create mode 100644 .gitmodules delete mode 100644 core/esmf-aspect-model-aas-generator/src/test/resources/idta/IDTA 02022-1-0_Template_Wireless Communication.aasx delete mode 100644 core/esmf-aspect-model-aas-generator/src/test/resources/idta/Sample_ZVEI_Digital_Nameplate_V10.aasx create mode 160000 core/esmf-aspect-model-aas-generator/src/test/resources/submodel-templates diff --git a/.github/workflows/pull-request-check.yml b/.github/workflows/pull-request-check.yml index 59f6d651c..e2f507da7 100644 --- a/.github/workflows/pull-request-check.yml +++ b/.github/workflows/pull-request-check.yml @@ -16,6 +16,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + submodules: recursive - name: Check code style if: matrix.os == 'ubuntu-20.04' diff --git a/.github/workflows/release-workflow.yml b/.github/workflows/release-workflow.yml index 15e35fc18..955cc1c2d 100644 --- a/.github/workflows/release-workflow.yml +++ b/.github/workflows/release-workflow.yml @@ -12,6 +12,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + submodules: recursive # Required for Maven - name: Set up JDK 17 @@ -50,6 +52,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + submodules: recursive # Even though the build itself is done using the GraalVM JDK # (see below), we use the setup-java action to have GPG configured @@ -145,6 +149,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + submodules: recursive - name: Setup JDK uses: graalvm/setup-graalvm@2f25c0caae5b220866f732832d5e3e29ff493338 # v1.2.1 @@ -218,6 +224,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + submodules: recursive - name: Setup JDK uses: graalvm/setup-graalvm@2f25c0caae5b220866f732832d5e3e29ff493338 # v1.2.1 @@ -362,7 +370,7 @@ jobs: comment-template: | Release {release_link} addresses this. - # Sign SAML-CLI Windows executable + # Sign SAMM-CLI Windows executable - name: Get Artifact ID (Windows) shell: bash run: | diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..184240d8f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "core/esmf-aspect-model-aas-generator/src/test/resources/submodel-templates"] + path = core/esmf-aspect-model-aas-generator/src/test/resources/submodel-templates + url = https://github.com/admin-shell-io/submodel-templates.git + branch = main diff --git a/core/esmf-aspect-model-aas-generator/src/test/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGeneratorTest.java b/core/esmf-aspect-model-aas-generator/src/test/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGeneratorTest.java index 25d795efe..b9d5524fa 100644 --- a/core/esmf-aspect-model-aas-generator/src/test/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGeneratorTest.java +++ b/core/esmf-aspect-model-aas-generator/src/test/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGeneratorTest.java @@ -60,6 +60,9 @@ void testIdtaAasxFilesCanBeTranslated( final File aasxFile ) { try ( final InputStream input = new FileInputStream( aasxFile ) ) { final AasToAspectModelGenerator aspectModelGenerator = AasToAspectModelGenerator.fromAasx( input ); final List aspects = aspectModelGenerator.generate().map( AspectArtifact::getContent ).toList(); + if ( aspects.isEmpty() ) { + fail( "Translation of " + aasxFile.getName() + " yielded no Aspects" ); + } final String result = AspectSerializer.INSTANCE.aspectToString( aspects.iterator().next() ); final AspectModel aspectModel = new AspectModelLoader().load( new ByteArrayInputStream( result.getBytes() ) ); final List violations = new AspectModelValidator().validateModel( aspectModel ); @@ -81,26 +84,44 @@ void testIdtaAasxFilesCanBeTranslated( final File aasxFile ) { } } + private static final List IGNORED_AASX_FILES = List.of( + "IDTA02026-1-0_Template_ProvisionOf3DModels.aasx", + "IDTA 02004-1-2_Template_Handover Documentation.aasx", + "Sample_ZVEI_Digital_Nameplate_V10.aasx", + "SMT_Pure_Technical_Data_V11.aasx", + "sample-zvei-techdata-V11.aasx", + "SMT_qualified_Technical_Data_V11.aasx", + "IDTA 02010-1-0_Template_ServiceRequestNotification.aasx", + "IDTA 02011-1-0_Template_HierarchicalStructuresEnablingBoM.aasx", + "IDTA 02011-1-1_Template_HSEBoM.aasx" + ); + protected static Stream idtaSubmodelFiles() throws URISyntaxException, IOException { + final String submodelTemplatesMissing = + "IDTA AASX files not found. Please make sure they are available; in the project root run: git submodule update --init " + + "--recursive"; final URL resource = AasToAspectModelGeneratorTest.class.getResource( "/submodel-templates" ); try ( final Stream stream = Files.walk( Paths.get( resource.toURI() ) ) ) { final List list = stream.filter( Files::isRegularFile ) .filter( file -> file.getFileName().toString().endsWith( ".aasx" ) ) .map( Path::toFile ) + .filter( file -> !IGNORED_AASX_FILES.contains( file.getName() ) ) .map( file -> Arguments.of( Named.of( file.getName(), file ) ) ) .toList(); if ( list.isEmpty() ) { - fail( "IDTA AASX files not found. Please make sure they are available; in the project root run: git submodule update --init " - + "--recursive" ); + fail( submodelTemplatesMissing ); } return list.stream(); + } catch ( final NullPointerException exception ) { + fail( submodelTemplatesMissing ); + return Stream.empty(); } } @Test void testSeeReferences() { final InputStream inputStream = AasToAspectModelGeneratorTest.class.getClassLoader().getResourceAsStream( - "idta/IDTA 02022-1-0_Template_Wireless Communication.aasx" ); + "submodel-templates/published/Wireless Communication/1/0/IDTA 02022-1-0_Template_Wireless Communication.aasx" ); final AasToAspectModelGenerator aspectModelGenerator = AasToAspectModelGenerator.fromAasx( inputStream ); final List aspects = aspectModelGenerator.generate().map( AspectArtifact::getContent ).toList(); @@ -203,15 +224,4 @@ private Environment loadEnvironment( final String name ) { } return null; } - - private InputStream getIdtaModel( final String path ) { - try { - final URL url = new URL( - "https://github.com/admin-shell-io/submodel-templates/raw/refs/heads/main/published/" + path.replaceAll( " ", "%20" ) ); - return url.openStream(); - } catch ( final Exception e ) { - e.printStackTrace(); - return null; - } - } } diff --git a/core/esmf-aspect-model-aas-generator/src/test/resources/idta/IDTA 02022-1-0_Template_Wireless Communication.aasx b/core/esmf-aspect-model-aas-generator/src/test/resources/idta/IDTA 02022-1-0_Template_Wireless Communication.aasx deleted file mode 100644 index 39a0e562b3e963aed8574e53ebe17b1a7cb9cb83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9326 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 Date: Fri, 6 Dec 2024 15:30:52 +0100 Subject: [PATCH 5/7] Add assertions for correct case of generated model elements --- .../aas/AasToAspectModelGeneratorTest.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/core/esmf-aspect-model-aas-generator/src/test/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGeneratorTest.java b/core/esmf-aspect-model-aas-generator/src/test/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGeneratorTest.java index b9d5524fa..ba28f6e5f 100644 --- a/core/esmf-aspect-model-aas-generator/src/test/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGeneratorTest.java +++ b/core/esmf-aspect-model-aas-generator/src/test/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGeneratorTest.java @@ -39,6 +39,9 @@ import org.eclipse.esmf.aspectmodel.validation.services.ViolationFormatter; import org.eclipse.esmf.metamodel.Aspect; import org.eclipse.esmf.metamodel.AspectModel; +import org.eclipse.esmf.metamodel.Operation; +import org.eclipse.esmf.metamodel.Property; +import org.eclipse.esmf.metamodel.Unit; import org.eclipse.esmf.test.TestAspect; import org.eclipse.esmf.test.TestResources; @@ -65,6 +68,19 @@ void testIdtaAasxFilesCanBeTranslated( final File aasxFile ) { } final String result = AspectSerializer.INSTANCE.aspectToString( aspects.iterator().next() ); final AspectModel aspectModel = new AspectModelLoader().load( new ByteArrayInputStream( result.getBytes() ) ); + + aspectModel.elements().forEach( element -> { + if ( element instanceof Property || element instanceof Operation || element instanceof Unit ) { + assertThat( element.getName().charAt( 0 ) ) + .describedAs( element.getName() + " is a " + element.getClass().getSimpleName() + " and must be lower case" ) + .isLowerCase(); + } else { + assertThat( element.getName().charAt( 0 ) ) + .describedAs( element.getName() + " is a " + element.getClass().getSimpleName() + " and must be upper case" ) + .isUpperCase(); + } + } ); + final List violations = new AspectModelValidator().validateModel( aspectModel ); if ( !violations.isEmpty() ) { final String report = new ViolationFormatter().apply( violations ); From ec8a641e1314822f3499ef8627e2241186815433 Mon Sep 17 00:00:00 2001 From: "Textor Andreas (BCI/ESW17)" Date: Fri, 6 Dec 2024 15:33:33 +0100 Subject: [PATCH 6/7] Fix errors in test file --- .../aspectmodel/aas/AasToAspectModelGeneratorTest.java | 10 +++++----- .../src/test/resources/AspectWithEntity.aas.xml | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/esmf-aspect-model-aas-generator/src/test/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGeneratorTest.java b/core/esmf-aspect-model-aas-generator/src/test/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGeneratorTest.java index ba28f6e5f..9e01c8dc0 100644 --- a/core/esmf-aspect-model-aas-generator/src/test/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGeneratorTest.java +++ b/core/esmf-aspect-model-aas-generator/src/test/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGeneratorTest.java @@ -146,9 +146,7 @@ void testSeeReferences() { aspects.stream() .flatMap( aspect -> aspect.getProperties().stream() ) .flatMap( property -> property.getSee().stream() ) - .forEach( see -> { - assertThat( see ).doesNotContain( "/ " ); - } ); + .forEach( see -> assertThat( see ).doesNotContain( "/ " ) ); } @ParameterizedTest @@ -162,9 +160,11 @@ void testRoundtripConversion( final TestAspect testAspect ) throws Deserializati assertThat( generatedAspect.urn() ).isEqualTo( aspect.urn() ) ); } ).doesNotThrowAnyException(); + final byte[] content = new AspectModelAasGenerator( aspect, + AasGenerationConfigBuilder.builder().format( AasFileFormat.XML ).build() ).getContent(); + assertThat( new String( content ) ).doesNotContain( "Optional[" ); final Environment aasEnvironmentFromXml = new XmlDeserializer().read( - new ByteArrayInputStream( new AspectModelAasGenerator( aspect, - AasGenerationConfigBuilder.builder().format( AasFileFormat.XML ).build() ).getContent() ) ); + new ByteArrayInputStream( content ) ); assertForValidator.accept( AasToAspectModelGenerator.fromEnvironment( aasEnvironmentFromXml ) ); final Environment aasEnvironmentFromJson = new JsonDeserializer().read( diff --git a/core/esmf-aspect-model-aas-generator/src/test/resources/AspectWithEntity.aas.xml b/core/esmf-aspect-model-aas-generator/src/test/resources/AspectWithEntity.aas.xml index b3cb3faf3..bbd654fcc 100644 --- a/core/esmf-aspect-model-aas-generator/src/test/resources/AspectWithEntity.aas.xml +++ b/core/esmf-aspect-model-aas-generator/src/test/resources/AspectWithEntity.aas.xml @@ -19,7 +19,7 @@ GlobalReference - Optional[urn:samm:org.eclipse.esmf.test:1.0.0#AspectWithEntity] + urn:samm:org.eclipse.esmf.test:1.0.0#AspectWithEntity @@ -62,7 +62,7 @@ - Optional[urn:samm:org.eclipse.esmf.test:1.0.0#AspectWithEntity]/submodel + urn:samm:org.eclipse.esmf.test:1.0.0#AspectWithEntity/submodel Template ModelReference @@ -144,7 +144,7 @@ GlobalReference - Optional[urn:samm:org.eclipse.esmf.test:1.0.0#AspectWithEntity] + urn:samm:org.eclipse.esmf.test:1.0.0#AspectWithEntity @@ -189,7 +189,7 @@ GlobalReference - Optional[urn:samm:org.eclipse.esmf.test:1.0.0#entityProperty] + urn:samm:org.eclipse.esmf.test:1.0.0#entityProperty @@ -237,7 +237,7 @@ GlobalReference - Optional[urn:samm:org.eclipse.esmf.test:1.0.0#testProperty] + urn:samm:org.eclipse.esmf.test:1.0.0#testProperty From 27e411d253e0620990d58be1550ff8606e5b12e4 Mon Sep 17 00:00:00 2001 From: "Textor Andreas (BCI/ESW17)" Date: Fri, 6 Dec 2024 15:34:54 +0100 Subject: [PATCH 7/7] Fix casing of SAMM model elements generated from AASX --- .../aas/AasToAspectModelGenerator.java | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGenerator.java b/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGenerator.java index ee3a7b36d..f8b13ddb1 100644 --- a/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGenerator.java +++ b/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AasToAspectModelGenerator.java @@ -257,35 +257,37 @@ private String randomElementName( final Object object ) { return DigestUtils.sha1Hex( object.toString() ).substring( 0, 10 ); } - private ElementName determineSubmodelName( final Submodel submodel ) { + private ElementName determineSubmodelName( final Submodel submodel, final boolean upperCase ) { // idShort present? Great. if ( submodel.getIdShort() != null ) { - return new ElementName( sanitizeAspectModelElementName( submodel.getIdShort() ), false ); + return new ElementName( sanitizeAspectModelElementName( submodel.getIdShort(), upperCase ), false ); } // We only have an ID. Is it an IRDI? If it is, use its printable parts as element name, with a hash // suffix to prevent name clashes. final String id = submodel.getId(); LOG.warn( "Submodel with id {} has no idShort", id ); final Optional nameFromIrdi = Irdi.from( id ).map( irdi -> - sanitizeAspectModelElementName( irdi.toString() ) + DigestUtils.sha1Hex( id ).substring( 0, 8 ) ); + sanitizeAspectModelElementName( irdi.toString(), upperCase ) + DigestUtils.sha1Hex( id ).substring( 0, 8 ) ); // Fallback: Hash the id return new ElementName( nameFromIrdi.orElseGet( () -> randomElementName( id ) ), true ); } - private ElementName determineSubmodelElementName( final SubmodelElement submodelElement, final String namePrefix ) { + private ElementName determineSubmodelElementName( final SubmodelElement submodelElement, final String namePrefix, + final boolean upperCase ) { if ( submodelElement.getIdShort() == null ) { return new ElementName( namePrefix + StringUtils.capitalize( randomElementName( submodelElement ) ), true ); } final String idPart = namePrefix.isEmpty() ? submodelElement.getIdShort() : StringUtils.capitalize( submodelElement.getIdShort() ); - return new ElementName( sanitizeAspectModelElementName( namePrefix + idPart ), false ); + return new ElementName( sanitizeAspectModelElementName( namePrefix + idPart, upperCase ), false ); } - private String sanitizeAspectModelElementName( final String potentialIdentifier ) { - return potentialIdentifier.chars() + private String sanitizeAspectModelElementName( final String potentialIdentifier, final boolean upperCase ) { + final String identifier = potentialIdentifier.chars() .dropWhile( character -> !Character.isJavaIdentifierStart( character ) ) .filter( Character::isJavaIdentifierPart ) .mapToObj( character -> String.valueOf( (char) character ) ) .collect( Collectors.joining() ); + return upperCase ? StringUtils.capitalize( identifier ) : StringUtils.uncapitalize( identifier ); } private Optional aspectModelUrnFromId( final Identifiable element ) { @@ -310,7 +312,7 @@ private boolean impliesCollectionAspect( final List properties ) { } private Aspect submodelToAspect( final Submodel submodel ) { - final ElementName aspectName = determineSubmodelName( submodel ); + final ElementName aspectName = determineSubmodelName( submodel, true ); aspectUrn = aspectModelUrnFromId( submodel ) .or( () -> aspectModelUrnFromSemanticId( submodel ) ) .orElseGet( () -> AspectModelUrn.fromUrn( String.format( "%s:%s:%s:%s#%s", @@ -404,15 +406,16 @@ private DetermineAutomatically() { private record UseGivenUrn( AspectModelUrn aspectModelUrn ) implements ElementNamingStrategy {} - private MetaModelBaseAttributes baseAttributes( final SubmodelElement element, final ElementNamingStrategy elementNamingStrategy ) { + private MetaModelBaseAttributes baseAttributes( final SubmodelElement element, final ElementNamingStrategy elementNamingStrategy, + final boolean upperCase ) { final ElementName elementName; final AspectModelUrn urn; if ( elementNamingStrategy instanceof final DetermineAutomatically automatically ) { - elementName = determineSubmodelElementName( element, automatically.namePrefix() ); + elementName = determineSubmodelElementName( element, automatically.namePrefix(), upperCase ); urn = aspectModelUrnFromSemanticId( element ) .orElseGet( () -> aspectUrn.withName( elementName.name() ) ); } else if ( elementNamingStrategy instanceof final UseGivenUrn givenUrn ) { - elementName = determineSubmodelElementName( element, "" ); + elementName = determineSubmodelElementName( element, "", upperCase ); urn = givenUrn.aspectModelUrn(); } else { throw new AspectModelGenerationException( "Unknown ElementNamingStrategy" ); @@ -433,7 +436,7 @@ private Property createProperty( final org.eclipse.digitaltwin.aas4j.v3.model.Su return existingProperty; } - final MetaModelBaseAttributes metaModelBaseAttributes = baseAttributes( submodelElement, new DetermineAutomatically() ); + final MetaModelBaseAttributes metaModelBaseAttributes = baseAttributes( submodelElement, new DetermineAutomatically(), false ); final Characteristic characteristic = createCharacteristic( submodelElement, metaModelBaseAttributes.urn() ); final Optional exampleValue = submodelElement instanceof final org.eclipse.digitaltwin.aas4j.v3.model.Property property @@ -455,7 +458,7 @@ private Property createProperty( final org.eclipse.digitaltwin.aas4j.v3.model.Su } private Operation createOperation( final org.eclipse.digitaltwin.aas4j.v3.model.Operation operation ) { - final MetaModelBaseAttributes metaModelBaseAttributes = baseAttributes( operation, new DetermineAutomatically() ); + final MetaModelBaseAttributes metaModelBaseAttributes = baseAttributes( operation, new DetermineAutomatically(), false ); final List potentialOutputs = Stream.concat( operation.getOutputVariables().stream(), operation.getInoutputVariables().stream() ).toList(); if ( potentialOutputs.size() > 1 ) { @@ -485,7 +488,7 @@ private Optional determineOperationProperty( final OperationVariable v } private Event createEvent( final EventElement event ) { - final MetaModelBaseAttributes metaModelBaseAttributes = baseAttributes( event, new DetermineAutomatically() ); + final MetaModelBaseAttributes metaModelBaseAttributes = baseAttributes( event, new DetermineAutomatically(), true ); // Since an AAS EventElement/BasicEvent does not have Properties but only info about the broker, we can't create anything // meaningful here LOG.warn( "Creating event {} with empty list of properties", metaModelBaseAttributes.urn().getName() ); @@ -541,7 +544,8 @@ private Characteristic createCharacteristicFromRelationShipElement( final Relati final String characteristicDescription = "First reference: %s, second reference: %s".formatted( describeReference.apply( relationshipElement.getFirst() ), describeReference.apply( relationshipElement.getSecond() ) ); - final ElementName elementName = determineSubmodelElementName( relationshipElement, propertyUrn.getName() + "RelationshipElement" ); + final ElementName elementName = determineSubmodelElementName( relationshipElement, propertyUrn.getName() + "RelationshipElement", + true ); final AspectModelUrn urn = aspectModelUrnFromSemanticId( relationshipElement ) .orElseGet( () -> aspectUrn.withName( elementName.name() ) ); @@ -608,7 +612,7 @@ private Characteristic createCharacteristicFromRange( final Range range, final A Optional.of( new DefaultScalar( dataTypeUri ) ) ); final MetaModelBaseAttributes traitMetaModelBaseAttributes = baseAttributes( range, new DetermineAutomatically( - propertyUrn.getName() + "Trait" ) ); + propertyUrn.getName() + "Trait" ), true ); return new DefaultTrait( traitMetaModelBaseAttributes, baseCharacteristic, List.of( constraint ) ); } @@ -626,7 +630,7 @@ private Characteristic createCharacteristicFromCapability( final Capability capa private Characteristic createCharacteristicFromEntity( final org.eclipse.digitaltwin.aas4j.v3.model.Entity entity, final AspectModelUrn propertyUrn ) { final MetaModelBaseAttributes metaModelBaseAttributes = baseAttributes( entity, - new DetermineAutomatically( propertyUrn.getName() + "Entity" ) ); + new DetermineAutomatically( propertyUrn.getName() + "Entity" ), true ); final List properties = entity.getStatements().stream() .map( this::createProperty ) .toList(); @@ -636,7 +640,7 @@ private Characteristic createCharacteristicFromEntity( final org.eclipse.digital private Characteristic createCharacteristicFromSubmodelElementCollection( final SubmodelElementCollection submodelElementCollection ) { final MetaModelBaseAttributes metaModelBaseAttributes = baseAttributes( submodelElementCollection, - new DetermineAutomatically( "ElementCollection" + randomElementName( submodelElementCollection ) ) ); + new DetermineAutomatically( "ElementCollection" + randomElementName( submodelElementCollection ) ), true ); final List properties = submodelElementCollection.getValue().stream() .map( this::createProperty ) .toList(); @@ -649,7 +653,7 @@ private Characteristic createCharacteristicFromSubmodelElementList( final Submod final AasSubmodelElements type = submodelElementList.getTypeValueListElement(); final Characteristic elementCharacteristic = createCharacteristic( type, submodelElementList, propertyUrn ); final MetaModelBaseAttributes metaModelBaseAttributes = baseAttributes( submodelElementList, - new DetermineAutomatically( propertyUrn.getName() + "List" ) ); + new DetermineAutomatically( propertyUrn.getName() + "List" ), true ); return new DefaultList( metaModelBaseAttributes, Optional.empty(), Optional.of( elementCharacteristic ) ); } @@ -672,7 +676,7 @@ private AasSubmodelElements submodelElementType( final SubmodelElement element ) private Characteristic createDefaultScalarCharacteristic( final SubmodelElement submodelElement, final String dataTypeUri, final ElementNamingStrategy elementNamingStrategy ) { - final MetaModelBaseAttributes metaModelBaseAttributes = baseAttributes( submodelElement, elementNamingStrategy ); + final MetaModelBaseAttributes metaModelBaseAttributes = baseAttributes( submodelElement, elementNamingStrategy, true ); return new DefaultCharacteristic( metaModelBaseAttributes, Optional.of( new DefaultScalar( dataTypeUri ) ) ); }