Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SAMM-CLI aas generation: Mapping of Set Characteristics #597

Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
package org.eclipse.esmf.aspectmodel.aas;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
Expand Down Expand Up @@ -105,6 +107,7 @@ public class AspectModelAasVisitor implements AspectVisitor<Environment, Context
public static final String ADMIN_SHELL_NAME = "defaultAdminShell";
public static final String DEFAULT_LOCALE = "en";
public static final String CONCEPT_DESCRIPTION_CATEGORY = "APPLICATION_CLASS";
public static final String ALLOWS_ENUMERATION_VALUE_REGEX = "[^a-zA-Z0-9-_]";
public static final String CONCEPT_DESCRIPTION_DATA_SPECIFICATION_URL =
"https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIec61360/3/0";

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

private Reference buildReferenceToEnumValue( final Enumeration enumeration, final String value ) {
final String updatedValue;
try {
updatedValue = !value.matches( ALLOWS_ENUMERATION_VALUE_REGEX ) ? transformEnumerationValue( value ) : value;
} catch ( final NoSuchAlgorithmException e ) {
throw new IllegalStateException( e );
}
final Key key = new DefaultKey.Builder()
.type( KeyTypes.DATA_ELEMENT )
.value( DEFAULT_MAPPER.determineIdentifierFor( enumeration ) + ":" + value )
.value( DEFAULT_MAPPER.determineIdentifierFor( enumeration ) + ":" + updatedValue )
.build();
return new DefaultReference.Builder().type( ReferenceTypes.MODEL_REFERENCE ).keys( key ).build();
}

