diff --git a/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AspectModelAasVisitor.java b/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AspectModelAasVisitor.java index f03033331..cda71eac8 100644 --- a/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AspectModelAasVisitor.java +++ b/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AspectModelAasVisitor.java @@ -62,6 +62,7 @@ import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; import org.eclipse.digitaltwin.aas4j.v3.model.ConceptDescription; import org.eclipse.digitaltwin.aas4j.v3.model.DataSpecificationIec61360; +import org.eclipse.digitaltwin.aas4j.v3.model.DataTypeDefXsd; import org.eclipse.digitaltwin.aas4j.v3.model.DataTypeIec61360; import org.eclipse.digitaltwin.aas4j.v3.model.EmbeddedDataSpecification; import org.eclipse.digitaltwin.aas4j.v3.model.Environment; @@ -72,6 +73,7 @@ import org.eclipse.digitaltwin.aas4j.v3.model.ModellingKind; import org.eclipse.digitaltwin.aas4j.v3.model.Operation; import org.eclipse.digitaltwin.aas4j.v3.model.OperationVariable; +import org.eclipse.digitaltwin.aas4j.v3.model.Qualifier; import org.eclipse.digitaltwin.aas4j.v3.model.Reference; import org.eclipse.digitaltwin.aas4j.v3.model.ReferenceTypes; import org.eclipse.digitaltwin.aas4j.v3.model.Submodel; @@ -92,6 +94,7 @@ import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultOperation; import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultOperationVariable; import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultProperty; +import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultQualifier; import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultReference; import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodel; import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementCollection; @@ -111,6 +114,9 @@ public class AspectModelAasVisitor implements AspectVisitor mapText( final Property property, final Contex final SubmodelElement element = context.getPropertyResult(); element.setSupplementalSemanticIds( updateGlobalReferenceWithSeeReferences( element, property ) ); + if ( property.isOptional() ) { + final List qualifiers = new ArrayList<>( element.getQualifiers() ); + qualifiers.add( buildZeroToOneCardinalityQualifier() ); + element.setQualifiers( qualifiers ); + } + if ( !property.getPayloadName().isEmpty() && !( element instanceof SubmodelElementList ) && !( element instanceof SubmodelElementCollection ) ) { @@ -458,6 +470,23 @@ private Reference buildReferenceForCollection( final String submodelId ) { .build(); } + private Qualifier buildZeroToOneCardinalityQualifier() { + final Key key = new DefaultKey.Builder() + .type( KeyTypes.GLOBAL_REFERENCE ) + .value( SMT_CARDINALITY_SEMANTIC_ID_URL ) + .build(); + final Reference semanticId = new DefaultReference.Builder() + .type( ReferenceTypes.EXTERNAL_REFERENCE ) + .keys( key ) + .build(); + return new DefaultQualifier.Builder() + .type( SMT_CARDINALITY_QUALIFIER_TYPE ) + .valueType( DataTypeDefXsd.STRING ) + .value( SMT_CARDINALITY_ZERO_TO_ONE ) + .semanticId( semanticId ) + .build(); + } + private void createConceptDescription( final Property property, final Context context ) { if ( property.getCharacteristic().isEmpty() ) { return; diff --git a/core/esmf-aspect-model-aas-generator/src/test/java/org/eclipse/esmf/aspectmodel/aas/AspectModelAasGeneratorTest.java b/core/esmf-aspect-model-aas-generator/src/test/java/org/eclipse/esmf/aspectmodel/aas/AspectModelAasGeneratorTest.java index a866f1f56..5aa5f52ff 100644 --- a/core/esmf-aspect-model-aas-generator/src/test/java/org/eclipse/esmf/aspectmodel/aas/AspectModelAasGeneratorTest.java +++ b/core/esmf-aspect-model-aas-generator/src/test/java/org/eclipse/esmf/aspectmodel/aas/AspectModelAasGeneratorTest.java @@ -312,6 +312,24 @@ void testGenerateAasxFromBammWithConstraint() throws DeserializationException { checkDataSpecificationIec61360( semanticIds, env ); } + @Test + void testOptionalPropertyHasZeroToOneCardinalityQualifier() throws DeserializationException { + final Environment env = getAssetAdministrationShellFromAspect( TestAspect.ASPECT_WITH_OPTIONAL_PROPERTY ); + final SubmodelElement element = env.getSubmodels().get( 0 ).getSubmodelElements().get( 0 ); + assertThat( element.getQualifiers() ) + .hasSize( 1 ) + .first() + .satisfies( qualifier -> { + assertThat( qualifier.getType() ).isEqualTo( AspectModelAasVisitor.SMT_CARDINALITY_QUALIFIER_TYPE ); + assertThat( qualifier.getValue() ).isEqualTo( AspectModelAasVisitor.SMT_CARDINALITY_ZERO_TO_ONE ); + assertThat( qualifier.getValueType() ).isEqualTo( DataTypeDefXsd.STRING ); + assertThat( qualifier.getSemanticId().getKeys() ) + .singleElement() + .satisfies( key -> assertThat( key.getValue() ) + .isEqualTo( AspectModelAasVisitor.SMT_CARDINALITY_SEMANTIC_ID_URL ) ); + } ); + } + @Test void testGenerateAasxFromAspectModelWithRecursivePropertyWithOptional() throws DeserializationException { final Environment env = getAssetAdministrationShellFromAspect( TestAspect.ASPECT_WITH_RECURSIVE_PROPERTY_WITH_OPTIONAL );