Skip to content

Commit 53bc3f0

Browse files
authored
Merge pull request #250 from bci-oss/#249_validate_namespace_of_custom_units
[#249] Validate namespace of units not specified in the unit catalog
2 parents ccc5b46 + 3de8910 commit 53bc3f0

File tree

7 files changed

+174
-10
lines changed

7 files changed

+174
-10
lines changed

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@
1313

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

16+
import java.util.ArrayList;
1617
import java.util.List;
1718
import java.util.Optional;
1819
import java.util.Set;
1920

2021
import org.apache.jena.rdf.model.Model;
22+
import org.apache.jena.rdf.model.Resource;
23+
import org.apache.jena.rdf.model.Statement;
2124
import org.apache.jena.rdf.model.StmtIterator;
2225
import org.apache.jena.vocabulary.RDF;
2326
import org.slf4j.Logger;
@@ -29,6 +32,7 @@
2932
import io.openmanufacturing.sds.aspectmodel.UnsupportedVersionException;
3033
import io.openmanufacturing.sds.aspectmodel.resolver.AspectModelResolver;
3134
import io.openmanufacturing.sds.aspectmodel.resolver.exceptions.InvalidModelException;
35+
import io.openmanufacturing.sds.aspectmodel.resolver.exceptions.InvalidNamespaceException;
3236
import io.openmanufacturing.sds.aspectmodel.resolver.exceptions.InvalidRootElementCountException;
3337
import io.openmanufacturing.sds.aspectmodel.resolver.exceptions.InvalidVersionException;
3438
import io.openmanufacturing.sds.aspectmodel.resolver.services.VersionedModel;
@@ -73,9 +77,12 @@ private static Try<Aspect> load( final VersionedModel versionedModel ) {
7377
final BAMM bamm = new BAMM( metaModelVersion.get() );
7478
final StmtIterator iterator = model.listStatements( null, RDF.type, bamm.Aspect() );
7579
try {
80+
validateNamespaceOfCustomUnits( bamm, versionedModel.getRawModel() );
7681
final ModelElementFactory modelElementFactory = new ModelElementFactory( metaModelVersion.get(), model );
7782
final Aspect aspect = modelElementFactory.create( Aspect.class, iterator.nextStatement().getSubject() );
7883
return Try.success( aspect );
84+
} catch ( final InvalidNamespaceException exception ) {
85+
return Try.failure( exception );
7986
} catch ( final RuntimeException exception ) {
8087
return Try.failure( new InvalidModelException( "Could not load Aspect model, please make sure the model is valid", exception ) );
8188
}
@@ -114,6 +121,21 @@ public static Try<Aspect> fromVersionedModel( final VersionedModel versionedMode
114121
} );
115122
}
116123