private Reference buildReferenceToConceptDescription( final Aspect aspect ) {
private Reference buildAspectReferenceToGlobalReference( final Aspect aspect ) {
final Key key = new DefaultKey.Builder()
.type( KeyTypes.CONCEPT_DESCRIPTION )
.type( KeyTypes.GLOBAL_REFERENCE )
.value( DEFAULT_MAPPER.determineIdentifierFor( aspect ) )
.build();
return new DefaultReference.Builder().type( ReferenceTypes.MODEL_REFERENCE ).keys( key ).build();
return new DefaultReference.Builder().type( ReferenceTypes.EXTERNAL_REFERENCE ).keys( key ).build();
}

private Reference buildReferenceForSeeElement( final String seeReference ) {
Expand Down Expand Up @@ -555,8 +564,9 @@ private <T extends Collection> Environment visitCollectionProperty( final T coll
.displayName( LangStringMapper.NAME.map( property.getPreferredNames() ) )
.description( LangStringMapper.TEXT.map( property.getDescriptions() ) )
.value( List.of( decideOnMapping( property, context ) ) )
.typeValueListElement( AasSubmodelElements.SUBMODEL_ELEMENT )
.supplementalSemanticIds( buildGlobalReferenceForSeeReferences( collection ) );
.typeValueListElement( AasSubmodelElements.SUBMODEL_ELEMENT_COLLECTION )
Yauhenikapl marked this conversation as resolved.
Show resolved Hide resolved
.supplementalSemanticIds( buildGlobalReferenceForSeeReferences( collection ) )
.orderRelevant( false );

if ( !collection.isAnonymous() ) {
submodelBuilder.semanticId( buildReferenceForCollection( collection.urn().getUrn().toString() ) );
Expand All @@ -574,14 +584,15 @@ private <T extends Collection> Environment visitCollectionProperty( final T coll
context.getRawPropertyValue()
.filter( ArrayNode.class::isInstance )
.map( ArrayNode.class::cast )
.map( arrayNode -> ( Property property ) -> {
.map( arrayNode -> ( final Property property ) -> {
final List<SubmodelElement> values = getValues( collection, property, context, arrayNode );
return new DefaultSubmodelElementList.Builder()
.idShort( property.getName() )
.displayName( LangStringMapper.NAME.map( property.getPreferredNames() ) )
.description( LangStringMapper.TEXT.map( property.getDescriptions() ) )
.value( values )
.typeValueListElement( AasSubmodelElements.SUBMODEL_ELEMENT )
.typeValueListElement( AasSubmodelElements.SUBMODEL_ELEMENT_COLLECTION )
Yauhenikapl marked this conversation as resolved.
Show resolved Hide resolved
.orderRelevant( false )
.build();
} ) )
.orElse( defaultBuilder );
Expand Down Expand Up @@ -734,6 +745,32 @@ public Environment visitEnumeration( final Enumeration enumeration, final Contex
return context.environment;
}

/**
* Transforms a given enumValue string to a specific format.
* The transformation is done by removing spaces and special characters
* from the input string, then appending the first 8 characters of the
* sha256 hash of the cleaned string to the provided prefix.
*
* @param enumValue the input string to be transformed
* @return the transformed string in the format "_role[8_characters_of_hash]"
* @throws NoSuchAlgorithmException if the SHA-256 algorithm is not available
*/
private String transformEnumerationValue( final String enumValue ) throws NoSuchAlgorithmException {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would the mapping work in the opposite direction with this approach?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, that I have to add separate method in the opposite direction.

Thanks!

final String cleanedEnumValue = enumValue.replaceAll( ALLOWS_ENUMERATION_VALUE_REGEX, "" );

final MessageDigest digest = MessageDigest.getInstance( "SHA-256" );
final byte[] hashBytes = digest.digest( cleanedEnumValue.getBytes( StandardCharsets.UTF_8 ) );

final StringBuilder hexString = new StringBuilder();
for ( final byte b : hashBytes ) {
hexString.append( String.format( "%02x", b ) );
}

final String hashPrefix = hexString.substring( 0, 8 );

return "_" + cleanedEnumValue + hashPrefix;
}

@Override
public Environment visitState( final State state, final Context context ) {
// Same handling as with enumerations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,17 @@
public class DefaultPropertyMapper implements PropertyMapper<Property> {
@Override
public Property mapToAasProperty( final Type type, final org.eclipse.esmf.metamodel.Property property, final Context context ) {
return new DefaultProperty.Builder()
.idShort( context.getPropertyShortId() )
.valueType( AasDataTypeMapper.mapAspectTypeToAasXsdDataType( mapType( type ) ) )
.displayName( LangStringMapper.NAME.map( property.getPreferredNames() ) )
.value( context.getPropertyValue( UNKNOWN_EXAMPLE ) )
.semanticId( buildReferenceToGlobalReference( property ) )
.build();
final DefaultProperty defaultProperty = new DefaultProperty();
defaultProperty.setIdShort( context.getPropertyShortId() );
defaultProperty.setValueType( AasDataTypeMapper.mapAspectTypeToAasXsdDataType( mapType( type ) ) );
defaultProperty.setDisplayName( LangStringMapper.NAME.map( property.getPreferredNames() ) );
defaultProperty.setSemanticId( buildPropertyReferenceToGlobalReference( property ) );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why propertyReference?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please explain more, because in this method "buildPropertyReferenceToGlobalReference" we building GlobalReference, not property reference?

@BirgitBoss


if ( !context.getPropertyValue( UNKNOWN_EXAMPLE ).equals( UNKNOWN_EXAMPLE ) ) {
defaultProperty.setValue( context.getPropertyValue( UNKNOWN_EXAMPLE ) );
}

return defaultProperty;
}

private String mapType( final Type type ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public MultiLanguageProperty mapToAasProperty( final Type type, final Property p
return new DefaultMultiLanguageProperty.Builder().idShort( context.getPropertyShortId() )
.description( LangStringMapper.TEXT.map( property.getDescriptions() ) )
.displayName( LangStringMapper.NAME.map( property.getPreferredNames() ) )
.semanticId( buildReferenceToGlobalReference( property ) )
.semanticId( buildPropertyReferenceToGlobalReference( property ) )
.value( extractLangStrings( property, context ) )
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ default int compareTo( PropertyMapper<T> otherPropertyMapper ) {
* @param property the property to build the reference for
* @return the newly created reference
*/
default Reference buildReferenceToGlobalReference( final Property property ) {
default Reference buildPropertyReferenceToGlobalReference( final Property property ) {
final Key key = new DefaultKey.Builder()
.type( KeyTypes.GLOBAL_REFERENCE )
.value( determineIdentifierFor( property ) )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/
package org.eclipse.esmf.aspectmodel.aas;

import static org.assertj.core.api.Assertions.as;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.InstanceOfAssertFactories.type;
import static org.junit.jupiter.api.Assertions.fail;
Expand All @@ -21,7 +22,6 @@
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Set;

import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
Expand Down Expand Up @@ -110,18 +110,21 @@ void generateAasxWithAspectDataForNestedEntityLists() throws DeserializationExce
assertThat( env.getSubmodels() )
.singleElement()
.satisfies( subModel -> assertThat( subModel.getSubmodelElements() )
.anySatisfy( sme ->
assertThat( sme ).asInstanceOf( type( SubmodelElementList.class ) )
.extracting( SubmodelElementList::getValue )
.asInstanceOf( InstanceOfAssertFactories.LIST )
.anySatisfy( entity ->
assertThat( entity ).asInstanceOf( type( SubmodelElementCollection.class ) )
.extracting( SubmodelElementCollection::getValue )
.asInstanceOf( InstanceOfAssertFactories.LIST )
.anySatisfy( property ->
assertThat( property ).asInstanceOf( type( Property.class ) )
.extracting( Property::getValue )
.isEqualTo( "2.25" ) ) ) ) );
.anySatisfy( sme -> {
assertThat( sme ).asInstanceOf( type( SubmodelElementList.class ) )
.extracting( SubmodelElementList::getValue )
.asInstanceOf( InstanceOfAssertFactories.LIST )
.anySatisfy( entity ->
assertThat( entity ).asInstanceOf( type( SubmodelElementCollection.class ) )
.extracting( SubmodelElementCollection::getValue )
.asInstanceOf( InstanceOfAssertFactories.LIST )
.anySatisfy( property ->
assertThat( property ).asInstanceOf( type( Property.class ) )
.extracting( Property::getValue )
.isEqualTo( "2.25" ) ) );
assertThat( ( ( SubmodelElementList ) sme ).getOrderRelevant() ).isFalse();
}
) );
}

@Test
Expand Down Expand Up @@ -204,7 +207,8 @@ void testGenerateAasxFromAspectModelWithCollection() throws DeserializationExcep
assertThat( submodelElement ).asInstanceOf( type( SubmodelElementList.class ) )
.satisfies( submodelElementList -> {
assertThat( submodelElementList.getIdShort() ).isEqualTo( "testProperty" );
assertThat( submodelElementList.getTypeValueListElement() ).isEqualTo( AasSubmodelElements.SUBMODEL_ELEMENT );
assertThat( submodelElementList.getTypeValueListElement() ).isEqualTo( AasSubmodelElements.SUBMODEL_ELEMENT_COLLECTION );
assertThat( ( submodelElementList ).getOrderRelevant() ).isFalse();
} );

assertThat( submodelElement.getSemanticId().getKeys().get( 0 ).getType() ).isEqualTo( KeyTypes.GLOBAL_REFERENCE );
Expand All @@ -221,7 +225,8 @@ void testGenerateAasxFromAspectModelWithList() throws DeserializationException {
assertThat( submodelElement ).asInstanceOf( type( SubmodelElementList.class ) )
.satisfies( submodelElementList -> {
assertThat( submodelElementList.getIdShort() ).isEqualTo( "testProperty" );
assertThat( submodelElementList.getTypeValueListElement() ).isEqualTo( AasSubmodelElements.SUBMODEL_ELEMENT );
assertThat( submodelElementList.getTypeValueListElement() ).isEqualTo( AasSubmodelElements.SUBMODEL_ELEMENT_COLLECTION );
assertThat( ( submodelElementList ).getOrderRelevant() ).isFalse();
} );

assertThat( submodelElement.getSemanticId().getKeys().get( 0 ).getType() ).isEqualTo( KeyTypes.GLOBAL_REFERENCE );
Expand All @@ -238,7 +243,8 @@ void testGenerateAasxFromAspectModelWithSet() throws DeserializationException {
assertThat( submodelElement ).asInstanceOf( type( SubmodelElementList.class ) )
.satisfies( submodelElementList -> {
assertThat( submodelElementList.getIdShort() ).isEqualTo( "testProperty" );
assertThat( submodelElementList.getTypeValueListElement() ).isEqualTo( AasSubmodelElements.SUBMODEL_ELEMENT );
assertThat( submodelElementList.getTypeValueListElement() ).isEqualTo( AasSubmodelElements.SUBMODEL_ELEMENT_COLLECTION );
assertThat( ( submodelElementList ).getOrderRelevant() ).isFalse();
} );
assertThat( submodelElement.getSemanticId().getKeys().get( 0 ).getType() ).isEqualTo( KeyTypes.GLOBAL_REFERENCE );

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

Expand Down Expand Up @@ -414,6 +421,7 @@ void testGeneratedAasxFromAspectModelSemanticIdsAreGlobalReferences() throws Des
final Property property = (Property) environment.getSubmodels().get( 0 ).getSubmodelElements().get( 0 );

assertThat( environment.getSubmodels().get( 0 ).getSubmodelElements() ).hasSize( 1 );
assertThat( environment.getSubmodels().get( 0 ).getSemanticId().getKeys().get( 0 ).getType() ).isEqualTo( KeyTypes.GLOBAL_REFERENCE );
assertThat( environment.getConceptDescriptions() ).hasSize( 2 );
assertThat( environment.getConceptDescriptions().get( 1 ).getEmbeddedDataSpecifications() ).hasSize( 1 );
assertThat( property.getDescription() ).isEmpty();
Expand Down
Loading