Skip to content

Commit 9fa42dd

Browse files
authored
Merge branch 'main' into bugfix/652-aspect-model-loader-throws-npe
2 parents ed986d3 + 32bceac commit 9fa42dd

File tree

5 files changed

+82
-32
lines changed

5 files changed

+82
-32
lines changed

core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AspectModelAasVisitor.java

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
package org.eclipse.esmf.aspectmodel.aas;
1414

1515
import java.nio.charset.StandardCharsets;
16+
import java.security.MessageDigest;
17+
import java.security.NoSuchAlgorithmException;
1618
import java.util.ArrayList;
1719
import java.util.Collections;
1820
import java.util.HashSet;
@@ -105,6 +107,7 @@ public class AspectModelAasVisitor implements AspectVisitor<Environment, Context
105107
public static final String ADMIN_SHELL_NAME = "defaultAdminShell";
106108
public static final String DEFAULT_LOCALE = "en";
107109
public static final String CONCEPT_DESCRIPTION_CATEGORY = "APPLICATION_CLASS";
110+
public static final String ALLOWS_ENUMERATION_VALUE_REGEX = "[^a-zA-Z0-9-_]";
108111
public static final String CONCEPT_DESCRIPTION_DATA_SPECIFICATION_URL =
109112
"https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIec61360/3/0";
110113