124+
private static void validateNamespaceOfCustomUnits( final BAMM bamm, final Model rawModel ) {
125+
final List<String> customUnitsWithBammNamespace = new ArrayList<>();
126+
rawModel.listStatements( null, RDF.type, bamm.Unit() )
127+
.mapWith( Statement::getSubject )
128+
.filterKeep( subject -> subject.getNameSpace().equals( bamm.getNamespace() ) )
129+
.mapWith( Resource::getLocalName )
130+
.forEach( customUnitsWithBammNamespace::add );
131+
132+
if ( !customUnitsWithBammNamespace.isEmpty() ) {
133+
throw new InvalidNamespaceException(
134+
String.format( "Aspect model contains unit(s) %s not specified in the unit catalog but referred with bamm namespace",
135+
customUnitsWithBammNamespace ) );
136+
}
137+
}
138+
117139
private static List<Try<AspectModelUrn>> getUrns( final VersionedModel migratedModel, final KnownVersion version ) {
118140
final BAMM bamm = new BAMM( version );
119141
return migratedModel.getModel().listStatements( null, RDF.type, bamm.Aspect() ).mapWith( statement ->
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright (c) 2022 Robert Bosch Manufacturing Solutions GmbH
3+
*
4+
* See the AUTHORS file(s) distributed with this work for additional
5+
* information regarding authorship.
6+
*
7+
* This Source Code Form is subject to the terms of the Mozilla Public
8+
* License, v. 2.0. If a copy of the MPL was not distributed with this
9+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
10+
*
11+
* SPDX-License-Identifier: MPL-2.0
12+
*/
13+
package io.openmanufacturing.sds.aspectmodel.resolver.exceptions;
14+
15+
import java.io.Serial;
16+
17+
/**
18+
* An exception indicating that usage of the given namespace is invalid.
19+
*/
20+
public final class InvalidNamespaceException extends RuntimeException {
21+
22+
@Serial
23+
private static final long serialVersionUID = -1075433954587137319L;
24+
25+
/**
26+
* Creates an instance of the exception.
27+
* @param message The detailed message of the problem.
28+
* @param cause The cause of the problem.
29+
*/
30+
public InvalidNamespaceException( final String message, final Throwable cause ) {
31+
super( message, cause );
32+
}
33+
34+
/**
35+
* Creates an instance of the exception.
36+
* @param message The detailed message of the problem.
37+
*/
38+
public InvalidNamespaceException( final String message ) {
39+
super( message );
40+
}
41+
42+
}

core/sds-aspect-model-validator/src/main/java/io/openmanufacturing/sds/aspectmodel/validation/services/AspectModelValidator.java

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import io.openmanufacturing.sds.aspectmetamodel.KnownVersion;
4848
import io.openmanufacturing.sds.aspectmodel.UnsupportedVersionException;
4949
import io.openmanufacturing.sds.aspectmodel.resolver.ModelResolutionException;
50+
import io.openmanufacturing.sds.aspectmodel.resolver.exceptions.InvalidNamespaceException;
5051
import io.openmanufacturing.sds.aspectmodel.resolver.exceptions.InvalidRootElementCountException;
5152
import io.openmanufacturing.sds.aspectmodel.resolver.exceptions.InvalidVersionException;
5253
import io.openmanufacturing.sds.aspectmodel.resolver.exceptions.ParserException;
@@ -193,9 +194,9 @@ public List<Violation> validateElement( final Resource element ) {
193194
/**
194195
* Validates an Aspect Model that is provided as a {@link Try} of a {@link VersionedModel} that can
195196
* contain either a syntactically valid (but semantically invalid) Aspect model, or a
196-
* {@link RiotException} if a parser error occured.
197+
* {@link RiotException} if a parser error occurred.
197198
*
198-
* @param versionedModel The Aspect Model or the corresonding parser error
199+
* @param versionedModel The Aspect Model or the corresponding parser error
199200
* @return Either a {@link ValidationReport.ValidReport} if the model is syntactically correct and conforms to the
200201
* Aspect Meta Model semantics or a {@link ValidationReport.InvalidReport} that provides a number of
201202
* {@link ValidationError}s that describe all validation violations.
@@ -219,13 +220,7 @@ public ValidationReport validate( final Try<VersionedModel> versionedModel ) {
219220
// The SHACL validation succeeded. But to catch false positives, also try to load the model
220221
final Try<Aspect> aspects = AspectModelLoader.fromVersionedModel( model );
221222
if ( aspects.isFailure() && !(aspects.getCause() instanceof InvalidRootElementCountException) ) {
222-
return new ValidationReportBuilder()
223-
.withValidationErrors( List.of( new ValidationError.Processing(
224-
"Validation succeeded, but an error was found while processing the model. "
225-
+ "This indicates an error in the model validation; please consider reporting this issue including the model "
226-
+ "at https://github.com/OpenManufacturingPlatform/sds-bamm-aspect-meta-model/issues -- "
227-
+ buildCauseMessage( aspects.getCause() ) ) ) )
228-
.buildInvalidReport();
223+
return getInvalidReport( aspects.getCause() );
229224
}
230225
return new ValidationReportBuilder().buildValidReport();
231226
}
@@ -240,7 +235,24 @@ public ValidationReport validate( final Try<VersionedModel> versionedModel ) {
240235
).get();
241236
}
242237

243-
private String buildCauseMessage( final Throwable throwable ) {
238+
private static ValidationReport getInvalidReport( final Throwable cause ) {
239+
final String errorMessage;
240+
241+
if ( cause instanceof InvalidNamespaceException ) {
242+
errorMessage = buildCauseMessage( cause );
243+
} else {
244+
errorMessage = "Validation succeeded, but an error was found while processing the model. "
245+
+ "This indicates an error in the model validation; please consider reporting this issue including the model "
246+
+ "at https://github.com/OpenManufacturingPlatform/sds-bamm-aspect-meta-model/issues -- "
247+
+ buildCauseMessage( cause );
248+
}
249+
250+
return new ValidationReportBuilder()
251+
.withValidationErrors( List.of( new ValidationError.Processing( errorMessage ) ) )
252+
.buildInvalidReport();
253+
}
254+
255+
private static String buildCauseMessage( final Throwable throwable ) {
244256
final StringBuilder builder = new StringBuilder();
245257
Throwable t = throwable;
246258
while ( t != null ) {

core/sds-aspect-model-validator/src/test/java/io/openmanufacturing/sds/aspectmodel/validation/services/AspectModelValidatorTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,24 @@ public void testNonTurtleFile( final KnownVersion metaModelVersion ) {
224224
assertThat( violation.message() ).contains( "Not implemented (formulae, graph literals)" );
225225
}
226226

227+
/**
228+
* Verify that validation of the given aspect model containing a unit not specified in the unit catalog
229+
* and referred with bamm namespace fails.
230+
*/
231+
@ParameterizedTest
232+
@MethodSource( value = "allVersions" )
233+
public void testAspectWithBammNamespaceForCustomUnit( final KnownVersion metaModelVersion ) {
234+
final Try<VersionedModel> invalidAspectModel = TestResources
235+
.getModel( InvalidTestAspect.ASPECT_WITH_BAMM_NAMESPACE_FOR_CUSTOM_UNIT, metaModelVersion );
236+
237+
final ValidationReport report = service.validate( invalidAspectModel );
238+
assertThat( report.conforms() ).isFalse();
239+
240+
final Collection<? extends ValidationError> errors = report.getValidationErrors();
241+
assertThat( errors ).hasSize( 1 );
242+
assertThat( report.getValidationErrors().iterator().next() ).isOfAnyClassIn( ValidationError.Processing.class );
243+
}
244+
227245
@ParameterizedTest
228246
@MethodSource( value = "allVersions" )
229247
public void testAspectWithInvalidMetaModelVersion( final KnownVersion metaModelVersion ) {

core/sds-test-aspect-models/src/main/java/io/openmanufacturing/sds/test/InvalidTestAspect.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public enum InvalidTestAspect implements TestModel {
2121
ASPECT_MISSING_PROPERTIES,
2222
ASPECT_WITH_FALSE_POSITIVE_VALIDATION,
2323
ASPECT_WITH_INVALID_VERSION,
24+
ASPECT_WITH_BAMM_NAMESPACE_FOR_CUSTOM_UNIT,
2425
ASPECT_WITH_RECURSIVE_PROPERTY,
2526
INVALID_SYNTAX,
2627
MISSING_ASPECT_DECLARATION,
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Copyright (c) 2022 Robert Bosch Manufacturing Solutions GmbH
2+
#
3+
# See the AUTHORS file(s) distributed with this work for additional
4+
# information regarding authorship.
5+
#
6+
# This Source Code Form is subject to the terms of the Mozilla Public
7+
# License, v. 2.0. If a copy of the MPL was not distributed with this
8+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
9+
#
10+
# SPDX-License-Identifier: MPL-2.0
11+
12+
@prefix : <urn:bamm:io.openmanufacturing:aspect-model:AspectWithBammNamespaceForCustomUnit:1.0.0#> .
13+
@prefix bamm: <urn:bamm:io.openmanufacturing:meta-model:1.0.0#> .
14+
@prefix bamm-c: <urn:bamm:io.openmanufacturing:characteristic:1.0.0#> .
15+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
16+
@prefix unit: <urn:bamm:io.openmanufacturing:unit:1.0.0#> .
17+
18+
:AspectWithBammNamespaceForCustomUnit a bamm:Aspect ;
19+
bamm:name "AspectWithCustomUnit" ;
20+
bamm:properties ( :testProperty ) ;
21+
bamm:operations ( ) .
22+
23+
:testProperty a bamm:Property ;
24+
bamm:name "testProperty" ;
25+
bamm:characteristic :TestQuantifiable .
26+
27+
:TestQuantifiable a bamm-c:Quantifiable ;
28+
bamm:name "TestQuantifiable" ;
29+
bamm:dataType xsd:int ;
30+
bamm-c:unit bamm:normLitrePerMinute .
31+
32+
bamm:normLitrePerMinute a unit:Unit ;
33+
bamm:name "normLitrePerMinute" ;
34+
bamm:preferredName "norm litre per minute"@en ;
35+
unit:quantityKind unit:volumeFlowRate ;
36+
unit:symbol "nl/min" .
37+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Copyright (c) 2022 Robert Bosch Manufacturing Solutions GmbH
2+
#
3+
# See the AUTHORS file(s) distributed with this work for additional
4+
# information regarding authorship.
5+
#
6+
# This Source Code Form is subject to the terms of the Mozilla Public
7+
# License, v. 2.0. If a copy of the MPL was not distributed with this
8+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
9+
#
10+
# SPDX-License-Identifier: MPL-2.0
11+
12+
@prefix : <urn:bamm:io.openmanufacturing.test:1.0.0#> .
13+
@prefix bamm: <urn:bamm:io.openmanufacturing:meta-model:2.0.0#> .
14+
@prefix bamm-c: <urn:bamm:io.openmanufacturing:characteristic:2.0.0#> .
15+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
16+
@prefix unit: <urn:bamm:io.openmanufacturing:unit:2.0.0#> .
17+
18+
:AspectWithBammNamespaceForCustomUnit a bamm:Aspect ;
19+
bamm:properties ( :testProperty ) ;
20+
bamm:operations ( ) .
21+
22+
:testProperty a bamm:Property ;
23+
bamm:characteristic :TestQuantifiable .
24+
25+
:TestQuantifiable a bamm-c:Quantifiable ;
26+
bamm:dataType xsd:int ;
27+
bamm-c:unit bamm:normLitrePerMinute .
28+
29+
bamm:normLitrePerMinute a bamm:Unit ;
30+
bamm:preferredName "norm litre per minute"@en ;
31+
bamm:quantityKind unit:volumeFlowRate ;
32+
bamm:symbol "nl/min" .

0 commit comments

Comments
 (0)