Skip to content

Commit ff1eb23

Browse files
committed
Add XMLSchema validation to test code. Start fixing issues found via XMLSchema validation
Signed-off-by: Johannes Kristan <[email protected]>
1 parent 3b65f04 commit ff1eb23

File tree

3 files changed

+107
-27
lines changed

3 files changed

+107
-27
lines changed

core/sds-aspect-model-aas-generator/src/main/java/io/openmanufacturing/sds/aspectmodel/aas/AspectModelAASVisitor.java

Lines changed: 62 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
import org.apache.jena.rdf.model.ResourceFactory;
2828
import org.apache.jena.vocabulary.RDF;
2929
import org.apache.jena.vocabulary.XSD;
30+
import org.eclipse.digitaltwin.aas4j.v3.model.AdministrativeInformation;
3031
import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell;
32+
import org.eclipse.digitaltwin.aas4j.v3.model.AssetKind;
3133
import org.eclipse.digitaltwin.aas4j.v3.model.ConceptDescription;
3234
import org.eclipse.digitaltwin.aas4j.v3.model.DataSpecificationIEC61360;
3335
import org.eclipse.digitaltwin.aas4j.v3.model.DataTypeDefXsd;
@@ -41,13 +43,16 @@
4143
import org.eclipse.digitaltwin.aas4j.v3.model.Operation;
4244
import org.eclipse.digitaltwin.aas4j.v3.model.OperationVariable;
4345
import org.eclipse.digitaltwin.aas4j.v3.model.Reference;
46+
import org.eclipse.digitaltwin.aas4j.v3.model.ReferenceTypes;
4447
import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
4548
import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement;
4649
import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementCollection;
4750
import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElementList;
4851
import org.eclipse.digitaltwin.aas4j.v3.model.ValueList;
4952
import org.eclipse.digitaltwin.aas4j.v3.model.ValueReferencePair;
53+
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultAdministrativeInformation;
5054
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultAssetAdministrationShell;
55+
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultAssetInformation;
5156
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultConceptDescription;
5257
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultDataSpecificationIEC61360;
5358
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultEmbeddedDataSpecification;
@@ -186,14 +191,25 @@ public Environment visitAspect( final Aspect aspect, Context context ) {
186191

187192
final Submodel submodel = context.getSubmodel();
188193
submodel.setIdShort( aspect.getName() );
194+
submodel.setId( aspect.getAspectModelUrn().toString() + "/submodel" );
189195
submodel.setSemanticId( buildReferenceToConceptDescription( aspect ) );
190196
submodel.setDescription( map( aspect.getDescriptions() ) );
191197
submodel.setKind( ModelingKind.TEMPLATE );
198+
submodel.setAdministration( new DefaultAdministrativeInformation.Builder().build() );
199+
submodel.setChecksum( String.valueOf( aspect.hashCode() ) );
192200

193201
createConceptDescription( aspect, context );
194202

195203
final AssetAdministrationShell administrationShell =
196-
new DefaultAssetAdministrationShell.Builder().idShort( ADMIN_SHELL_NAME ).build();
204+
new DefaultAssetAdministrationShell.Builder()
205+
.idShort( ADMIN_SHELL_NAME )
206+
.description( createLangString( ADMIN_SHELL_NAME, "en" ) )
207+
.checksum( "a checksum" )
208+
.id( ADMIN_SHELL_NAME )
209+
.administration( new DefaultAdministrativeInformation.Builder().build() )
210+
.assetInformation( new DefaultAssetInformation.Builder().assetKind( AssetKind.INSTANCE ).build() )
211+
.embeddedDataSpecifications( extractEmbeddedDataSpecification( aspect ) )
212+
.build();
197213
context
198214
.getEnvironment()
199215
.setAssetAdministrationShells( Collections.singletonList( administrationShell ) );
@@ -211,14 +227,17 @@ private List<SubmodelElement> visitOperations(
211227

212228
private List<SubmodelElement> visitProperties(
213229
final List<Property> elements, final Context context ) {
214-
return elements.stream().map( i -> map( i, context ) ).collect( Collectors.toList() );
230+
return elements.stream().map( i -> map( i, context ) )
231+
.filter(Optional::isPresent)
232+
.map(Optional::get)
233+
.collect( Collectors.toList() );
215234
}
216235

217-
private SubmodelElement map( final Property property, final Context context ) {
218-
final Supplier<SubmodelElement> defaultResultForProperty = () -> context.getSubmodel().getSubmodelElements().stream()
236+
private Optional<SubmodelElement> map( final Property property, final Context context ) {
237+
final Optional<SubmodelElement> defaultResultForProperty = context.getSubmodel()
238+
.getSubmodelElements().stream()
219239
.filter( i -> i.getIdShort().equals( property.getName() ) )
220-
.findFirst()
221-
.orElse( new DefaultProperty.Builder().build() );
240+
.findFirst();
222241
if ( recursiveProperty.contains( property ) ) {
223242
// The guard checks for recursion in properties. If a recursion happens, the respective
224243
// property will be excluded from generation.
@@ -228,13 +247,13 @@ private SubmodelElement map( final Property property, final Context context ) {
228247
} else {
229248
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.", property ) );
230249
}
231-
return defaultResultForProperty.get();
250+
return defaultResultForProperty;
232251
}
233252
recursiveProperty.add( property );
234253

235-
if ( property.getCharacteristic().isEmpty() ) {
254+
if ( property.getCharacteristic().isEmpty() || property.isAbstract() ) {
236255
LOG.warn( String.format( "Having an Abstract Property. Will be excluded from AAS mapping." ) );
237-
return defaultResultForProperty.get();
256+
return Optional.empty();
238257
}
239258

240259
// Characteristic defines how the property is mapped to SubmodelElement
@@ -245,7 +264,8 @@ private SubmodelElement map( final Property property, final Context context ) {
245264
final SubmodelElement element = context.getPropertyResult();
246265

247266
recursiveProperty.remove( property );
248-
return element;
267+
268+
return Optional.of( element );
249269
}
250270

251271
private SubmodelElement decideOnMapping( final Property property, final Context context ) {
@@ -283,12 +303,12 @@ private org.eclipse.digitaltwin.aas4j.v3.model.Property mapToAasProperty( final
283303
return new DefaultProperty.Builder()
284304
.idShort( property.getName() )
285305
.kind( ModelingKind.TEMPLATE )
286-
.valueType( mapAASXSDataType( property.getCharacteristic().flatMap( Characteristic::getDataType ).map( this::mapType ).orElse( UNKNOWN_TYPE ) ) ) // TODO this might not work and a proper mapping implementation is required
306+
.valueType( mapAASXSDataType( property.getCharacteristic().flatMap( Characteristic::getDataType ).map( this::mapType ).orElse( UNKNOWN_TYPE ) ) )
287307
.displayName( map( property.getPreferredNames() ) )
288308
.value( property.getExampleValue().map( i -> i.getValue().toString() ).orElse( UNKNOWN_EXAMPLE ) )
289309
.description( map( property.getDescriptions() ) )
290-
.semanticId( buildReferenceToConceptDescription( property ) ) // this is the link to the conceptDescription containing the details for
291-
// the Characteristic
310+
.semanticId( buildReferenceToConceptDescription( property ) ) // link to the conceptDescription containing the details for the Characteristic
311+
.supplementalSemanticIds( buildReferencesForSeeElements(property.getSee()) )
292312
.build();
293313
}
294314

@@ -318,7 +338,7 @@ private Operation map(
318338
}
319339

320340
private OperationVariable mapOperationVariable( final Property property, final Context context ) {
321-
return new DefaultOperationVariable.Builder().value( map( property, context ) ).build();
341+
return new DefaultOperationVariable.Builder().value( map( property, context ).get() ).build();
322342
}
323343

324344
private List<LangString> map( final Set<io.openmanufacturing.sds.metamodel.datatypes.LangString> localizedStrings ) {
@@ -345,7 +365,7 @@ private Reference buildReferenceToEnumValue( final Enumeration enumeration, fina
345365
.type( KeyTypes.DATA_ELEMENT )
346366
.value( determineIdentifierFor( enumeration ) + ":" + value.toString() )
347367
.build();
348-
return new DefaultReference.Builder().keys( key ).build();
368+
return new DefaultReference.Builder().type( ReferenceTypes.MODEL_REFERENCE ).keys( key ).build();
349369
}
350370

351371
private Reference buildReferenceToConceptDescription( final Aspect aspect ) {
@@ -354,7 +374,7 @@ private Reference buildReferenceToConceptDescription( final Aspect aspect ) {
354374
.type( KeyTypes.CONCEPT_DESCRIPTION )
355375
.value( extractIdentifier( aspect ) )
356376
.build();
357-
return new DefaultReference.Builder().keys( key ).build();
377+
return new DefaultReference.Builder().keys( key ).type( ReferenceTypes.MODEL_REFERENCE ).build();
358378
}
359379

360380
private Reference buildReferenceToConceptDescription( final Property property ) {
@@ -363,7 +383,23 @@ private Reference buildReferenceToConceptDescription( final Property property )
363383
.type( KeyTypes.CONCEPT_DESCRIPTION )
364384
.value( extractIdentifier( property ) )
365385
.build();
366-
return new DefaultReference.Builder().keys( key ).build();
386+
return new DefaultReference.Builder().type( ReferenceTypes.MODEL_REFERENCE ).keys( key ).build();
387+
}
388+
389+
private Reference buildReferenceForSeeElement( final String seeReference ) {
390+
final Key key =
391+
new DefaultKey.Builder()
392+
.type( KeyTypes.GLOBAL_REFERENCE )
393+
.value( seeReference )
394+
.build();
395+
return new DefaultReference.Builder()
396+
.type( ReferenceTypes.GLOBAL_REFERENCE )
397+
.keys( key )
398+
.build();
399+
}
400+
401+
private List<Reference> buildReferencesForSeeElements( final List<String> seeReferences ) {
402+
return seeReferences.stream().map( this::buildReferenceForSeeElement ).collect( Collectors.toList() );
367403
}
368404

369405
private String determineIdentifierFor( final NamedElement isDescribed ) {
@@ -410,12 +446,14 @@ private void createConceptDescription( final Aspect aspect, final Context contex
410446

411447
private EmbeddedDataSpecification extractEmbeddedDataSpecification( final Property property ) {
412448
return new DefaultEmbeddedDataSpecification.Builder()
449+
.dataSpecification( buildReferenceForSeeElement( property.getAspectModelUrn().toString() ) )
413450
.dataSpecificationContent( extractDataSpecificationContent( property ) )
414451
.build();
415452
}
416453

417454
private EmbeddedDataSpecification extractEmbeddedDataSpecification( final Aspect aspect ) {
418455
return new DefaultEmbeddedDataSpecification.Builder()
456+
.dataSpecification( buildReferenceForSeeElement( aspect.getAspectModelUrn().toString() ) )
419457
.dataSpecificationContent( extractDataSpecificationContent( aspect ) )
420458
.build();
421459
}
@@ -428,7 +466,9 @@ private DataSpecificationIEC61360 extractDataSpecificationContent( final Propert
428466

429467
return new DefaultDataSpecificationIEC61360.Builder()
430468
.definition( definitions )
431-
.preferredName( map( property.getPreferredNames() ) )
469+
.preferredName( property.getPreferredNames().isEmpty() ?
470+
Collections.singletonList( createLangString( property.getName(), DEFAULT_LOCALE ) ) :
471+
map( property.getPreferredNames() ) )
432472
.shortName( createLangString( property.getName(), DEFAULT_LOCALE ) )
433473
.dataType( mapIEC61360DataType( property.getCharacteristic() ) )
434474
.build();
@@ -440,8 +480,10 @@ private DataSpecificationIEC61360 extractDataSpecificationContent( final Aspect
440480

441481
return new DefaultDataSpecificationIEC61360.Builder()
442482
.definition( definitions )
443-
.preferredName( map( aspect.getPreferredNames() ) )
444-
.shortName( createLangString( aspect.getName(), DEFAULT_LOCALE ) )
483+
.preferredName( aspect.getPreferredNames().isEmpty() ?
484+
Collections.singletonList( createLangString( aspect.getName(), DEFAULT_LOCALE ) ) :
485+
map( aspect.getPreferredNames() ) )
486+
.value( aspect.getName() )
445487
.build();
446488
}
447489

core/sds-aspect-model-aas-generator/src/test/java/io/openmanufacturing/sds/aspectmodel/aas/AspectModelAASGeneratorTest.java

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,21 @@
1818
import java.io.ByteArrayInputStream;
1919
import java.io.ByteArrayOutputStream;
2020
import java.io.IOException;
21+
import java.net.MalformedURLException;
22+
import java.net.URL;
23+
import java.nio.charset.StandardCharsets;
2124
import java.util.List;
2225
import java.util.Set;
2326
import java.util.stream.Collectors;
2427

28+
import javax.xml.XMLConstants;
29+
import javax.xml.transform.Result;
30+
import javax.xml.transform.sax.SAXResult;
31+
import javax.xml.transform.stream.StreamSource;
32+
import javax.xml.validation.Schema;
33+
import javax.xml.validation.SchemaFactory;
34+
import javax.xml.validation.Validator;
35+
2536
import org.eclipse.digitaltwin.aas4j.v3.dataformat.DeserializationException;
2637
import org.eclipse.digitaltwin.aas4j.v3.dataformat.xml.XmlDeserializer;
2738
import org.eclipse.digitaltwin.aas4j.v3.model.ConceptDescription;
@@ -36,6 +47,7 @@
3647
import org.junit.jupiter.api.Test;
3748
import org.junit.jupiter.params.ParameterizedTest;
3849
import org.junit.jupiter.params.provider.EnumSource;
50+
import org.xml.sax.SAXException;
3951

4052
import io.openmanufacturing.sds.aspectmetamodel.KnownVersion;
4153
import io.openmanufacturing.sds.aspectmodel.resolver.services.VersionedModel;
@@ -227,9 +239,18 @@ void testGenerateAasxFromBammAspectWithEnumeration() throws IOException, Deseria
227239
} )
228240
// anonymous enumeration in test has no urn for enum values but is required for Concept
229241
// Description referencing
230-
public void testGeneration( final TestAspect testAspect ) throws IOException, DeserializationException {
231-
final Environment env = getAssetAdministrationShellFromAspect( testAspect );
242+
public void testGeneration( final TestAspect testAspect ) throws IOException, DeserializationException, SAXException {
243+
ByteArrayOutputStream baos = getByteArrayOutputStreamFromAspect( testAspect );
244+
byte[] xmlFile = baos.toByteArray();
245+
final Environment env = loadAASX( new ByteArrayInputStream( xmlFile ) );
232246
assertTrue( env.getSubmodels().size() >= 1, "No Submodel in AAS present." );
247+
try{
248+
validate( new ByteArrayInputStream( xmlFile ) );
249+
}catch(SAXException e){
250+
String model = "AAS XML file causing the Exception. \n" + new String(xmlFile, StandardCharsets.UTF_8);
251+
throw new SAXException(model, e);
252+
}
253+
233254
}
234255

235256
private void checkDataSpecificationIEC61360( final Set<String> semanticIds, final Environment env ) {
@@ -258,13 +279,32 @@ private Environment getAssetAdministrationShellFromAspect( final TestAspect test
258279
return loadAASX( out );
259280
}
260281

282+
private ByteArrayOutputStream getByteArrayOutputStreamFromAspect( final TestAspect testAspect )
283+
throws IOException {
284+
final Aspect aspect = loadAspect( testAspect );
285+
return generator.generateXmlOutput( aspect );
286+
}
287+
288+
private void validate(ByteArrayInputStream xmlStream) throws IOException, SAXException {
289+
SchemaFactory factory =
290+
SchemaFactory.newInstance( XMLConstants.W3C_XML_SCHEMA_NS_URI);
291+
Schema schema = factory.newSchema(new URL( "https://raw.githubusercontent.com/admin-shell-io/aas-specs/V3.0.5RC02/schemas/xml/AAS.xsd" ) );
292+
Validator validator = schema.newValidator();
293+
validator.validate(new StreamSource( xmlStream ), null);
294+
295+
}
296+
261297
private Aspect loadAspect( final TestAspect testAspect ) {
262298
final VersionedModel model = TestResources.getModel( testAspect, KnownVersion.getLatest() ).get();
263299
return AspectModelLoader.getSingleAspect( model ).get();
264300
}
265301

266302
private Environment loadAASX( final ByteArrayOutputStream byteStream ) throws DeserializationException {
303+
return loadAASX( new ByteArrayInputStream( byteStream.toByteArray() ) );
304+
}
305+
306+
private Environment loadAASX( final ByteArrayInputStream byteStream ) throws DeserializationException {
267307
final XmlDeserializer deserializer = new XmlDeserializer();
268-
return deserializer.read( new ByteArrayInputStream( byteStream.toByteArray() ) );
308+
return deserializer.read( byteStream );
269309
}
270310
}

pom.xml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,6 @@
7979
<graphviz-version>0.18.1</graphviz-version>
8080
<guava-version>31.1-jre</guava-version>
8181
<guice-version>5.1.0</guice-version>
82-
<io-admin-shell-aas-model>1.2.0</io-admin-shell-aas-model>
83-
<io-admin-shell-aas-serializer>1.2.0</io-admin-shell-aas-serializer>
8482
<javaparser-version>3.24.2</javaparser-version>
8583
<jena-version>4.4.0</jena-version>
8684
<jqwik-version>1.6.5</jqwik-version>
@@ -103,8 +101,8 @@
103101
<vavr-version>0.10.4</vavr-version>
104102
<!-- TODO remember to update the link to the Velocity engine user guide in the 'java-aspect-tooling.adoc' page -->
105103
<velocity-version>2.3</velocity-version>
106-
<io-admin-shell-aas-model>1.0.0-milestone-01</io-admin-shell-aas-model>
107-
<io-admin-shell-aas-serializer>1.0.0-milestone-01</io-admin-shell-aas-serializer>
104+
<io-admin-shell-aas-model>1.0.0-milestone-02</io-admin-shell-aas-model>
105+
<io-admin-shell-aas-serializer>1.0.0-milestone-02</io-admin-shell-aas-serializer>
108106

109107
<!-- Versions of plugins -->
110108
<build-helper-maven-plugin-version>3.2.0</build-helper-maven-plugin-version>

0 commit comments

Comments
 (0)