Skip to content

Commit

Permalink
Merge branch 'main' into 2.9.x
Browse files Browse the repository at this point in the history
  • Loading branch information
atextor committed Jul 31, 2024
2 parents 81e9f58 + 7492acb commit b0fd5b6
Show file tree
Hide file tree
Showing 14 changed files with 386 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@

import static java.util.stream.Collectors.toSet;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.ArrayDeque;
Expand All @@ -30,6 +35,8 @@
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.eclipse.esmf.aspectmodel.AspectModelFile;
import org.eclipse.esmf.aspectmodel.resolver.AspectModelFileLoader;
Expand Down Expand Up @@ -66,6 +73,7 @@
*/
public class AspectModelLoader implements ResolutionStrategySupport {
private static final Logger LOG = LoggerFactory.getLogger( AspectModelLoader.class );
private static final String ASPECT_MODELS_FOLDER = "aspect-models";

public static final Supplier<ResolutionStrategy> DEFAULT_STRATEGY = () -> {
final Path currentDirectory = Path.of( System.getProperty( "user.dir" ) );
Expand Down Expand Up @@ -173,6 +181,90 @@ public AspectModel load( final InputStream inputStream ) {
return buildAspectModel( loaderContext.loadedFiles() );
}

@FunctionalInterface
public interface InputStreamProvider {
InputStream get() throws IOException;
}

/**
* Load Namespace Package (Archive) an Aspect Model from a File
*
* @param namespacePackage the archive file
* @return the Aspect Model
*/
public AspectModel loadNamespacePackage( final File namespacePackage ) {
if ( !namespacePackage.exists() || !namespacePackage.isFile() ) {
throw new RuntimeException( new FileNotFoundException( "The specified file does not exist or is not a file." ) );
}

try ( InputStream inputStream = new FileInputStream( namespacePackage ) ) {
return loadNamespacePackage( inputStream );
} catch ( IOException e ) {
LOG.error( "Error reading the file: {}", namespacePackage.getAbsolutePath(), e );
throw new RuntimeException( "Error reading the file: " + namespacePackage.getAbsolutePath(), e );
}
}

/**
* Load Namespace Package (Archive) an Aspect Model from an InputStream
*
* @param inputStream the input stream
* @return the Aspect Model
*/
public AspectModel loadNamespacePackage( final InputStream inputStream ) {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
inputStream.transferTo( baos );
} catch ( IOException e ) {
throw new RuntimeException( e );
}
final boolean hasAspectModelsFolder = containsFolderInNamespacePackage( new ByteArrayInputStream( baos.toByteArray() ) );
return loadNamespacePackageFromStream( new ByteArrayInputStream( baos.toByteArray() ), hasAspectModelsFolder );
}

private AspectModel loadNamespacePackageFromStream( final InputStream inputStream, final boolean hasAspectModelsFolder ) {
List<AspectModelFile> aspectModelFiles = new ArrayList<>();

try ( ZipInputStream zis = new ZipInputStream( inputStream ) ) {
ZipEntry entry;

while ( (entry = zis.getNextEntry()) != null ) {
boolean isRelevantEntry =
(hasAspectModelsFolder && entry.getName().contains( String.format( "%s/", ASPECT_MODELS_FOLDER ) ) && entry.getName()
.endsWith( ".ttl" ))
|| (!hasAspectModelsFolder && entry.getName().endsWith( ".ttl" ));

if ( isRelevantEntry ) {
AspectModelFile aspectModelFile = migrate( AspectModelFileLoader.load( zis ) );
aspectModelFiles.add( aspectModelFile );
}
}

zis.closeEntry();
} catch ( IOException e ) {
LOG.error( "Error reading the Archive input stream", e );
throw new RuntimeException( "Error reading the Archive input stream", e );
}

LoaderContext loaderContext = new LoaderContext();
resolve( aspectModelFiles, loaderContext );
return buildAspectModel( loaderContext.loadedFiles() );
}

private boolean containsFolderInNamespacePackage( final InputStream inputStream ) {
try ( ZipInputStream zis = new ZipInputStream( inputStream ) ) {
ZipEntry entry;
while ( (entry = zis.getNextEntry()) != null ) {
if ( entry.isDirectory() && entry.getName().contains( String.format( "%s/", ASPECT_MODELS_FOLDER ) ) ) {
return true;
}
}
} catch ( IOException e ) {
throw new RuntimeException( e );
}
return false;
}

private AspectModelFile migrate( final AspectModelFile file ) {
return MetaModelVersionMigrator.INSTANCE.apply( file );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public DefaultNamespace( final String packagePart, final VersionNumber versionNu
* @param elements the list of elements in the namspace
*/
public DefaultNamespace( final String uri, final List<ModelElement> elements, final Optional<AspectModelFile> source ) {
this( uri.split( ":" )[2], VersionNumber.parse( uri.split( ":" )[3] ), elements, source );
this( uri.split( ":" )[2], VersionNumber.parse( uri.split( ":" )[3].replace( "#", "" ) ), elements, source );
}

// /**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,32 @@

import static org.assertj.core.api.Assertions.assertThat;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.eclipse.esmf.aspectmodel.resolver.FileSystemStrategy;
import org.eclipse.esmf.aspectmodel.resolver.ResolutionStrategy;
import org.eclipse.esmf.metamodel.AbstractEntity;
import org.eclipse.esmf.metamodel.AspectModel;
import org.eclipse.esmf.metamodel.ComplexType;
import org.eclipse.esmf.samm.KnownVersion;
import org.eclipse.esmf.test.TestAspect;
import org.eclipse.esmf.test.TestResources;

import org.junit.jupiter.api.Test;

class AspectModelLoaderTest {
@Test
public void testOfAbstractEntityCyclomaticCreation() {
void testOfAbstractEntityCyclomaticCreation() {
final Map<String, ComplexType> entities =
TestResources.load( TestAspect.ASPECT_WITH_MULTIPLE_ENTITIES_SAME_EXTEND ).elements().stream()
.filter( ComplexType.class::isInstance )
Expand All @@ -43,4 +54,134 @@ public void testOfAbstractEntityCyclomaticCreation() {
assertThat( entities ).extracting( "testEntityTwo" ).isInstanceOfSatisfying( ComplexType.class, type ->
assertThat( type ).extracting( ComplexType::getExtends ).extracting( Optional::get ).isSameAs( abstractEntity ) );
}

@Test
void testLoadAspectModelFromZipArchiveFile() {
final Path archivePath = getPackage( "namespaces.zip" );
final AspectModel aspectModel = new AspectModelLoader().loadNamespacePackage( new File( archivePath.toString() ) );

assertThat( aspectModel.namespaces() ).hasSize( 2 );
assertThat( aspectModel.namespaces().get( 0 ).getName() ).contains( "urn:samm:org.eclipse.examples:1.1.0" );
assertThat( aspectModel.namespaces().get( 1 ).getName() ).contains( "urn:samm:org.eclipse.examples:1.0.0" );

final List<String> aspectsNames = List.of( "Movement2", "Movement3", "Movement", "SimpleAspect" );

assertThat( aspectModel.files() ).hasSize( 4 );
assertThat( aspectModel.files() )
.anySatisfy( aspectModelFile -> {
assertThat( aspectsNames ).contains( aspectModelFile.aspect().getName() );
} );
}

@Test
void testLoadAspectModelFromZipArchiveInputStream() throws FileNotFoundException {
final Path archivePath = getPackage( "namespaces.zip" );
final AspectModel aspectModel = new AspectModelLoader().loadNamespacePackage( new FileInputStream( archivePath.toString() ) );

assertThat( aspectModel.namespaces() ).hasSize( 2 );
assertThat( aspectModel.namespaces().get( 0 ).getName() ).contains( "urn:samm:org.eclipse.examples:1.1.0" );
assertThat( aspectModel.namespaces().get( 1 ).getName() ).contains( "urn:samm:org.eclipse.examples:1.0.0" );

final List<String> aspectsNames = List.of( "Movement2", "Movement3", "Movement", "SimpleAspect" );

assertThat( aspectModel.files() ).hasSize( 4 );
assertThat( aspectModel.files() )
.anySatisfy( aspectModelFile -> {
assertThat( aspectsNames ).contains( aspectModelFile.aspect().getName() );
} );
}

/**
* Test migration to the latest version of Aspect Model in Archive
*/
@Test
void testLoadAspectModelFromZipArchive2_0_0() throws FileNotFoundException {
final Path archivePath = getPackage( "namespaces_with_old_version.zip" );
final AspectModel aspectModel = new AspectModelLoader().loadNamespacePackage( new FileInputStream( archivePath.toString() ) );

assertThat( aspectModel.namespaces() ).hasSize( 2 );
assertThat( aspectModel.namespaces().get( 0 ).getName() ).contains( "urn:samm:org.eclipse.examples:1.1.0" );
assertThat( aspectModel.namespaces().get( 1 ).getName() ).contains( "urn:samm:org.eclipse.examples:1.0.0" );

final List<String> aspectsNames = List.of( "Movement2", "Movement3", "Movement4", "Movement", "SimpleAspect" );

assertThat( aspectModel.files() ).hasSize( 5 );
assertThat( aspectModel.files() )
.anySatisfy( aspectModelFile -> {
assertThat( aspectsNames ).contains( aspectModelFile.aspect().getName() );
} );
}

@Test
void testLoadAspectModelFromZipArchiveAspectModelsRoot() throws FileNotFoundException {
final Path archivePath = getPackage( "namespaces-aspect-models-root.zip" );
final AspectModel aspectModel = new AspectModelLoader().loadNamespacePackage( new FileInputStream( archivePath.toString() ) );

assertThat( aspectModel.namespaces() ).hasSize( 2 );
assertThat( aspectModel.namespaces().get( 0 ).getName() ).contains( "urn:samm:org.eclipse.examples:1.1.0" );
assertThat( aspectModel.namespaces().get( 1 ).getName() ).contains( "urn:samm:org.eclipse.examples:1.0.0" );

final List<String> aspectsNames = List.of( "Movement2", "Movement3", "Movement", "SimpleAspect" );

assertThat( aspectModel.files() ).hasSize( 4 );
assertThat( aspectModel.files() )
.anySatisfy( aspectModelFile -> {
assertThat( aspectsNames ).contains( aspectModelFile.aspect().getName() );
} );
}

@Test
void testLoadAspectModelFromZipArchiveAspectModelsSubfolder() throws FileNotFoundException {
final Path archivePath = getPackage( "namespaces-aspect-models-subfolder.zip" );
final AspectModel aspectModel = new AspectModelLoader().loadNamespacePackage( new FileInputStream( archivePath.toString() ) );

assertThat( aspectModel.namespaces() ).hasSize( 2 );
assertThat( aspectModel.namespaces().get( 0 ).getName() ).contains( "urn:samm:org.eclipse.examples:1.1.0" );
assertThat( aspectModel.namespaces().get( 1 ).getName() ).contains( "urn:samm:org.eclipse.examples:1.0.0" );

final List<String> aspectsNames = List.of( "Movement2", "Movement3", "Movement", "SimpleAspect" );

assertThat( aspectModel.files() ).hasSize( 4 );
assertThat( aspectModel.files() )
.anySatisfy( aspectModelFile -> {
assertThat( aspectsNames ).contains( aspectModelFile.aspect().getName() );
} );
}

@Test
void testLoadAspectModelFromZipArchiveWithSharedProperty() throws FileNotFoundException, URISyntaxException {
final Path archivePath = getPackage( "namespace-with-shared-property.zip" );

final File aspectModelsRootDirectory = new File(
AspectModelLoaderTest.class.getClassLoader()
.getResource( KnownVersion.getLatest().toString().toLowerCase() )
.toURI().getPath() );

final ResolutionStrategy urnStrategy = new FileSystemStrategy( aspectModelsRootDirectory.toPath() );

final AspectModel aspectModel = new AspectModelLoader( urnStrategy ).loadNamespacePackage(
new FileInputStream( archivePath.toString() ) );

assertThat( aspectModel.namespaces() ).hasSize( 1 );
assertThat( aspectModel.namespaces().get( 0 ).getName() ).contains( "urn:samm:org.eclipse.esmf.test:1.0.0" );

final List<String> aspectsNames = List.of( "Movement" );

assertThat( aspectModel.files() ).hasSize( 2 );
assertThat( aspectModel.files() )
.anySatisfy( aspectModelFile -> {
if ( !aspectModelFile.aspects().isEmpty() ) {
assertThat( aspectsNames ).contains( aspectModelFile.aspect().getName() );
}
} );
}

/**
* Returns the File object for a test model file
*/
private Path getPackage( final String packageName ) {
return Paths.get( String.format(
"%s/../../core/esmf-test-aspect-models/src/main/resources/packages/%s",
System.getProperty( "user.dir" ), packageName ) );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright (c) 2023 Robert Bosch Manufacturing Solutions GmbH
#
# See the AUTHORS file(s) distributed with this work for additional
# information regarding authorship.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
#
# SPDX-License-Identifier: MPL-2.0

@prefix : <urn:samm:org.eclipse.esmf.test:1.0.0#> .
@prefix samm: <urn:samm:org.eclipse.esmf.samm:meta-model:2.1.0#> .
@prefix samm-c: <urn:samm:org.eclipse.esmf.samm:characteristic:2.1.0#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

:propertyWithExampleValue a samm:Property ;
samm:characteristic samm-c:Text ;
samm:exampleValue "test" .
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ void generateAasxWithAspectDataForCollectionProperty() throws DeserializationExc

@Test
void generateAasxWithAspectDataForCollectionPropertyWithCustomMapper() throws DeserializationException {
AspectModelAasGenerator customGenerator = new AspectModelAasGenerator( List.of( new IntegerCollectionMapper() ) );
final AspectModelAasGenerator customGenerator = new AspectModelAasGenerator( List.of( new IntegerCollectionMapper() ) );
final Environment env = getAssetAdministrationShellFromAspectWithData( TestAspect.ASPECT_WITH_COLLECTION_OF_SIMPLE_TYPE,
customGenerator );
assertThat( env.getSubmodels() )
Expand Down Expand Up @@ -357,17 +357,17 @@ void testGenerateAasxFromAspectModelWithEnumeration() throws DeserializationExce
// anonymous enumeration in test has no urn for enum values but is required for Concept
// Description referencing
public void testGeneration( final TestAspect testAspect ) throws DeserializationException {
final String aspectAsString = aspectToString( testAspect );
final byte[] xmlFile = aspectAsString.getBytes();
final String aasXmlString = aspectToAasXml( testAspect );
final byte[] aasXmlInput = aasXmlString.getBytes();

final String aasXml = new String( xmlFile );
final String aasXml = new String( aasXmlInput );
assertThat( aasXml ).doesNotContain( "DefaultScalarValue[" );
assertThat( aasXml ).doesNotContain( "DefaultEntity[" );
assertThat( aasXml ).doesNotContain( "Optional[" );

final Environment env = loadAasx( new ByteArrayInputStream( xmlFile ) );
final Environment env = loadAasx( new ByteArrayInputStream( aasXmlInput ) );
assertThat( env.getSubmodels() ).isNotEmpty();
validate( new ByteArrayInputStream( xmlFile ) );
validate( new ByteArrayInputStream( aasXmlInput ) );
}

@Test
Expand Down Expand Up @@ -455,7 +455,7 @@ private Environment getAssetAdministrationShellFromAspectWithData( final TestAsp
return loadAasx( generator.generateAsByteArray( AasFileFormat.XML, aspect, aspectData ) );
}

private String aspectToString( final TestAspect testAspect ) {
private String aspectToAasXml( final TestAspect testAspect ) {
final Aspect aspect = TestResources.load( testAspect ).aspect();
return new String( generator.generateAsByteArray( AasFileFormat.XML, aspect ), StandardCharsets.UTF_8 );
}
Expand Down
4 changes: 4 additions & 0 deletions core/esmf-aspect-model-document-generators/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@
<artifactId>logback-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.esmf</groupId>
<artifactId>esmf-aspect-model-serializer</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
Loading

0 comments on commit b0fd5b6

Please sign in to comment.