Skip to content

Commit 899f0af

Browse files
committed
Handle models with any number of Aspects in artifact generators
This adjusts the constructors of the document and code generators so that the Aspect they intend to work on must be passed as an explicit argument. For that the `AspectContext` is introduced which wraps a `VersionedModel` and a single `Aspect` that is contained in the model. The task to determine the Aspect from the loaded model is delegated to the caller (here bamm-cli and Maven plugin).
1 parent 20fb9cd commit 899f0af

File tree

61 files changed

+689
-570
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+689
-570
lines changed

core/sds-aspect-meta-model-interface/src/main/java/io/openmanufacturing/sds/aspectmodel/resolver/AspectMetaModelResourceResolver.java

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,6 @@ public interface AspectMetaModelResourceResolver {
4343
*/
4444
Try<VersionedModel> mergeMetaModelIntoRawModel( final Model rawModel, final VersionNumber version );
4545

46-
/**
47-
* Parses an Aspect (meta) model URN into an {@link AspectModelUrn}
48-
*
49-
* @param uri The Aspect (meta) model URN
50-
* @return The {@link AspectModelUrn} if parsing succeeds, an {@link UrnSyntaxException} otherwise
51-
*/
52-
default Try<AspectModelUrn> getAspectModelUrn( final String uri ) {
53-
try {
54-
return Try.success( AspectModelUrn.fromUrn( uri ) );
55-
} catch ( final UrnSyntaxException exception ) {
56-
return Try.failure( exception );
57-
}
58-
}
59-
6046
/**
6147
* Retrieves the meta model version an Aspect model uses
6248
*

core/sds-aspect-meta-model-java/src/main/java/io/openmanufacturing/sds/metamodel/AspectContext.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,11 @@
1515

1616
import io.openmanufacturing.sds.aspectmodel.resolver.services.VersionedModel;
1717

18+
/**
19+
* The AspectContext wraps a loaded/resolved Aspect Model and a single Aspect that was instantiated from this model, i.e.,
20+
* which must be defined in the RDF model.
21+
* @param rdfModel the RDF model
22+
* @param aspect the Aspect
23+
*/
1824
public record AspectContext(VersionedModel rdfModel, Aspect aspect) {
1925
}

core/sds-aspect-meta-model-java/src/main/java/io/openmanufacturing/sds/metamodel/loader/AspectModelLoader.java

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313

1414
package io.openmanufacturing.sds.metamodel.loader;
1515

16+
import java.io.File;
1617
import java.util.ArrayList;
1718
import java.util.List;
1819
import java.util.Map;
1920
import java.util.Optional;
2021
import java.util.Set;
22+
import java.util.function.Predicate;
2123
import java.util.stream.Collectors;
2224

2325
import org.apache.jena.rdf.model.Model;
@@ -41,6 +43,7 @@
4143
import io.openmanufacturing.sds.aspectmodel.versionupdate.MigratorService;
4244
import io.openmanufacturing.sds.aspectmodel.vocabulary.BAMM;
4345
import io.openmanufacturing.sds.metamodel.Aspect;
46+
import io.openmanufacturing.sds.metamodel.AspectContext;
4447
import io.openmanufacturing.sds.metamodel.ModelElement;
4548
import io.openmanufacturing.sds.metamodel.ModelNamespace;
4649
import io.openmanufacturing.sds.metamodel.NamedElement;
@@ -79,7 +82,7 @@ private AspectModelLoader() {
7982
*
8083
* @see AspectModelResolver
8184
*/
82-
@Deprecated
85+
@Deprecated( forRemoval = true )
8386
public static Try<Aspect> fromVersionedModel( final VersionedModel versionedModel ) {
8487
return getSingleAspect( versionedModel );
8588
}
@@ -103,12 +106,12 @@ private static void validateNamespaceOfCustomUnits( final BAMM bamm, final Model
103106
* Creates an {@link Aspect} instance from a Turtle input model.
104107
*
105108
* @param versionedModel The RDF model representation of the Aspect model
106-
* @return The Aspect *
109+
* @return The Aspect
107110
* @deprecated use {@link #getSingleAspectUnchecked(VersionedModel)} instead to retain the same semantics, but better use
108111
* {@link #getElementsUnchecked(VersionedModel)} instead to also properly handle models that contain not exactly one Aspect
109112
* @see #fromVersionedModel(VersionedModel)
110113
*/
111-
@Deprecated
114+
@Deprecated( forRemoval = true )
112115
public static Aspect fromVersionedModelUnchecked( final VersionedModel versionedModel ) {
113116
return fromVersionedModel( versionedModel ).getOrElseThrow( cause -> {
114117
LOG.error( "Could not load Aspect", cause );
@@ -228,12 +231,7 @@ public static List<Aspect> getAspectsUnchecked( final VersionedModel versionedMo
228231
* @return the single Aspect contained in the model
229232
*/
230233
public static Try<Aspect> getSingleAspect( final VersionedModel versionedModel ) {
231-
return getAspects( versionedModel ).flatMap( aspects -> {
232-
if ( aspects.size() != 1 ) {
233-
return Try.failure( new InvalidRootElementCountException( "Aspect model does not contain exactly one Aspect" ) );
234-
}
235-
return Try.success( aspects.get( 0 ) );
236-
} );
234+
return getSingleAspect( versionedModel, aspect -> true );
237235
}
238236

239237
/**
@@ -252,4 +250,40 @@ public static Aspect getSingleAspectUnchecked( final VersionedModel versionedMod
252250
throw new AspectLoadingException( cause );
253251
} );
254252
}
253+
254+
/**
255+
* Similar to {@link #getSingleAspect(VersionedModel)}, except that a predicate can be provided to select which of potentially
256+
* multiple aspects should be selected
257+
* @param versionedModel the RDF model reprensentation of the Aspect model
258+
* @param selector the predicate to select an Aspect
259+
* @return the selected Aspect, or a failure if 0 or more than 1 matching Aspects were found
260+
*/
261+
public static Try<Aspect> getSingleAspect( final VersionedModel versionedModel, final Predicate<Aspect> selector ) {
262+
return getAspects( versionedModel ).flatMap( allAspects -> {
263+
final List<Aspect> aspects = allAspects.stream().filter( selector::test ).toList();
264+
return switch ( aspects.size() ) {
265+
case 1 -> Try.success( aspects.iterator().next() );
266+
case 0 -> Try.failure( new InvalidRootElementCountException( "No Aspects were found in the model" ) );
267+
default -> Try.failure( new AspectLoadingException( "Multiple Aspects were found in the resolved model" ) );
268+
};
269+
} );
270+
}
271+
272+
/**
273+
* Convenience method to create an {@link AspectContext} directly from a model file. This method makes the following assumptions:
274+
* <ul>
275+
* <li>The model file is located in a directory structure as required by the {@link io.openmanufacturing.sds.aspectmodel.resolver.FileSystemStrategy}</li>
276+
* <li>The closure of the loaded model contains exactly one Aspect</li>
277+
* <li>The Aspect has the same name as the file's basename</li>
278+
* </ul>
279+
* The method is intended for use in tests and comparable use cases, not as a general replacement for loading Aspect Models, since it does not
280+
* handle model files with less or more than one Aspect.
281+
* @param input the model file
282+
* @return the loaded Aspect Context
283+
*/
284+
public static Try<AspectContext> getAspectContext( final File input ) {
285+
return AspectModelResolver.loadAndResolveModel( input ).flatMap( versionedModel ->
286+
getSingleAspect( versionedModel, aspect -> aspect.getName().equals( input.getName() ) )
287+
.map( aspect -> new AspectContext( versionedModel, aspect ) ) );
288+
}
255289
}

core/sds-aspect-meta-model-java/src/main/java/io/openmanufacturing/sds/metamodel/loader/MetaModelBaseAttributes.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,15 +203,15 @@ private static String getSyntheticName( final Resource modelElement, final Model
203203
throw new AspectLoadingException( "At least one anonymous node in the model does not have a parent with a regular name." );
204204
}
205205
final String parentModelElementUri = namedParent.getURI();
206-
final String parentModelElementName = metaModelResourceResolver.getAspectModelUrn( parentModelElementUri )
206+
final String parentModelElementName = AspectModelUrn.from( parentModelElementUri )
207207
.toJavaOptional()
208208
.map( AspectModelUrn::getName )
209209
.map( StringUtils::capitalize )
210210
.orElse( "" );
211211

212212
final Resource modelElementType = getModelElementType( modelElement, bamm );
213213
final String modelElementTypeUri = modelElementType.getURI();
214-
final String modelElementTypeName = metaModelResourceResolver.getAspectModelUrn( modelElementTypeUri )
214+
final String modelElementTypeName = AspectModelUrn.from( modelElementTypeUri )
215215
.toJavaOptional()
216216
.map( AspectModelUrn::getName )
217217
.orElse( "" );

core/sds-aspect-meta-model-resolver/src/main/java/io/openmanufacturing/sds/aspectmodel/resolver/services/SdsAspectMetaModelResourceResolver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ public Set<VersionNumber> getUsedMetaModelVersions( final Model model ) {
220220
.map( RDFNode::asResource )
221221
.map( Resource::getURI )
222222
.filter( uri -> uri.startsWith( bammUrnStart ) )
223-
.flatMap( uri -> getAspectModelUrn( uri ).toJavaStream() )
223+
.flatMap( uri -> AspectModelUrn.from( uri ).toJavaStream() )
224224
.filter( urn -> (urn.getElementType().equals( ElementType.META_MODEL ) || urn.getElementType().equals( ElementType.CHARACTERISTIC )) )
225225
.map( AspectModelUrn::getVersion )
226226
.map( VersionNumber::parse )

core/sds-aspect-meta-model-resolver/src/test/java/io/openmanufacturing/sds/aspectmodel/resolver/services/SdsAspectMetaModelResourceResolverTest.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
import org.junit.jupiter.params.provider.MethodSource;
2828

2929
import io.openmanufacturing.sds.aspectmetamodel.KnownVersion;
30-
import io.openmanufacturing.sds.aspectmodel.VersionNumber;
3130
import io.openmanufacturing.sds.aspectmodel.MissingMetaModelVersionException;
31+
import io.openmanufacturing.sds.aspectmodel.VersionNumber;
3232
import io.openmanufacturing.sds.aspectmodel.urn.AspectModelUrn;
3333
import io.openmanufacturing.sds.test.MetaModelVersions;
3434
import io.vavr.control.Try;
@@ -82,20 +82,18 @@ public void testGetBammVersionInvalidMetaModelUrnElementExpectFailure( final Kno
8282
@ParameterizedTest
8383
@MethodSource( "allVersions" )
8484
public void testGetAspectModelUrnExpectSuccess( final KnownVersion metaModelVersion ) {
85-
final Try<AspectModelUrn> aspectModelUrn = aspectMetaModelResourceResolver.getAspectModelUrn(
85+
final Try<AspectModelUrn> aspectModelUrn = AspectModelUrn.from(
8686
"urn:bamm:io.openmanufacturing:meta-model:" + metaModelVersion.toVersionString() + "#Aspect" );
8787
assertThat( aspectModelUrn ).isSuccess();
8888
org.assertj.core.api.Assertions.assertThat( aspectModelUrn.get().getUrn().toString() )
89-
.isEqualTo( "urn:bamm:io.openmanufacturing:meta-model:" + metaModelVersion
90-
.toVersionString() + "#Aspect" );
89+
.isEqualTo( "urn:bamm:io.openmanufacturing:meta-model:" + metaModelVersion
90+
.toVersionString() + "#Aspect" );
9191
}
9292

9393
@ParameterizedTest
9494
@MethodSource( "allVersions" )
9595
public void testGetAspectModelUrnInvalidUrnExpectFailure( final KnownVersion metaModelVersion ) {
96-
final Try<AspectModelUrn> aspectModelUrn = aspectMetaModelResourceResolver
97-
.getAspectModelUrn(
98-
"urn:foo:io.openmanufacturing:meta-model:" + metaModelVersion.toVersionString() );
96+
final Try<AspectModelUrn> aspectModelUrn = AspectModelUrn.from( "urn:foo:io.openmanufacturing:meta-model:" + metaModelVersion.toVersionString() );
9997
assertThat( aspectModelUrn ).isFailure();
10098
}
10199

@@ -120,7 +118,7 @@ public void testLoadMetaModelExpectSuccess( final KnownVersion metaModelVersion
120118
final Model model = aspectMetaModelResourceResolver.loadMetaModel( metaModelVersion ).get();
121119

122120
org.assertj.core.api.Assertions.assertThat( model.contains( ResourceFactory.createResource(
123-
"urn:bamm:io.openmanufacturing:meta-model:" + metaModelVersion.toVersionString() + "#value" ),
121+
"urn:bamm:io.openmanufacturing:meta-model:" + metaModelVersion.toVersionString() + "#value" ),
124122
RDF.type, (RDFNode) null ) ).isTrue();
125123
}
126124
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ private AssetAdministrationShellEnvironment getAssetAdministrationShellFromAspec
264264

265265
private Aspect loadAspect( final TestAspect testAspect ) {
266266
final VersionedModel model = TestResources.getModel( testAspect, KnownVersion.getLatest() ).get();
267-
return AspectModelLoader.fromVersionedModelUnchecked( model );
267+
return AspectModelLoader.getSingleAspectUnchecked( model );
268268
}
269269

270270
private AssetAdministrationShellEnvironment loadAASX( final ByteArrayOutputStream byteStream ) throws DeserializationException {

core/sds-aspect-model-document-generators/src/main/java/io/openmanufacturing/sds/aspectmodel/generator/AspectModelHelper.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import java.util.Set;
2222
import java.util.stream.Collectors;
2323

24-
import io.openmanufacturing.sds.aspectmodel.resolver.services.VersionedModel;
24+
import io.openmanufacturing.sds.aspectmetamodel.KnownVersion;
2525
import io.openmanufacturing.sds.metamodel.Aspect;
2626
import io.openmanufacturing.sds.metamodel.ModelElement;
2727
import io.openmanufacturing.sds.metamodel.ComplexType;
@@ -35,14 +35,14 @@
3535
import io.openmanufacturing.sds.metamodel.visitor.AspectStreamTraversalVisitor;
3636

3737
public class AspectModelHelper {
38-
private final VersionedModel modelVersion;
38+
private final KnownVersion metaModelVersion;
3939

40-
public AspectModelHelper( final VersionedModel modelVersion ) {
41-
this.modelVersion = modelVersion;
40+
public AspectModelHelper( final KnownVersion metaModelVersion ) {
41+
this.metaModelVersion = metaModelVersion;
4242
}
4343

44-
public VersionedModel getModelVersion() {
45-
return modelVersion;
44+
public KnownVersion getMetaModelVersion() {
45+
return metaModelVersion;
4646
}
4747

4848
public static List<Property> sortPropertiesByPreferredName( final List<Property> properties, final Locale locale ) {

core/sds-aspect-model-document-generators/src/main/java/io/openmanufacturing/sds/aspectmodel/generator/docu/AspectModelDocumentationGenerator.java

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,10 @@
4040
import io.openmanufacturing.sds.aspectmodel.generator.LanguageCollector;
4141
import io.openmanufacturing.sds.aspectmodel.generator.TemplateEngine;
4242
import io.openmanufacturing.sds.aspectmodel.generator.diagram.AspectModelDiagramGenerator;
43-
import io.openmanufacturing.sds.aspectmodel.resolver.services.VersionedModel;
4443
import io.openmanufacturing.sds.metamodel.Aspect;
44+
import io.openmanufacturing.sds.metamodel.AspectContext;
4545
import io.openmanufacturing.sds.metamodel.NamedElement;
4646
import io.openmanufacturing.sds.metamodel.Scalar;
47-
import io.openmanufacturing.sds.metamodel.loader.AspectModelLoader;
4847
import io.openmanufacturing.sds.metamodel.visitor.AspectStreamTraversalVisitor;
4948

5049
/**
@@ -76,17 +75,15 @@ public String getArtifactFilename( final String artifactName ) {
7675
}
7776
}
7877

79-
private final Aspect aspect;
80-
private final VersionedModel versionedModel;
78+
private final AspectContext context;
8179
private Locale selectedLanguage = null;
8280

83-
public AspectModelDocumentationGenerator( final VersionedModel versionedModel ) {
84-
this.versionedModel = versionedModel;
85-
aspect = AspectModelLoader.fromVersionedModelUnchecked( versionedModel );
81+
public AspectModelDocumentationGenerator( final AspectContext context ) {
82+
this.context = context;
8683
}
8784

88-
public AspectModelDocumentationGenerator( final String language, final VersionedModel versionedModel ) {
89-
this( versionedModel );
85+
public AspectModelDocumentationGenerator( final String language, final AspectContext context ) {
86+
this( context );
9087
selectedLanguage = Locale.forLanguageTag( language );
9188
}
9289

@@ -160,7 +157,7 @@ private void generateInternal( final Function<String, OutputStream> nameMapper,
160157
}
161158

162159
private void generateHtmlDocu( final Function<String, OutputStream> nameMapper, final Format format, final Locale desiredLanguage ) {
163-
final Set<Locale> languagesInModel = LanguageCollector.collectUsedLanguages( aspect );
160+
final Set<Locale> languagesInModel = LanguageCollector.collectUsedLanguages( context.aspect() );
164161
if ( !languagesInModel.contains( desiredLanguage ) ) {
165162
throw new RuntimeException( String.format( "The model does not contain the desired language: %s.", desiredLanguage.toString() ) );
166163
}
@@ -169,15 +166,15 @@ private void generateHtmlDocu( final Function<String, OutputStream> nameMapper,
169166
}
170167

171168
private void generateHtmlDocu( final Function<String, OutputStream> nameMapper, final Format format ) {
172-
final Set<Locale> languagesInModel = LanguageCollector.collectUsedLanguages( aspect );
169+
final Set<Locale> languagesInModel = LanguageCollector.collectUsedLanguages( context.aspect() );
173170
final Set<Locale> languages = languagesInModel.isEmpty() ? Set.of( Locale.ENGLISH ) : languagesInModel;
174171
generateHtmlDocu( nameMapper, format, languages );
175172
}
176173

177174
private void generateHtmlDocu( final Function<String, OutputStream> nameMapper, final Format format, final Set<Locale> languages ) {
178-
final Map<String, Object> context = new HashMap<>();
179-
context.put( "aspectModel", aspect );
180-
context.put( "aspectModelHelper", new AspectModelHelper( versionedModel ) );
175+
final Map<String, Object> configuration = new HashMap<>();
176+
configuration.put( "aspectModel", context.aspect() );
177+
configuration.put( "aspectModelHelper", new AspectModelHelper( context.aspect().getMetaModelVersion() ) );
181178

182179
final Properties engineConfiguration = new Properties();
183180
engineConfiguration.put( "file.resource.loader.path", ".," + DOCU_TEMPLATE_ROOT_DIR + "/html" );
@@ -194,13 +191,13 @@ private void generateHtmlDocu( final Function<String, OutputStream> nameMapper,
194191
final Predicate<Locale> byLanguage = locale -> selectedLanguage == null || locale.getLanguage().equals( selectedLanguage.getLanguage() );
195192
languages.stream().filter( byLanguage ).forEach( language -> {
196193

197-
logMissingTranslations( aspect, language );
194+
logMissingTranslations( context.aspect(), language );
198195

199-
context.put( "i18n", new I18nLanguageBundle( language ) );
200-
context.put( "Scalar", Scalar.class );
201-
final TemplateEngine templateEngine = new TemplateEngine( context, engineConfiguration );
196+
configuration.put( "i18n", new I18nLanguageBundle( language ) );
197+
configuration.put( "Scalar", Scalar.class );
198+
final TemplateEngine templateEngine = new TemplateEngine( configuration, engineConfiguration );
202199

203-
final String artifactName = getArtifactName( aspect, language );
200+
final String artifactName = getArtifactName( context.aspect(), language );
204201
if ( nameMapper instanceof BufferingNameMapper ) {
205202
((BufferingNameMapper) nameMapper).setLanguageForArtifact( artifactName, language );
206203
}
@@ -244,7 +241,7 @@ private String byteArrayOutputStreamToString( final ByteArrayOutputStream output
244241
}
245242

246243
private String insertAspectModelDiagram( final String html, final Locale language ) throws IOException {
247-
final AspectModelDiagramGenerator diagramGenerator = new AspectModelDiagramGenerator( versionedModel );
244+
final AspectModelDiagramGenerator diagramGenerator = new AspectModelDiagramGenerator( context.rdfModel() );
248245
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
249246
diagramGenerator.generateDiagram( AspectModelDiagramGenerator.Format.SVG, language, buffer );
250247
final String encodedImage = "data:image/svg+xml;base64," +

core/sds-aspect-model-document-generators/src/main/java/io/openmanufacturing/sds/aspectmodel/generator/json/AspectModelJsonPayloadGenerator.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@
5858
import io.openmanufacturing.sds.aspectmodel.generator.NumericTypeTraits;
5959
import io.openmanufacturing.sds.aspectmodel.jackson.AspectModelJacksonModule;
6060
import io.openmanufacturing.sds.aspectmodel.resolver.services.DataType;
61-
import io.openmanufacturing.sds.aspectmodel.resolver.services.VersionedModel;
6261
import io.openmanufacturing.sds.aspectmodel.vocabulary.BAMM;
6362
import io.openmanufacturing.sds.characteristic.Collection;
6463
import io.openmanufacturing.sds.characteristic.Either;
@@ -70,6 +69,7 @@
7069
import io.openmanufacturing.sds.constraint.RegularExpressionConstraint;
7170
import io.openmanufacturing.sds.metamodel.AbstractEntity;
7271
import io.openmanufacturing.sds.metamodel.Aspect;
72+
import io.openmanufacturing.sds.metamodel.AspectContext;
7373
import io.openmanufacturing.sds.metamodel.Characteristic;
7474
import io.openmanufacturing.sds.metamodel.ComplexType;
7575
import io.openmanufacturing.sds.metamodel.Constraint;
@@ -81,7 +81,6 @@
8181
import io.openmanufacturing.sds.metamodel.Value;
8282
import io.openmanufacturing.sds.metamodel.datatypes.Curie;
8383
import io.openmanufacturing.sds.metamodel.impl.BoundDefinition;
84-
import io.openmanufacturing.sds.metamodel.loader.AspectModelLoader;
8584

8685
public class AspectModelJsonPayloadGenerator extends AbstractGenerator {
8786
/**
@@ -102,8 +101,8 @@ public AspectModelJsonPayloadGenerator( final Aspect aspect ) {
102101
this( aspect, new BAMM( aspect.getMetaModelVersion() ), new Random() );
103102
}
104103

105-
public AspectModelJsonPayloadGenerator( final VersionedModel versionedModel ) {
106-
this( AspectModelLoader.fromVersionedModelUnchecked( versionedModel ) );
104+
public AspectModelJsonPayloadGenerator( final AspectContext context ) {
105+
this( context.aspect() );
107106
}
108107

109108
public AspectModelJsonPayloadGenerator( final Aspect aspect, final Random randomStrategy ) {

0 commit comments

Comments
 (0)