diff --git a/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AspectModelAASGenerator.java b/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AspectModelAASGenerator.java index 2a48ff671..499c6cff1 100644 --- a/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AspectModelAASGenerator.java +++ b/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/AspectModelAASGenerator.java @@ -20,6 +20,9 @@ import java.util.function.Function; import java.util.stream.Collectors; +import org.eclipse.esmf.metamodel.Aspect; + +import com.fasterxml.jackson.databind.JsonNode; import org.eclipse.digitaltwin.aas4j.v3.dataformat.SerializationException; import org.eclipse.digitaltwin.aas4j.v3.dataformat.Serializer; import org.eclipse.digitaltwin.aas4j.v3.dataformat.aasx.AASXSerializer; @@ -29,9 +32,6 @@ import org.eclipse.digitaltwin.aas4j.v3.model.Submodel; import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultEnvironment; import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodel; -import org.eclipse.esmf.metamodel.Aspect; - -import com.fasterxml.jackson.databind.JsonNode; /** * Generator that generates an AASX file containing an AAS submodel for a given Aspect model @@ -39,7 +39,7 @@ public class AspectModelAASGenerator { /** - * Generates an AASX archive file for a given Aspect and writes it to a given OutputStream provided by nameMapper + * Generates an AASX archive file for a given Aspect and writes it to a given OutputStream provided by {@code nameMapper} * * @param aspect the Aspect for which an AASX archive shall be generated * @param nameMapper a Name Mapper implementation, which provides an OutputStream for a given filename @@ -53,7 +53,7 @@ public void generateAASXFile( final Aspect aspect, final FunctionnameMapper + * Generates an AAS XML archive file for a given Aspect and writes it to a given OutputStream provided by {@code nameMapper} * * @param aspect the Aspect for which an xml file shall be generated * @param nameMapper a Name Mapper implementation, which provides an OutputStream for a given filename @@ -74,7 +74,7 @@ public void generateAasXmlFile( } /** - * Generates an AAS JSON file for a given Aspect and writes it to a given OutputStream provided by nameMapper + * Generates an AAS JSON file for a given Aspect and writes it to a given OutputStream provided by {@code nameMapper} * * @param aspect the Aspect for which an JSON shall be generated * @param nameMapper a Name Mapper implementation, which provides an OutputStream for a given filename @@ -94,7 +94,8 @@ protected ByteArrayOutputStream generateXmlOutput( final Map a aspectsWithData.entrySet().stream() .map( aspectWithData -> { final Submodel submodel = new DefaultSubmodel.Builder().build(); - final Environment environment = new DefaultEnvironment.Builder().submodels( Collections.singletonList( submodel ) ).build(); + final Environment environment = new DefaultEnvironment.Builder().submodels( Collections.singletonList( submodel ) ) + .build(); final Context context = new Context( environment, submodel ); context.setEnvironment( environment ); context.setAspectData( aspectWithData.getValue() ); @@ -113,9 +114,9 @@ protected ByteArrayOutputStream generateXmlOutput( final Map a } private Environment mergeEnvironments( final Map aspectEnvironments ) { - final Submodel submodel = new DefaultSubmodel.Builder().build(); return new DefaultEnvironment.Builder() - .assetAdministrationShells( aspectEnvironments.values().stream().flatMap( e -> e.getAssetAdministrationShells().stream() ).toList() ) + .assetAdministrationShells( + aspectEnvironments.values().stream().flatMap( e -> e.getAssetAdministrationShells().stream() ).toList() ) .submodels( aspectEnvironments.values().stream().flatMap( e -> e.getSubmodels().stream() ).toList() ) .conceptDescriptions( aspectEnvironments.values().stream().flatMap( e -> e.getConceptDescriptions().stream() ).toList() ) .build(); @@ -138,11 +139,11 @@ protected ByteArrayOutputStream generateXmlOutput( final Aspect aspect ) throws return generate( new XmlSerializer(), aspect ); } - protected ByteArrayOutputStream generateJsonOutput( Aspect aspect ) throws IOException { + protected ByteArrayOutputStream generateJsonOutput( final Aspect aspect ) throws IOException { return generate( new JsonSerializer(), aspect ); } - protected ByteArrayOutputStream generate( Serializer serializer, Aspect aspect ) throws IOException { + protected ByteArrayOutputStream generate( final Serializer serializer, final Aspect aspect ) throws IOException { final AspectModelAASVisitor visitor = new AspectModelAASVisitor(); final Environment environment = visitor.visitAspect( aspect, null ); 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 a363bd9bf..781ae2168 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 @@ -23,6 +23,37 @@ import java.util.stream.Collectors; import java.util.stream.StreamSupport; +import org.eclipse.esmf.aspectmodel.urn.AspectModelUrn; +import org.eclipse.esmf.aspectmodel.vocabulary.SAMM; +import org.eclipse.esmf.characteristic.Code; +import org.eclipse.esmf.characteristic.Collection; +import org.eclipse.esmf.characteristic.Duration; +import org.eclipse.esmf.characteristic.Either; +import org.eclipse.esmf.characteristic.Enumeration; +import org.eclipse.esmf.characteristic.Measurement; +import org.eclipse.esmf.characteristic.Quantifiable; +import org.eclipse.esmf.characteristic.SingleEntity; +import org.eclipse.esmf.characteristic.SortedSet; +import org.eclipse.esmf.characteristic.State; +import org.eclipse.esmf.characteristic.StructuredValue; +import org.eclipse.esmf.characteristic.Trait; +import org.eclipse.esmf.metamodel.Aspect; +import org.eclipse.esmf.metamodel.Characteristic; +import org.eclipse.esmf.metamodel.CollectionValue; +import org.eclipse.esmf.metamodel.Entity; +import org.eclipse.esmf.metamodel.EntityInstance; +import org.eclipse.esmf.metamodel.ModelElement; +import org.eclipse.esmf.metamodel.Property; +import org.eclipse.esmf.metamodel.Scalar; +import org.eclipse.esmf.metamodel.ScalarValue; +import org.eclipse.esmf.metamodel.Type; +import org.eclipse.esmf.metamodel.loader.MetaModelBaseAttributes; +import org.eclipse.esmf.metamodel.visitor.AspectVisitor; +import org.eclipse.esmf.samm.KnownVersion; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.google.common.collect.ImmutableMap; import org.apache.jena.rdf.model.Resource; import org.apache.jena.rdf.model.ResourceFactory; import org.apache.jena.vocabulary.RDF; @@ -67,45 +98,18 @@ import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodelElementList; import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultValueList; import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultValueReferencePair; -import org.eclipse.esmf.aspectmodel.urn.AspectModelUrn; -import org.eclipse.esmf.aspectmodel.vocabulary.SAMM; -import org.eclipse.esmf.characteristic.Code; -import org.eclipse.esmf.characteristic.Collection; -import org.eclipse.esmf.characteristic.Duration; -import org.eclipse.esmf.characteristic.Either; -import org.eclipse.esmf.characteristic.Enumeration; -import org.eclipse.esmf.characteristic.Measurement; -import org.eclipse.esmf.characteristic.Quantifiable; -import org.eclipse.esmf.characteristic.SingleEntity; -import org.eclipse.esmf.characteristic.SortedSet; -import org.eclipse.esmf.characteristic.State; -import org.eclipse.esmf.characteristic.StructuredValue; -import org.eclipse.esmf.characteristic.Trait; -import org.eclipse.esmf.metamodel.Aspect; -import org.eclipse.esmf.metamodel.Characteristic; -import org.eclipse.esmf.metamodel.Entity; -import org.eclipse.esmf.metamodel.ModelElement; -import org.eclipse.esmf.metamodel.Property; -import org.eclipse.esmf.metamodel.Scalar; -import org.eclipse.esmf.metamodel.Type; -import org.eclipse.esmf.metamodel.loader.MetaModelBaseAttributes; -import org.eclipse.esmf.metamodel.visitor.AspectVisitor; -import org.eclipse.esmf.samm.KnownVersion; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.google.common.collect.ImmutableMap; - public class AspectModelAASVisitor implements AspectVisitor { - private static final Logger LOG = LoggerFactory.getLogger( AspectModelAASVisitor.class ); + private static final ValueSerializer VALUE_SERIALIZER = new ValueSerializer(); public static final String ADMIN_SHELL_NAME = "defaultAdminShell"; - public static final String DEFAULT_LOCALE = "EN"; + public static final String DEFAULT_LOCALE = "en"; public static final String CONCEPT_DESCRIPTION_CATEGORY = "APPLICATION_CLASS"; - public static final String ID_PREFIX = "id_"; + public static final String CONCEPT_DESCRIPTION_DATA_SPECIFICATION_URL = + "https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIec61360/3/0"; /** * Maps Aspect types to DataTypeIEC61360 Schema types, with no explicit mapping defaulting to @@ -177,9 +181,11 @@ public Environment visitAspect( final Aspect aspect, final Context context ) { usedContext.setEnvironment( environment ); } + final String submodelId = aspect.getAspectModelUrn().get().getUrn().toString() + "/submodel"; + final Submodel submodel = usedContext.getSubmodel(); submodel.setIdShort( aspect.getName() ); - submodel.setId( aspect.getAspectModelUrn().toString() + "/submodel" ); + submodel.setId( submodelId ); submodel.setSemanticID( buildReferenceToConceptDescription( aspect ) ); submodel.setDescription( LangStringMapper.TEXT.map( aspect.getDescriptions() ) ); submodel.setKind( usedContext.getModelingKind() ); @@ -190,13 +196,13 @@ public Environment visitAspect( final Aspect aspect, final Context context ) { final AssetAdministrationShell administrationShell = new DefaultAssetAdministrationShell.Builder() .id( DEFAULT_MAPPER.determineIdentifierFor( aspect ) ) - .idShort( ID_PREFIX + ADMIN_SHELL_NAME ) + .idShort( ADMIN_SHELL_NAME ) .description( LangStringMapper.TEXT.createLangString( ADMIN_SHELL_NAME, "en" ) ) .administration( new DefaultAdministrativeInformation.Builder().build() ) .assetInformation( new DefaultAssetInformation.Builder() .assetKind( usedContext.getAssetKind() ) .build() ) - .embeddedDataSpecifications( extractEmbeddedDataSpecification( aspect ) ) + .submodels( buildReferenceForSubmodel( submodelId ) ) .build(); usedContext.getEnvironment() .setAssetAdministrationShells( Collections.singletonList( administrationShell ) ); @@ -235,7 +241,8 @@ private Optional mapText( final Property property, final Contex return defaultResultForProperty; } else { LOG.error( String.format( - "Having a recursive property: %s which is not optional is not valid. Check the model. Property will be excluded from AAS mapping.", + "Having a recursive property: %s which is not optional is not valid. Check the model. Property will be excluded from " + + "AAS mapping.", property.getAspectModelUrn().map( AspectModelUrn::toString ).orElse( "(unknown)" ) ) ); } return defaultResultForProperty; @@ -270,17 +277,17 @@ private SubmodelElement decideOnMapping( final Property property, final Context private SubmodelElement decideOnMapping( final Type type, final Property property, final Context context ) { if ( type instanceof Entity ) { - return mapToAasSubModelElementCollection( (Entity) type, property.getName(), context ); + return mapToAasSubModelElementCollection( (Entity) type, context ); } else { return findPropertyMapper( property ).mapToAasProperty( type, property, context ); } } - private SubmodelElementCollection mapToAasSubModelElementCollection( final Entity entity, final String name, final Context context ) { + private SubmodelElementCollection mapToAasSubModelElementCollection( final Entity entity, final Context context ) { final List submodelElements = visitProperties( entity.getAllProperties(), context ); return new DefaultSubmodelElementCollection.Builder() - .idShort( ID_PREFIX + name ) + .idShort( entity.getName() ) .displayName( LangStringMapper.NAME.map( entity.getPreferredNames() ) ) .description( LangStringMapper.TEXT.map( entity.getDescriptions() ) ) .value( submodelElements ) @@ -291,15 +298,13 @@ private Operation mapText( final org.eclipse.esmf.metamodel.Operation operation, return new DefaultOperation.Builder() .displayName( LangStringMapper.NAME.map( operation.getPreferredNames() ) ) .description( LangStringMapper.TEXT.map( operation.getDescriptions() ) ) - .idShort( ID_PREFIX + operation.getName() ) - .inputVariables( - operation.getInput().stream() - .map( i -> mapOperationVariable( i, context ) ) - .collect( Collectors.toList() ) ) - .outputVariables( - operation.getOutput().stream() - .map( i -> mapOperationVariable( i, context ) ) - .collect( Collectors.toList() ) ) + .idShort( operation.getName() ) + .inputVariables( operation.getInput().stream() + .map( i -> mapOperationVariable( i, context ) ) + .collect( Collectors.toList() ) ) + .outputVariables( operation.getOutput().stream() + .map( i -> mapOperationVariable( i, context ) ) + .collect( Collectors.toList() ) ) .build(); } @@ -307,36 +312,44 @@ private OperationVariable mapOperationVariable( final Property property, final C return new DefaultOperationVariable.Builder().value( mapText( property, context ).orElseThrow() ).build(); } - private Reference buildReferenceToEnumValue( final Enumeration enumeration, final Object value ) { - final Key key = - new DefaultKey.Builder() - .type( KeyTypes.DATA_ELEMENT ) - .value( DEFAULT_MAPPER.determineIdentifierFor( enumeration ) + ":" + value.toString() ) - .build(); + private Reference buildReferenceToEnumValue( final Enumeration enumeration, final String value ) { + final Key key = new DefaultKey.Builder() + .type( KeyTypes.DATA_ELEMENT ) + .value( DEFAULT_MAPPER.determineIdentifierFor( enumeration ) + ":" + value ) + .build(); return new DefaultReference.Builder().type( ReferenceTypes.MODEL_REFERENCE ).keys( key ).build(); } private Reference buildReferenceToConceptDescription( final Aspect aspect ) { - final Key key = - new DefaultKey.Builder() - .type( KeyTypes.CONCEPT_DESCRIPTION ) - .value( DEFAULT_MAPPER.determineIdentifierFor( aspect ) ) - .build(); + final Key key = new DefaultKey.Builder() + .type( KeyTypes.CONCEPT_DESCRIPTION ) + .value( DEFAULT_MAPPER.determineIdentifierFor( aspect ) ) + .build(); return new DefaultReference.Builder().type( ReferenceTypes.MODEL_REFERENCE ).keys( key ).build(); } private Reference buildReferenceForSeeElement( final String seeReference ) { - final Key key = - new DefaultKey.Builder() - .type( KeyTypes.GLOBAL_REFERENCE ) - .value( seeReference ) - .build(); + final Key key = new DefaultKey.Builder() + .type( KeyTypes.GLOBAL_REFERENCE ) + .value( seeReference ) + .build(); return new DefaultReference.Builder() .type( ReferenceTypes.EXTERNAL_REFERENCE ) .keys( key ) .build(); } + private Reference buildReferenceForSubmodel( final String submodelId ) { + final Key key = new DefaultKey.Builder() + .type( KeyTypes.SUBMODEL ) + .value( submodelId ) + .build(); + return new DefaultReference.Builder() + .type( ReferenceTypes.MODEL_REFERENCE ) + .keys( key ) + .build(); + } + private List buildReferencesForSeeElements( final List seeReferences ) { return seeReferences.stream().map( this::buildReferenceForSeeElement ).collect( Collectors.toList() ); } @@ -350,7 +363,7 @@ private void createConceptDescription( final Property property, final Context co if ( !context.hasEnvironmentConceptDescription( property.getAspectModelUrn().toString() ) ) { final ConceptDescription conceptDescription = new DefaultConceptDescription.Builder() - .idShort( ID_PREFIX + characteristic.getName() ) + .idShort( characteristic.getName() ) .displayName( LangStringMapper.NAME.map( characteristic.getPreferredNames() ) ) .embeddedDataSpecifications( extractEmbeddedDataSpecification( property ) ) .id( DEFAULT_MAPPER.determineIdentifierFor( property ) ) @@ -364,7 +377,7 @@ private void createConceptDescription( final Aspect aspect, final Context contex if ( !context.hasEnvironmentConceptDescription( aspect.getAspectModelUrn().toString() ) ) { final ConceptDescription conceptDescription = new DefaultConceptDescription.Builder() - .idShort( ID_PREFIX + aspect.getName() ) + .idShort( aspect.getName() ) .displayName( LangStringMapper.NAME.map( aspect.getPreferredNames() ) ) .embeddedDataSpecifications( extractEmbeddedDataSpecification( aspect ) ) .id( DEFAULT_MAPPER.determineIdentifierFor( aspect ) ) @@ -377,14 +390,14 @@ private void createConceptDescription( final Aspect aspect, final Context contex private EmbeddedDataSpecification extractEmbeddedDataSpecification( final Property property ) { return new DefaultEmbeddedDataSpecification.Builder() - .dataSpecification( buildReferenceForSeeElement( property.getAspectModelUrn().toString() ) ) + .dataSpecification( buildReferenceForSeeElement( CONCEPT_DESCRIPTION_DATA_SPECIFICATION_URL ) ) .dataSpecificationContent( extractDataSpecificationContent( property ) ) .build(); } private EmbeddedDataSpecification extractEmbeddedDataSpecification( final Aspect aspect ) { return new DefaultEmbeddedDataSpecification.Builder() - .dataSpecification( buildReferenceForSeeElement( aspect.getAspectModelUrn().toString() ) ) + .dataSpecification( buildReferenceForSeeElement( CONCEPT_DESCRIPTION_DATA_SPECIFICATION_URL ) ) .dataSpecificationContent( extractDataSpecificationContent( aspect ) ) .build(); } @@ -418,11 +431,11 @@ private DataSpecificationIec61360 extractDataSpecificationContent( final Aspect .preferredName( preferredNames ) .shortName( LangStringMapper.SHORT_NAME.createLangString( aspect.getName(), DEFAULT_LOCALE ) ) .build(); - } private DataTypeIec61360 mapIEC61360DataType( final Optional characteristic ) { - return mapIEC61360DataType( characteristic.flatMap( Characteristic::getDataType ).map( Type::getUrn ).orElse( RDF.langString.getURI() ) ); + return mapIEC61360DataType( + characteristic.flatMap( Characteristic::getDataType ).map( Type::getUrn ).orElse( RDF.langString.getURI() ) ); } private DataTypeIec61360 mapIEC61360DataType( final Characteristic characteristic ) { @@ -442,27 +455,23 @@ private void createSubmodelElement( final SubmodelElementBuilder op, final Conte } @Override - public Environment visitCharacteristic( - final Characteristic characteristic, final Context context ) { + public Environment visitCharacteristic( final Characteristic characteristic, final Context context ) { createSubmodelElement( ( property ) -> decideOnMapping( property, context ), context ); return context.getEnvironment(); } @Override - public Environment visitCollection( - final Collection collection, final Context context ) { + public Environment visitCollection( final Collection collection, final Context context ) { return visitCollectionProperty( collection, context ); } @Override - public Environment visitList( - final org.eclipse.esmf.characteristic.List list, final Context context ) { + public Environment visitList( final org.eclipse.esmf.characteristic.List list, final Context context ) { return visitCollectionProperty( list, context ); } @Override - public Environment visitSet( - final org.eclipse.esmf.characteristic.Set set, final Context context ) { + public Environment visitSet( final org.eclipse.esmf.characteristic.Set set, final Context context ) { return visitCollectionProperty( set, context ); // this type is not available in AAS4J } @@ -475,28 +484,26 @@ public Environment visitSortedSet( private Environment visitCollectionProperty( final T collection, final Context context ) { - final SubmodelElementBuilder builder = - ( property ) -> - new DefaultSubmodelElementList.Builder() - .idShort( ID_PREFIX + property.getName() ) - .typeValueListElement( AASSubmodelElements.DATA_ELEMENT ) - .displayName( LangStringMapper.NAME.map( property.getPreferredNames() ) ) - .description( LangStringMapper.TEXT.map( property.getDescriptions() ) ) - .value( List.of( decideOnMapping( property, context ) ) ) - .build(); + final SubmodelElementBuilder builder = property -> + new DefaultSubmodelElementList.Builder() + .idShort( property.getName() ) + .typeValueListElement( AASSubmodelElements.DATA_ELEMENT ) + .displayName( LangStringMapper.NAME.map( property.getPreferredNames() ) ) + .description( LangStringMapper.TEXT.map( property.getDescriptions() ) ) + .value( List.of( decideOnMapping( property, context ) ) ) + .build(); final Optional rawValue = context.getRawPropertyValue(); return rawValue.map( node -> { if ( node instanceof final ArrayNode arrayNode ) { - final SubmodelElementBuilder listBuilder = - ( property ) -> { - final var 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 ) - .build(); - }; + final SubmodelElementBuilder listBuilder = property -> { + final List 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 ) + .build(); + }; createSubmodelElement( listBuilder, context ); return context.getEnvironment(); } @@ -518,7 +525,7 @@ private List getValues( final T collecti .collect( Collectors.joining( "," ) ) .getBytes( StandardCharsets.UTF_8 ) ).build() ); } else { - final var values = StreamSupport.stream( arrayNode.spliterator(), false ) + final List values = StreamSupport.stream( arrayNode.spliterator(), false ) .map( n -> { context.iterate( property ); return decideOnMapping( property, context ); @@ -535,10 +542,9 @@ private List getValues( final T collecti // in the result and have to be manually selected. // When generating Submodels where data is given however only the present side is added. @Override - public Environment visitEither( - final Either either, final Context context ) { + public Environment visitEither( final Either either, final Context context ) { final List submodelElements = new ArrayList<>(); - final var property = context.getProperty(); + final Property property = context.getProperty(); either.getLeft().getDataType() .flatMap( dataType -> handleEitherField( "left", either.getLeft(), property, context ) ) .ifPresent( submodelElements::add ); @@ -548,7 +554,7 @@ public Environment visitEither( final SubmodelElementList eitherSubModelElements = new DefaultSubmodelElementList.Builder() - .idShort( ID_PREFIX + either.getName() ) + .idShort( either.getName() ) .typeValueListElement( AASSubmodelElements.DATA_ELEMENT ) .displayName( LangStringMapper.NAME.map( either.getPreferredNames() ) ) .description( LangStringMapper.TEXT.map( either.getDescriptions() ) ) @@ -560,7 +566,7 @@ public Environment visitEither( /** * Handles one {@code Either} field depending on whether a submodel template or a submodel is generated. - * + *

* In the latter case, a synthetic property is used to access the serialized data and retrieve the value to be added. * * @param field the name of the {@code Either} field ({@code left} or {@code right}) @@ -573,7 +579,7 @@ private Optional handleEitherField( final String field, final C final Property eitherProperty, final Context context ) { Optional result = Optional.empty(); if ( context.getModelingKind().equals( ModellingKind.INSTANCE ) ) { - final var fieldProperty = createProperty( eitherProperty.getMetaModelVersion(), field, fieldCharacteristic ); + final Property fieldProperty = createProperty( eitherProperty.getMetaModelVersion(), field, fieldCharacteristic ); context.setProperty( fieldProperty ); if ( context.getRawPropertyValue().isPresent() ) { result = fieldCharacteristic.getDataType().map( dataType -> decideOnMapping( dataType, context.getProperty(), context ) ); @@ -588,14 +594,15 @@ private Optional handleEitherField( final String field, final C private Property createProperty( final KnownVersion modelVersion, final String propertyName, final Characteristic characteristic ) { final MetaModelBaseAttributes propertyAttributes = - MetaModelBaseAttributes.from( modelVersion, AspectModelUrn.fromUrn( new SAMM( modelVersion ).Property().getURI() ), propertyName ); - return new org.eclipse.esmf.metamodel.impl.DefaultProperty( propertyAttributes, Optional.of( characteristic ), Optional.empty(), true, false, + MetaModelBaseAttributes.from( modelVersion, AspectModelUrn.fromUrn( new SAMM( modelVersion ).Property().getURI() ), + propertyName ); + return new org.eclipse.esmf.metamodel.impl.DefaultProperty( propertyAttributes, Optional.of( characteristic ), Optional.empty(), true, + false, Optional.empty(), false, Optional.empty() ); } @Override - public Environment visitQuantifiable( - final Quantifiable quantifiable, final Context context ) { + public Environment visitQuantifiable( final Quantifiable quantifiable, final Context context ) { createSubmodelElement( ( property ) -> decideOnMapping( property, context ), context ); if ( quantifiable.getUnit().isPresent() ) { @@ -613,22 +620,19 @@ public Environment visitQuantifiable( } @Override - public Environment visitMeasurement( - final Measurement measurement, final Context context ) { + public Environment visitMeasurement( final Measurement measurement, final Context context ) { // No special handling required can use Quantifiable mapping implementation return visitQuantifiable( measurement, context ); } @Override - public Environment visitDuration( - final Duration duration, final Context context ) { + public Environment visitDuration( final Duration duration, final Context context ) { // No special handling required can use Quantifiable mapping implementation return visitQuantifiable( duration, context ); } @Override - public Environment visitEnumeration( - final Enumeration enumeration, final Context context ) { + public Environment visitEnumeration( final Enumeration enumeration, final Context context ) { createSubmodelElement( ( property ) -> decideOnMapping( property, context ), context ); final ConceptDescription conceptDescription = @@ -641,17 +645,16 @@ public Environment visitEnumeration( dataSpecificationContent.setDataType( mapIEC61360DataType( enumeration ) ); final List valueReferencePairs = enumeration.getValues().stream() - .map( - x -> - new DefaultValueReferencePair.Builder() - .value( x.toString() ) - .valueID( buildReferenceToEnumValue( enumeration, x ) ) - .build() ) + .map( enumerationValue -> { + final String value = enumerationValue.accept( VALUE_SERIALIZER, enumeration ); + return new DefaultValueReferencePair.Builder() + .value( value ) + .valueID( buildReferenceToEnumValue( enumeration, value ) ) + .build(); + } ) .collect( Collectors.toList() ); - final ValueList valueList = - new DefaultValueList.Builder().valueReferencePairs( valueReferencePairs ).build(); - + final ValueList valueList = new DefaultValueList.Builder().valueReferencePairs( valueReferencePairs ).build(); dataSpecificationContent.setValueList( valueList ); } @@ -665,16 +668,14 @@ public Environment visitState( final State state, final Context context ) { } @Override - public Environment visitSingleEntity( - final SingleEntity singleEntity, final Context context ) { + public Environment visitSingleEntity( final SingleEntity singleEntity, final Context context ) { // Same handling as characteristics return visitCharacteristic( singleEntity, context ); } @Override - public Environment visitStructuredValue( - final StructuredValue structuredValue, final Context context ) { - // https://openmanufacturingplatform.github.io/sds-documentation/bamm-specification/v1.0.0/modeling-guidelines.html#declaring-structured-value + public Environment visitStructuredValue( final StructuredValue structuredValue, final Context context ) { + // https://eclipse-esmf.github.io/samm-specification/snapshot/characteristics.html#structured-value-characteristic // AAS cannot handle structuredValues, so we can handle them as ordinary Characteristics return visitCharacteristic( structuredValue, context ); } @@ -692,4 +693,32 @@ public Environment visitTrait( final Trait trait, final Context context ) { // ignored and have to be deduced by resolving a SAMM model referenced by its semanticID return visitCharacteristic( trait.getBaseCharacteristic(), context ); } + + public static class ValueSerializer implements AspectVisitor { + @Override + public String visitBase( final ModelElement modelElement, final ModelElement context ) { + throw new UnsupportedOperationException(); + } + + @Override + public String visitScalarValue( final ScalarValue value, final ModelElement context ) { + return context.is( Characteristic.class ) + ? value.getValue().toString() + : "\"" + value.getValue().toString() + "\""; + } + + @Override + public String visitEntityInstance( final EntityInstance instance, final ModelElement context ) { + return instance.getAssertions().entrySet().stream().map( entry -> + String.format( "\"%s\":%s", entry.getKey().getName(), entry.getValue().accept( this, instance ) ) ) + .collect( Collectors.joining( ",", "{", "}" ) ); + } + + @Override + public String visitCollectionValue( final CollectionValue value, final ModelElement context ) { + return value.getValues().stream().map( collectionValue -> + collectionValue.accept( this, value ) ) + .collect( Collectors.joining( ",", "[", "]" ) ); + } + } } diff --git a/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/Context.java b/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/Context.java index f661415e2..718b7451a 100644 --- a/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/Context.java +++ b/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/Context.java @@ -166,7 +166,7 @@ public String getPropertyShortId() { .collect( Collectors.joining( "_" ) ); } - return "id_" + shortId; + return shortId; } /** diff --git a/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/PropertyMapper.java b/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/PropertyMapper.java index 546097bc3..b5b02f57b 100644 --- a/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/PropertyMapper.java +++ b/core/esmf-aspect-model-aas-generator/src/main/java/org/eclipse/esmf/aspectmodel/aas/PropertyMapper.java @@ -40,7 +40,7 @@ public interface PropertyMapper { static String UNKNOWN_TYPE = "Unknown"; - static String UNKNOWN_EXAMPLE = UNKNOWN_TYPE; + static String UNKNOWN_EXAMPLE = ""; /** * Maps Aspect types to DataTypeDefXSD Schema types, with no explicit mapping defaulting to 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 a5d838384..9dd6db755 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 @@ -13,17 +13,18 @@ package org.eclipse.esmf.aspectmodel.aas; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.InstanceOfAssertFactories.type; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; import javax.xml.XMLConstants; import javax.xml.transform.stream.StreamSource; @@ -31,7 +32,14 @@ import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; -import org.assertj.core.api.InstanceOfAssertFactories; +import org.eclipse.esmf.aspectmodel.resolver.services.VersionedModel; +import org.eclipse.esmf.metamodel.Aspect; +import org.eclipse.esmf.metamodel.loader.AspectModelLoader; +import org.eclipse.esmf.samm.KnownVersion; +import org.eclipse.esmf.test.TestAspect; +import org.eclipse.esmf.test.TestResources; + +import com.fasterxml.jackson.databind.JsonNode; import org.eclipse.digitaltwin.aas4j.v3.dataformat.DeserializationException; import org.eclipse.digitaltwin.aas4j.v3.dataformat.xml.XmlDeserializer; import org.eclipse.digitaltwin.aas4j.v3.model.AbstractLangString; @@ -46,19 +54,10 @@ import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementCollection; import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementList; -import org.eclipse.esmf.aspectmodel.resolver.services.VersionedModel; -import org.eclipse.esmf.metamodel.Aspect; -import org.eclipse.esmf.metamodel.loader.AspectModelLoader; -import org.eclipse.esmf.samm.KnownVersion; -import org.eclipse.esmf.test.TestAspect; -import org.eclipse.esmf.test.TestResources; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.xml.sax.SAXException; -import org.xml.sax.SAXParseException; - -import com.fasterxml.jackson.databind.JsonNode; class AspectModelAASGeneratorTest { @@ -77,34 +76,34 @@ void generateAasxWithAspectDataForMultilanguageText() throws IOException, Deseri assertThat( subModel.getSubmodelElements() ) .singleElement() .satisfies( property -> { - assertThat( property ).asInstanceOf( InstanceOfAssertFactories.type( MultiLanguageProperty.class ) ) - .extracting( mlp -> mlp.getValue() ) + assertThat( property ).asInstanceOf( type( MultiLanguageProperty.class ) ) + .extracting( MultiLanguageProperty::getValue ) .asList() .hasSize( 2 ) .allSatisfy( langString -> { - List.of( "en", "de" ).contains( ((AbstractLangString) langString).getLanguage() ); + assertThat( List.of( "en", "de" ) ).contains( ((AbstractLangString) langString).getLanguage() ); } ); } ); } ); } @Test - void generateAasxWithAspectDataForEitherWithEntity() throws IOException, DeserializationException, SAXException { + void generateAasxWithAspectDataForEitherWithEntity() throws IOException, DeserializationException { final Environment env = getAssetAdministrationShellFromAspectWithData( TestAspect.ASPECT_WITH_EITHER_WITH_COMPLEX_TYPES ); assertThat( env.getSubmodels() ) .singleElement() .satisfies( subModel -> { assertThat( subModel.getSubmodelElements() ) .anySatisfy( sme -> { - assertThat( sme ).asInstanceOf( InstanceOfAssertFactories.type( SubmodelElementList.class ) ) - .extracting( smel -> smel.getValue() ) + assertThat( sme ).asInstanceOf( type( SubmodelElementList.class ) ) + .extracting( SubmodelElementList::getValue ) .asList() .anySatisfy( entity -> { - assertThat( entity ).asInstanceOf( InstanceOfAssertFactories.type( SubmodelElementCollection.class ) ) - .extracting( smec -> smec.getValue() ) + assertThat( entity ).asInstanceOf( type( SubmodelElementCollection.class ) ) + .extracting( SubmodelElementCollection::getValue ) .asList() - .singleElement( InstanceOfAssertFactories.type( Property.class ) ) - .extracting( entityProperty -> entityProperty.getValue() ) + .singleElement( type( Property.class ) ) + .extracting( Property::getValue ) .isEqualTo( "The result" ); } ); } ); @@ -112,22 +111,22 @@ void generateAasxWithAspectDataForEitherWithEntity() throws IOException, Deseria } @Test - void generateAasxWithAspectDataForNestedEntityLists() throws IOException, DeserializationException, SAXException { + void generateAasxWithAspectDataForNestedEntityLists() throws IOException, DeserializationException { final Environment env = getAssetAdministrationShellFromAspectWithData( TestAspect.ASPECT_WITH_NESTED_ENTITY_LIST ); assertThat( env.getSubmodels() ) .singleElement() .satisfies( subModel -> { assertThat( subModel.getSubmodelElements() ) .anySatisfy( sme -> { - assertThat( sme ).asInstanceOf( InstanceOfAssertFactories.type( SubmodelElementList.class ) ) - .extracting( smel -> smel.getValue() ) + assertThat( sme ).asInstanceOf( type( SubmodelElementList.class ) ) + .extracting( SubmodelElementList::getValue ) .asList() .anySatisfy( entity -> { - assertThat( entity ).asInstanceOf( InstanceOfAssertFactories.type( SubmodelElementCollection.class ) ) - .extracting( smec -> smec.getValue() ) + assertThat( entity ).asInstanceOf( type( SubmodelElementCollection.class ) ) + .extracting( SubmodelElementCollection::getValue ) .asList() .anySatisfy( property -> { - assertThat( property ).asInstanceOf( InstanceOfAssertFactories.type( Property.class ) ) + assertThat( property ).asInstanceOf( type( Property.class ) ) .extracting( Property::getValue ) .isEqualTo( "2.25" ); } ); @@ -137,7 +136,7 @@ void generateAasxWithAspectDataForNestedEntityLists() throws IOException, Deseri } @Test - void testGenerateAasxFromBammAspectWithListAndAdditionalProperty() throws IOException, DeserializationException { + void testGenerateAasxFromAspectModelWithListAndAdditionalProperty() throws IOException, DeserializationException { final Environment env = getAssetAdministrationShellFromAspect( TestAspect.ASPECT_WITH_LIST_AND_ADDITIONAL_PROPERTY ); assertEquals( 3, env.getConceptDescriptions().size() ); assertEquals( 1, env.getSubmodels().size() ); @@ -151,7 +150,7 @@ void testGenerateAasxFromBammAspectWithListAndAdditionalProperty() throws IOExce } @Test - void testGenerateAasxFromBammAspectWithEntity() throws IOException, DeserializationException { + void testGenerateAasxFromAspectModelWithEntity() throws IOException, DeserializationException { final Environment env = getAssetAdministrationShellFromAspect( TestAspect.ASPECT_WITH_ENTITY ); assertEquals( 1, env.getSubmodels().size(), "Not exactly one Submodel in AAS." ); assertEquals( 1, env.getSubmodels().get( 0 ).getSubmodelElements().size(), "Not exactly one SubmodelElement in Submodel." ); @@ -159,67 +158,68 @@ void testGenerateAasxFromBammAspectWithEntity() throws IOException, Deserializat "SubmodelElement is not a SubmodelElementCollection." ); final SubmodelElementCollection collection = (SubmodelElementCollection) env.getSubmodels().get( 0 ).getSubmodelElements().get( 0 ); assertEquals( 1, collection.getValue().size(), "Not exactly one Element in SubmodelElementCollection" ); - assertEquals( "id_entityProperty", collection.getValue().stream().findFirst().get().getIdShort() ); + assertEquals( "entityProperty", collection.getValue().stream().findFirst().get().getIdShort() ); getDataSpecificationIEC61360( "urn:samm:org.eclipse.esmf.test:1.0.0#testProperty", env ); } @Test - void testGenerateAasxFromBammAspectWithCollection() throws IOException, DeserializationException { + void testGenerateAasxFromAspectModelWithCollection() throws IOException, DeserializationException { final Environment env = getAssetAdministrationShellFromAspect( TestAspect.ASPECT_WITH_COLLECTION ); assertEquals( 1, env.getSubmodels().size(), "Not exactly one Submodel in AAS." ); assertEquals( 1, env.getSubmodels().get( 0 ).getSubmodelElements().size(), "Not exactly one SubmodelElement in AAS." ); final SubmodelElement submodelElement = env.getSubmodels().get( 0 ).getSubmodelElements().get( 0 ); assertTrue( submodelElement instanceof SubmodelElementList, "SubmodelElement is not a SubmodelElementList" ); - assertEquals( "id_testProperty", submodelElement.getIdShort() ); + assertEquals( "testProperty", submodelElement.getIdShort() ); getDataSpecificationIEC61360( "urn:samm:org.eclipse.esmf.test:1.0.0#testProperty", env ); } @Test - void testGenerateAasxFromBammAspectWithList() throws IOException, DeserializationException { + void testGenerateAasxFromAspectModelWithList() throws IOException, DeserializationException { final Environment env = getAssetAdministrationShellFromAspect( TestAspect.ASPECT_WITH_LIST ); assertEquals( 1, env.getSubmodels().size(), "Not exactly one Submodel in AAS." ); assertEquals( 1, env.getSubmodels().get( 0 ).getSubmodelElements().size(), "Not exactly one SubmodelElement in AAS." ); final SubmodelElement submodelElement = env.getSubmodels().get( 0 ).getSubmodelElements().get( 0 ); assertTrue( submodelElement instanceof SubmodelElementList, "SubmodelElement is not a SubmodelElementList" ); - assertEquals( "id_testProperty", submodelElement.getIdShort() ); + assertEquals( "testProperty", submodelElement.getIdShort() ); getDataSpecificationIEC61360( "urn:samm:org.eclipse.esmf.test:1.0.0#testProperty", env ); } @Test - void testGenerateAasxFromBammAspectWithSet() throws IOException, DeserializationException { + void testGenerateAasxFromAspectModelWithSet() throws IOException, DeserializationException { final Environment env = getAssetAdministrationShellFromAspect( TestAspect.ASPECT_WITH_SET ); assertEquals( 1, env.getSubmodels().size(), "Not exactly one Submodel in AAS." ); assertEquals( 1, env.getSubmodels().get( 0 ).getSubmodelElements().size(), "Not exactly one SubmodelElement in AAS." ); final SubmodelElement submodelElement = env.getSubmodels().get( 0 ).getSubmodelElements().get( 0 ); assertTrue( submodelElement instanceof SubmodelElementList, "SubmodelElement is not a SubmodelElementList" ); - assertEquals( "id_testProperty", submodelElement.getIdShort() ); + assertEquals( "testProperty", submodelElement.getIdShort() ); getDataSpecificationIEC61360( "urn:samm:org.eclipse.esmf.test:1.0.0#testProperty", env ); } @Test - void testGenerateAasxFromBammAspectWithSortedSet() throws IOException, DeserializationException { + void testGenerateAasxFromAspectModelWithSortedSet() throws IOException, DeserializationException { final Environment env = getAssetAdministrationShellFromAspect( TestAspect.ASPECT_WITH_SORTED_SET ); assertEquals( 1, env.getSubmodels().size(), "Not exactly one Submodel in AAS." ); assertEquals( 1, env.getSubmodels().get( 0 ).getSubmodelElements().size(), "Not exactly one SubmodelElement in AAS." ); final SubmodelElement submodelElement = env.getSubmodels().get( 0 ).getSubmodelElements().get( 0 ); assertTrue( submodelElement instanceof SubmodelElementList, "SubmodelElement is not a SubmodelElementList" ); - assertEquals( "id_testProperty", submodelElement.getIdShort() ); + assertEquals( "testProperty", submodelElement.getIdShort() ); getDataSpecificationIEC61360( "urn:samm:org.eclipse.esmf.test:1.0.0#testProperty", env ); } @Test - void testGenerateAasxFromBammAspectWithEitherWithComplexTypes() throws IOException, DeserializationException { + void testGenerateAasxFromAspectModelWithEitherWithComplexTypes() throws IOException, DeserializationException { final Environment env = getAssetAdministrationShellFromAspect( TestAspect.ASPECT_WITH_EITHER_WITH_COMPLEX_TYPES ); assertEquals( 1, env.getSubmodels().size(), "Not exactly one Submodel in AAS." ); assertEquals( 1, env.getSubmodels().get( 0 ).getSubmodelElements().size(), 1, "Not exactly one Element in SubmodelElements." ); final SubmodelElementList elementCollection = ((SubmodelElementList) env.getSubmodels().get( 0 ).getSubmodelElements().get( 0 )); - final List testValues = List.of( "id_testProperty", "id_testProperty" ); - assertTrue( elementCollection.getValue().stream().anyMatch( x -> testValues.contains( x.getIdShort() ) ), "Neither LeftEntity (with id_testProperty) nor RightEntity (with id_testProperty) contained." ); + final Set testValues = Set.of( "RightEntity", "LeftEntity" ); + assertTrue( elementCollection.getValue().stream().anyMatch( x -> testValues.contains( x.getIdShort() ) ), + "Neither left nor right entity contained." ); final Set semanticIds = Set.of( "urn:samm:org.eclipse.esmf.test:1.0.0#result", @@ -229,14 +229,15 @@ void testGenerateAasxFromBammAspectWithEitherWithComplexTypes() throws IOExcepti } @Test - void testGenerateAasxFromBammAspectWithQuantifiable() throws IOException, DeserializationException { + void testGenerateAasxFromAspectModelWithQuantifiable() throws IOException, DeserializationException { final Environment env = getAssetAdministrationShellFromAspect( TestAspect.ASPECT_WITH_QUANTIFIABLE_WITH_UNIT ); assertEquals( 1, env.getSubmodels().size(), "Not exactly one Submodel in AAS." ); assertEquals( 1, env.getSubmodels().get( 0 ).getSubmodelElements().size(), 1, "Not exactly one Element in SubmodelElements." ); final SubmodelElement element = env.getSubmodels().get( 0 ).getSubmodelElements().get( 0 ); - assertEquals( "id_testProperty", element.getIdShort() ); + assertEquals( "testProperty", element.getIdShort() ); - final DataSpecificationContent dataSpecificationContent = getDataSpecificationIEC61360( "urn:samm:org.eclipse.esmf.test:1.0.0#testProperty", env ); + final DataSpecificationContent dataSpecificationContent = getDataSpecificationIEC61360( + "urn:samm:org.eclipse.esmf.test:1.0.0#testProperty", env ); assertEquals( "percent", ((DataSpecificationIec61360) dataSpecificationContent).getUnit(), "Unit is not percent" ); } @@ -248,10 +249,10 @@ void testGenerateAasxFromBammWithConstraint() throws IOException, Deserializatio assertEquals( 1, env.getSubmodels().get( 0 ).getSubmodelElements().size(), 6, "Not exactly six Elements in SubmodelElements." ); final SubmodelElement submodelElement = env.getSubmodels().get( 0 ).getSubmodelElements().stream() - .filter( x -> x.getIdShort().equals( "id_stringLcProperty" ) ) + .filter( x -> x.getIdShort().equals( "stringLcProperty" ) ) .findFirst() .orElseThrow(); - assertEquals( "id_stringLcProperty", submodelElement.getIdShort() ); + assertEquals( "stringLcProperty", submodelElement.getIdShort() ); final Set semanticIds = Set.of( "urn:samm:org.eclipse.esmf.test:1.0.0#stringLcProperty", @@ -265,13 +266,13 @@ void testGenerateAasxFromBammWithConstraint() throws IOException, Deserializatio } @Test - void testGenerateAasxFromBammAspectWithRecursivePropertyWithOptional() throws IOException, DeserializationException { + void testGenerateAasxFromAspectModelWithRecursivePropertyWithOptional() throws IOException, DeserializationException { final Environment env = getAssetAdministrationShellFromAspect( TestAspect.ASPECT_WITH_RECURSIVE_PROPERTY_WITH_OPTIONAL ); assertEquals( 1, env.getSubmodels().size(), "Not exactly one Submodel in AAS." ); } @Test - void testGenerateAasxFromBammAspectWithCode() throws IOException, DeserializationException { + void testGenerateAasxFromAspectModelWithCode() throws IOException, DeserializationException { final Environment env = getAssetAdministrationShellFromAspect( TestAspect.ASPECT_WITH_CODE ); assertEquals( 2, env.getConceptDescriptions().size() ); assertEquals( 1, env.getSubmodels().size() ); @@ -283,22 +284,20 @@ void testGenerateAasxFromBammAspectWithCode() throws IOException, Deserializatio } @Test - void testGenerateAasxFromBammAspectWithEnumeration() throws IOException, DeserializationException { + void testGenerateAasxFromAspectModelWithEnumeration() throws IOException, DeserializationException { final Environment env = getAssetAdministrationShellFromAspect( TestAspect.ASPECT_WITH_ENUMERATION ); assertEquals( 2, env.getConceptDescriptions().size() ); - final DataSpecificationIec61360 dataSpecificationContent = - (DataSpecificationIec61360) - env.getConceptDescriptions().stream() - .filter( x -> x.getIdShort().equals( "id_TestEnumeration" ) ) - .findFirst() - .get() - .getEmbeddedDataSpecifications() - .stream() - .findFirst() - .get() - .getDataSpecificationContent(); + final DataSpecificationIec61360 dataSpecificationContent = (DataSpecificationIec61360) env.getConceptDescriptions().stream() + .filter( conceptDescription -> conceptDescription.getIdShort().equals( "TestEnumeration" ) ) + .findFirst() + .get() + .getEmbeddedDataSpecifications() + .stream() + .findFirst() + .get() + .getDataSpecificationContent(); assertEquals( 3, dataSpecificationContent.getValueList().getValueReferencePairs().size() ); assertEquals( 1, env.getSubmodels().size() ); @@ -308,29 +307,21 @@ void testGenerateAasxFromBammAspectWithEnumeration() throws IOException, Deseria } @ParameterizedTest - @EnumSource( - value = TestAspect.class, - mode = EnumSource.Mode.EXCLUDE, - names = { - "ASPECT_WITH_STRING_ENUMERATION" - } ) + @EnumSource( value = TestAspect.class ) // anonymous enumeration in test has no urn for enum values but is required for Concept // Description referencing - public void testGeneration( final TestAspect testAspect ) throws IOException, DeserializationException, SAXException { + public void testGeneration( final TestAspect testAspect ) throws IOException, DeserializationException { final ByteArrayOutputStream baos = getByteArrayOutputStreamFromAspect( testAspect ); final byte[] xmlFile = baos.toByteArray(); - final Environment env = loadAASX( new ByteArrayInputStream( xmlFile ) ); - assertTrue( env.getSubmodels().size() >= 1, "No Submodel in AAS present." ); - try { - validate( new ByteArrayInputStream( xmlFile ) ); - } catch ( final SAXException e ) { - final String xmlContent = new String( xmlFile, StandardCharsets.UTF_8 ); - final int line = ((SAXParseException) e).getLineNumber(); - final String faultyLine = xmlContent.lines().skip( line - 1 ).findFirst().orElse( "" ); - final String model = "AAS XML file causing the Exception. \nProblem within line " + line + ": " + faultyLine + "\n" + xmlContent; - throw new SAXException( model, e ); - } + final String aasXml = new String( xmlFile ); + assertThat( aasXml ).doesNotContain( "DefaultScalarValue[" ); + assertThat( aasXml ).doesNotContain( "DefaultEntity[" ); + assertThat( aasXml ).doesNotContain( "Optional[" ); + + final Environment env = loadAASX( new ByteArrayInputStream( xmlFile ) ); + assertFalse( env.getSubmodels().isEmpty(), "No Submodel in AAS present." ); + validate( new ByteArrayInputStream( xmlFile ) ); } private void checkDataSpecificationIEC61360( final Set semanticIds, final Environment env ) { @@ -342,10 +333,11 @@ private DataSpecificationContent getDataSpecificationIEC61360( final String sema final List filteredConceptDescriptions = conceptDescriptions.stream() .filter( x -> x.getId().equals( semanticId ) ) - .collect( Collectors.toList() ); + .toList(); assertEquals( 1, filteredConceptDescriptions.size(), "Not exactly 1 ConceptDescription for semanticId. " + semanticId ); - final List embeddedDataSpecifications = filteredConceptDescriptions.get( 0 ).getEmbeddedDataSpecifications(); + final List embeddedDataSpecifications = filteredConceptDescriptions.get( 0 ) + .getEmbeddedDataSpecifications(); assertEquals( 1, embeddedDataSpecifications.size(), "Not exactly 1 EmbeddedDataSpecification for semanticId. " + semanticId ); assertTrue( embeddedDataSpecifications.stream().findFirst().isPresent(), "There is no EmbeddedDataSpecification" ); @@ -356,7 +348,7 @@ private Environment getAssetAdministrationShellFromAspect( final TestAspect test throws DeserializationException, IOException { final Aspect aspect = loadAspect( testAspect ); final ByteArrayOutputStream out = generator.generateXmlOutput( aspect ); - return loadAASX( out.toByteArray(), testAspect ); + return loadAASX( out.toByteArray() ); } private Environment getAssetAdministrationShellFromAspectWithData( final TestAspect testAspect ) @@ -365,24 +357,23 @@ private Environment getAssetAdministrationShellFromAspectWithData( final TestAsp final JsonNode aspectData = loadPayload( testAspect ); final ByteArrayOutputStream out = generator.generateXmlOutput( Map.of( aspect, aspectData ) ); final var data = out.toByteArray(); - return loadAASX( data, testAspect ); + return loadAASX( data ); } - private ByteArrayOutputStream getByteArrayOutputStreamFromAspect( final TestAspect testAspect ) - throws IOException { + private ByteArrayOutputStream getByteArrayOutputStreamFromAspect( final TestAspect testAspect ) throws IOException { final Aspect aspect = loadAspect( testAspect ); return generator.generateXmlOutput( aspect ); } - private void validate( ByteArrayInputStream xmlStream ) throws IOException, SAXException { - final SchemaFactory factory = - SchemaFactory.newInstance( XMLConstants.W3C_XML_SCHEMA_NS_URI ); - - final Schema schema = factory.newSchema( - new StreamSource( getClass().getResourceAsStream( XML_XSD_AAS_SCHEMA_LOCATION ) ) ); - final Validator validator = schema.newValidator(); - validator.validate( new StreamSource( xmlStream ), null ); - + private void validate( final ByteArrayInputStream xmlStream ) { + try { + final SchemaFactory factory = SchemaFactory.newInstance( XMLConstants.W3C_XML_SCHEMA_NS_URI ); + final Schema schema = factory.newSchema( new StreamSource( getClass().getResourceAsStream( XML_XSD_AAS_SCHEMA_LOCATION ) ) ); + final Validator validator = schema.newValidator(); + validator.validate( new StreamSource( xmlStream ), null ); + } catch ( final SAXException | IOException e ) { + fail( e ); + } } private Aspect loadAspect( final TestAspect testAspect ) { @@ -399,7 +390,7 @@ private Environment loadAASX( final ByteArrayInputStream byteStream ) throws Des return deserializer.read( byteStream ); } - private Environment loadAASX( final byte[] data, final TestAspect testAspect ) throws DeserializationException { + private Environment loadAASX( final byte[] data ) throws DeserializationException { final XmlDeserializer deserializer = new XmlDeserializer(); return deserializer.read( new ByteArrayInputStream( data ) ); }