@@ -207,7 +210,7 @@ public Environment visitAspect( final Aspect aspect, final Context context ) {
207210
final Submodel submodel = usedContext.getSubmodel();
208211
submodel.setIdShort( aspect.getName() );
209212
submodel.setId( submodelId );
210-
submodel.setSemanticId( buildReferenceToConceptDescription( aspect ) );
213+
submodel.setSemanticId( buildAspectReferenceToGlobalReference( aspect ) );
211214
submodel.setSupplementalSemanticIds( buildGlobalReferenceForSeeReferences( aspect ) );
212215
submodel.setDescription( LangStringMapper.TEXT.map( aspect.getDescriptions() ) );
213216
submodel.setKind( usedContext.getModelingKind() );
@@ -344,19 +347,25 @@ private OperationVariable mapOperationVariable( final Property property, final C
344347
}
345348

346349
private Reference buildReferenceToEnumValue( final Enumeration enumeration, final String value ) {
350+
final String updatedValue;
351+
try {
352+
updatedValue = !value.matches( ALLOWS_ENUMERATION_VALUE_REGEX ) ? transformEnumerationValue( value ) : value;
353+
} catch ( final NoSuchAlgorithmException e ) {
354+
throw new IllegalStateException( e );
355+
}
347356
final Key key = new DefaultKey.Builder()
348357
.type( KeyTypes.DATA_ELEMENT )
349-
.value( DEFAULT_MAPPER.determineIdentifierFor( enumeration ) + ":" + value )
358+
.value( DEFAULT_MAPPER.determineIdentifierFor( enumeration ) + ":" + updatedValue )
350359
.build();
351360
return new DefaultReference.Builder().type( ReferenceTypes.MODEL_REFERENCE ).keys( key ).build();
352361
}
353362

354-
private Reference buildReferenceToConceptDescription( final Aspect aspect ) {
363+
private Reference buildAspectReferenceToGlobalReference( final Aspect aspect ) {
355364
final Key key = new DefaultKey.Builder()
356-
.type( KeyTypes.CONCEPT_DESCRIPTION )
365+
.type( KeyTypes.GLOBAL_REFERENCE )
357366
.value( DEFAULT_MAPPER.determineIdentifierFor( aspect ) )
358367
.build();
359-
return new DefaultReference.Builder().type( ReferenceTypes.MODEL_REFERENCE ).keys( key ).build();
368+
return new DefaultReference.Builder().type( ReferenceTypes.EXTERNAL_REFERENCE ).keys( key ).build();
360369
}
361370

362371
private Reference buildReferenceForSeeElement( final String seeReference ) {
@@ -555,8 +564,9 @@ private <T extends Collection> Environment visitCollectionProperty( final T coll
555564
.displayName( LangStringMapper.NAME.map( property.getPreferredNames() ) )
556565
.description( LangStringMapper.TEXT.map( property.getDescriptions() ) )
557566
.value( List.of( decideOnMapping( property, context ) ) )
558-
.typeValueListElement( AasSubmodelElements.SUBMODEL_ELEMENT )
559-
.supplementalSemanticIds( buildGlobalReferenceForSeeReferences( collection ) );
567+
.typeValueListElement( AasSubmodelElements.SUBMODEL_ELEMENT_COLLECTION )
568+
.supplementalSemanticIds( buildGlobalReferenceForSeeReferences( collection ) )
569+
.orderRelevant( false );
560570

561571
if ( !collection.isAnonymous() ) {
562572
submodelBuilder.semanticId( buildReferenceForCollection( collection.urn().getUrn().toString() ) );
@@ -574,14 +584,15 @@ private <T extends Collection> Environment visitCollectionProperty( final T coll
574584
context.getRawPropertyValue()
575585
.filter( ArrayNode.class::isInstance )
576586
.map( ArrayNode.class::cast )
577-
.map( arrayNode -> ( Property property ) -> {
587+
.map( arrayNode -> ( final Property property ) -> {
578588
final List<SubmodelElement> values = getValues( collection, property, context, arrayNode );
579589
return new DefaultSubmodelElementList.Builder()
580590
.idShort( property.getName() )
581591
.displayName( LangStringMapper.NAME.map( property.getPreferredNames() ) )
582592
.description( LangStringMapper.TEXT.map( property.getDescriptions() ) )
583593
.value( values )
584594
.typeValueListElement( AasSubmodelElements.SUBMODEL_ELEMENT )
595+
.orderRelevant( false )
585596
.build();
586597
} ) )
587598
.orElse( defaultBuilder );
@@ -734,6 +745,32 @@ public Environment visitEnumeration( final Enumeration enumeration, final Contex
734745
return context.environment;
735746
}
736747

748+
/**
749+
* Transforms a given enumValue string to a specific format.
750+
* The transformation is done by removing spaces and special characters
751+
* from the input string, then appending the first 8 characters of the
752+
* sha256 hash of the cleaned string to the provided prefix.
753+
*
754+
* @param enumValue the input string to be transformed
755+
* @return the transformed string in the format "_role[8_characters_of_hash]"
756+
* @throws NoSuchAlgorithmException if the SHA-256 algorithm is not available
757+
*/
758+
private String transformEnumerationValue( final String enumValue ) throws NoSuchAlgorithmException {
759+
final String cleanedEnumValue = enumValue.replaceAll( ALLOWS_ENUMERATION_VALUE_REGEX, "" );
760+
761+
final MessageDigest digest = MessageDigest.getInstance( "SHA-256" );
762+
final byte[] hashBytes = digest.digest( cleanedEnumValue.getBytes( StandardCharsets.UTF_8 ) );
763+
764+
final StringBuilder hexString = new StringBuilder();
765+
for ( final byte b : hashBytes ) {
766+
hexString.append( String.format( "%02x", b ) );
767+
}
768+
769+
final String hashPrefix = hexString.substring( 0, 8 );
770+
771+
return "_" + cleanedEnumValue + hashPrefix;
772+
}
773+
737774
@Override
738775
public Environment visitState( final State state, final Context context ) {
739776
// Same handling as with enumerations

core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/DefaultPropertyMapper.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,17 @@
2323
public class DefaultPropertyMapper implements PropertyMapper<Property> {
2424
@Override
2525
public Property mapToAasProperty( final Type type, final org.eclipse.esmf.metamodel.Property property, final Context context ) {
26-
return new DefaultProperty.Builder()
27-
.idShort( context.getPropertyShortId() )
28-
.valueType( AasDataTypeMapper.mapAspectTypeToAasXsdDataType( mapType( type ) ) )
29-
.displayName( LangStringMapper.NAME.map( property.getPreferredNames() ) )
30-
.value( context.getPropertyValue( UNKNOWN_EXAMPLE ) )
31-
.semanticId( buildReferenceToGlobalReference( property ) )
32-
.build();
26+
final DefaultProperty defaultProperty = new DefaultProperty();
27+
defaultProperty.setIdShort( context.getPropertyShortId() );
28+
defaultProperty.setValueType( AasDataTypeMapper.mapAspectTypeToAasXsdDataType( mapType( type ) ) );
29+
defaultProperty.setDisplayName( LangStringMapper.NAME.map( property.getPreferredNames() ) );
30+
defaultProperty.setSemanticId( buildPropertyReferenceToGlobalReference( property ) );
31+
32+
if ( !context.getPropertyValue( UNKNOWN_EXAMPLE ).equals( UNKNOWN_EXAMPLE ) ) {
33+
defaultProperty.setValue( context.getPropertyValue( UNKNOWN_EXAMPLE ) );
34+
}
35+
36+
return defaultProperty;
3337
}
3438

3539
private String mapType( final Type type ) {

core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/LangStringPropertyMapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public MultiLanguageProperty mapToAasProperty( final Type type, final Property p
4646
return new DefaultMultiLanguageProperty.Builder().idShort( context.getPropertyShortId() )
4747
.description( LangStringMapper.TEXT.map( property.getDescriptions() ) )
4848
.displayName( LangStringMapper.NAME.map( property.getPreferredNames() ) )
49-
.semanticId( buildReferenceToGlobalReference( property ) )
49+
.semanticId( buildPropertyReferenceToGlobalReference( property ) )
5050
.value( extractLangStrings( property, context ) )
5151
.build();
5252
}

core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/PropertyMapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ default int compareTo( PropertyMapper<T> otherPropertyMapper ) {
8080
* @param property the property to build the reference for
8181
* @return the newly created reference
8282
*/
83-
default Reference buildReferenceToGlobalReference( final Property property ) {
83+
default Reference buildPropertyReferenceToGlobalReference( final Property property ) {
8484
final Key key = new DefaultKey.Builder()
8585
.type( KeyTypes.GLOBAL_REFERENCE )
8686
.value( determineIdentifierFor( property ) )

core/esmf-aspect-model-aas-generator/src/test/java/org/eclipse/esmf/aspectmodel/aas/AspectModelAasGeneratorTest.java

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
*/
1313
package org.eclipse.esmf.aspectmodel.aas;
1414

15+
import static org.assertj.core.api.Assertions.as;
1516
import static org.assertj.core.api.Assertions.assertThat;
1617
import static org.assertj.core.api.InstanceOfAssertFactories.type;
1718
import static org.junit.jupiter.api.Assertions.fail;
@@ -109,18 +110,21 @@ void generateAasxWithAspectDataForNestedEntityLists() throws DeserializationExce
109110
assertThat( env.getSubmodels() )
110111
.singleElement()
111112
.satisfies( subModel -> assertThat( subModel.getSubmodelElements() )
112-
.anySatisfy( sme ->
113-
assertThat( sme ).asInstanceOf( type( SubmodelElementList.class ) )
114-
.extracting( SubmodelElementList::getValue )
115-
.asInstanceOf( InstanceOfAssertFactories.LIST )
116-
.anySatisfy( entity ->
117-
assertThat( entity ).asInstanceOf( type( SubmodelElementCollection.class ) )
118-
.extracting( SubmodelElementCollection::getValue )
119-
.asInstanceOf( InstanceOfAssertFactories.LIST )
120-
.anySatisfy( property ->
121-
assertThat( property ).asInstanceOf( type( Property.class ) )
122-
.extracting( Property::getValue )
123-
.isEqualTo( "2.25" ) ) ) ) );
113+
.anySatisfy( sme -> {
114+
assertThat( sme ).asInstanceOf( type( SubmodelElementList.class ) )
115+
.extracting( SubmodelElementList::getValue )
116+
.asInstanceOf( InstanceOfAssertFactories.LIST )
117+
.anySatisfy( entity ->
118+
assertThat( entity ).asInstanceOf( type( SubmodelElementCollection.class ) )
119+
.extracting( SubmodelElementCollection::getValue )
120+
.asInstanceOf( InstanceOfAssertFactories.LIST )
121+
.anySatisfy( property ->
122+
assertThat( property ).asInstanceOf( type( Property.class ) )
123+
.extracting( Property::getValue )
124+
.isEqualTo( "2.25" ) ) );
125+
assertThat( ( ( SubmodelElementList ) sme ).getOrderRelevant() ).isFalse();
126+
}
127+
) );
124128
}
125129

126130
@Test
@@ -203,7 +207,8 @@ void testGenerateAasxFromAspectModelWithCollection() throws DeserializationExcep
203207
assertThat( submodelElement ).asInstanceOf( type( SubmodelElementList.class ) )
204208
.satisfies( submodelElementList -> {
205209
assertThat( submodelElementList.getIdShort() ).isEqualTo( "testProperty" );
206-
assertThat( submodelElementList.getTypeValueListElement() ).isEqualTo( AasSubmodelElements.SUBMODEL_ELEMENT );
210+
assertThat( submodelElementList.getTypeValueListElement() ).isEqualTo( AasSubmodelElements.SUBMODEL_ELEMENT_COLLECTION );
211+
assertThat( ( submodelElementList ).getOrderRelevant() ).isFalse();
207212
} );
208213

209214
assertThat( submodelElement.getSemanticId().getKeys().get( 0 ).getType() ).isEqualTo( KeyTypes.GLOBAL_REFERENCE );
@@ -220,7 +225,8 @@ void testGenerateAasxFromAspectModelWithList() throws DeserializationException {
220225
assertThat( submodelElement ).asInstanceOf( type( SubmodelElementList.class ) )
221226
.satisfies( submodelElementList -> {
222227
assertThat( submodelElementList.getIdShort() ).isEqualTo( "testProperty" );
223-
assertThat( submodelElementList.getTypeValueListElement() ).isEqualTo( AasSubmodelElements.SUBMODEL_ELEMENT );
228+
assertThat( submodelElementList.getTypeValueListElement() ).isEqualTo( AasSubmodelElements.SUBMODEL_ELEMENT_COLLECTION );
229+
assertThat( ( submodelElementList ).getOrderRelevant() ).isFalse();
224230
} );
225231

226232
assertThat( submodelElement.getSemanticId().getKeys().get( 0 ).getType() ).isEqualTo( KeyTypes.GLOBAL_REFERENCE );
@@ -237,7 +243,8 @@ void testGenerateAasxFromAspectModelWithSet() throws DeserializationException {
237243
assertThat( submodelElement ).asInstanceOf( type( SubmodelElementList.class ) )
238244
.satisfies( submodelElementList -> {
239245
assertThat( submodelElementList.getIdShort() ).isEqualTo( "testProperty" );
240-
assertThat( submodelElementList.getTypeValueListElement() ).isEqualTo( AasSubmodelElements.SUBMODEL_ELEMENT );
246+
assertThat( submodelElementList.getTypeValueListElement() ).isEqualTo( AasSubmodelElements.SUBMODEL_ELEMENT_COLLECTION );
247+
assertThat( ( submodelElementList ).getOrderRelevant() ).isFalse();
241248
} );
242249
assertThat( submodelElement.getSemanticId().getKeys().get( 0 ).getType() ).isEqualTo( KeyTypes.GLOBAL_REFERENCE );
243250

@@ -251,6 +258,7 @@ void testGenerateAasxFromAspectModelWithSortedSet() throws DeserializationExcept
251258
assertThat( env.getSubmodels().get( 0 ).getSubmodelElements() ).hasSize( 1 );
252259
final SubmodelElement submodelElement = env.getSubmodels().get( 0 ).getSubmodelElements().get( 0 );
253260
assertThat( submodelElement ).as( "SubmodelElement is not a SubmodelElementList" ).isInstanceOf( SubmodelElementList.class );
261+
assertThat( ( ( ( SubmodelElementList ) submodelElement ) ).getOrderRelevant() ).isFalse();
254262
assertThat( submodelElement.getIdShort() ).isEqualTo( "testProperty" );
255263
assertThat( submodelElement.getSemanticId().getKeys().get( 0 ).getType() ).isEqualTo( KeyTypes.GLOBAL_REFERENCE );
256264

@@ -413,6 +421,7 @@ void testGeneratedAasxFromAspectModelSemanticIdsAreGlobalReferences() throws Des
413421
final Property property = (Property) environment.getSubmodels().get( 0 ).getSubmodelElements().get( 0 );
414422

415423
assertThat( environment.getSubmodels().get( 0 ).getSubmodelElements() ).hasSize( 1 );
424+
assertThat( environment.getSubmodels().get( 0 ).getSemanticId().getKeys().get( 0 ).getType() ).isEqualTo( KeyTypes.GLOBAL_REFERENCE );
416425
assertThat( environment.getConceptDescriptions() ).hasSize( 2 );
417426
assertThat( environment.getConceptDescriptions().get( 1 ).getEmbeddedDataSpecifications() ).hasSize( 1 );
418427
assertThat( property.getDescription() ).isEmpty();

0 commit comments

Comments
 (0)