Skip to content

Commit 003b63e

Browse files
committed
Merge branch 'main' into bugfix/fix-unit-tests
# Conflicts: # tools/bamm-cli/src/main/java/io/openmanufacturing/sds/BammCli.java
2 parents 4c9b19d + 39ceb35 commit 003b63e

File tree

117 files changed

+1678
-870
lines changed

Some content is hidden

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

117 files changed

+1678
-870
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@ build
144144
/core/sds-aspect-meta-model-legacy*/src
145145
/core/sds-aspect-meta-model-legacy*/target
146146

147+
# Directories where classes are generated during unit tests
148+
core/sds-aspect-model-java-generator/src/test/java/io/openmanufacturing/test
149+
core/sds-aspect-model-jackson/src/main/java/io/openmanufacturing/test
150+
147151
# jqwik
148152
.jqwik-database
149153

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

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Copyright (c) 2021 Robert Bosch Manufacturing Solutions GmbH
33
*
44
* See the AUTHORS file(s) distributed with this work for additional
5-
* information regarding authorship.
5+
* information regarding authorship.
66
*
77
* This Source Code Form is subject to the terms of the Mozilla Public
88
* License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -24,9 +24,9 @@
2424
public interface Property extends Base, IsDescribed {
2525

2626
/**
27-
* @return the {@link Characteristic} describing this Property.
27+
* @return the {@link Characteristic} describing this Property. This can be empty when the Property is abstract.
2828
*/
29-
Characteristic getCharacteristic();
29+
Optional<Characteristic> getCharacteristic();
3030

3131
/**
3232
* @return an {@link Optional} which may contain an example value for the Property. The type of the value is
@@ -50,7 +50,8 @@ default boolean isOptional() {
5050
* @return a {@link boolean} which determines whether the Property is included in the runtime data of an Aspect.
5151
* By default Properties are included in the runtime data.
5252
*
53-
* @see <a href="https://openmanufacturingplatform.github.io/sds-bamm-aspect-meta-model/bamm-specification/snapshot/modeling-guidelines.html#declaring-enumerations">BAMM Aspect Meta Model
53+
* @see
54+
* <a href="https://openmanufacturingplatform.github.io/sds-bamm-aspect-meta-model/bamm-specification/snapshot/modeling-guidelines.html#declaring-enumerations">BAMM Aspect Meta Model
5455
* Specification - Declaring Enumerations</a>
5556
* @since BAMM 1.0.0
5657
*/
@@ -59,22 +60,40 @@ default boolean isNotInPayload() {
5960
}
6061

6162
/**
62-
* Returns the Property's unconstrained Characteristic
63+
* Returns true if the Property is abstract
6364
*
64-
* @return The Property's Characteristic without Constraints
65+
* @return
6566
*/
66-
default Characteristic getEffectiveCharacteristic() {
67-
Characteristic characteristic = getCharacteristic();
68-
while ( characteristic instanceof Trait ) {
69-
characteristic = ((Trait) characteristic).getBaseCharacteristic();
70-
}
71-
return characteristic;
67+
default boolean isAbstract() {
68+
return false;
69+
}
70+
71+
/**
72+
* Returns the Property's unconstrained Characteristic.
73+
* This is undefined when the Property is abstract
74+
*
75+
* @return The Property's Characteristic without Constraints, or empty, if {@link #isAbstract()} is true
76+
*/
77+
default Optional<Characteristic> getEffectiveCharacteristic() {
78+
return getCharacteristic().map( characteristic -> {
79+
while ( characteristic.is( Trait.class ) ) {
80+
characteristic = characteristic.as( Trait.class ).getBaseCharacteristic();
81+
}
82+
return characteristic;
83+
} );
7284
}
7385

7486
/**
7587
* @return the type for the Property.
7688
*/
7789
default Optional<Type> getDataType() {
78-
return getEffectiveCharacteristic().getDataType();
90+
return getEffectiveCharacteristic().flatMap( Characteristic::getDataType );
91+
}
92+
93+
/**
94+
* @return the Property that is extended by this Property, if present
95+
*/
96+
default Optional<Property> getExtends() {
97+
return Optional.empty();
7998
}
8099
}

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

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,30 @@
2323
import io.openmanufacturing.sds.metamodel.visitor.AspectVisitor;
2424

2525
public class DefaultProperty extends BaseImpl implements Property {
26-
private final Characteristic characteristic;
26+
private final Optional<Characteristic> characteristic;
2727
private final Optional<ScalarValue> exampleValue;
2828
private final boolean optional;
2929
private final boolean notInPayload;
3030
private final Optional<String> payloadName;
31+
private final boolean isAbstract;
32+
private final Optional<Property> extends_;
3133

3234
public DefaultProperty( final MetaModelBaseAttributes metaModelBaseAttributes,
33-
final Characteristic characteristic,
35+
final Optional<Characteristic> characteristic,
3436
final Optional<ScalarValue> exampleValue,
3537
final boolean optional,
3638
final boolean notInPayload,
37-
final Optional<String> payloadName ) {
39+
final Optional<String> payloadName,
40+
final boolean isAbstract,
41+
final Optional<Property> extends_ ) {
3842
super( metaModelBaseAttributes );
3943
this.characteristic = characteristic;
4044
this.exampleValue = exampleValue;
4145
this.optional = optional;
4246
this.notInPayload = notInPayload;
4347
this.payloadName = payloadName;
48+
this.isAbstract = isAbstract;
49+
this.extends_ = extends_;
4450
}
4551

4652
/**
@@ -49,7 +55,7 @@ public DefaultProperty( final MetaModelBaseAttributes metaModelBaseAttributes,
4955
* @return the characteristic.
5056
*/
5157
@Override
52-
public Characteristic getCharacteristic() {
58+
public Optional<Characteristic> getCharacteristic() {
5359
return characteristic;
5460
}
5561

@@ -87,6 +93,16 @@ public String getPayloadName() {
8793
return payloadName.orElseGet( this::getName );
8894
}
8995

96+
@Override
97+
public boolean isAbstract() {
98+
return isAbstract;
99+
}
100+
101+
@Override
102+
public Optional<Property> getExtends() {
103+
return extends_;
104+
}
105+
90106
/**
91107
* Accepts an Aspect visitor
92108
*
@@ -106,6 +122,8 @@ public String toString() {
106122
.add( "exampleValue=" + exampleValue )
107123
.add( "optional=" + optional )
108124
.add( "notInPayload=" + notInPayload )
125+
.add( "isAbstract=" + isAbstract )
126+
.add( "extends=" + extends_ )
109127
.toString();
110128
}
111129

@@ -124,12 +142,14 @@ public boolean equals( final Object o ) {
124142
final DefaultProperty that = (DefaultProperty) o;
125143
return optional == that.optional &&
126144
notInPayload == that.notInPayload &&
145+
isAbstract == that.isAbstract &&
127146
Objects.equals( characteristic, that.characteristic ) &&
128-
Objects.equals( exampleValue, that.exampleValue );
147+
Objects.equals( exampleValue, that.exampleValue ) &&
148+
Objects.equals( extends_, that.extends_ );
129149
}
130150

131151
@Override
132152
public int hashCode() {
133-
return Objects.hash( super.hashCode(), characteristic, exampleValue, optional, notInPayload );
153+
return Objects.hash( super.hashCode(), characteristic, exampleValue, optional, notInPayload, isAbstract, extends_ );
134154
}
135155
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
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+
14+
package io.openmanufacturing.sds.metamodel.loader;
15+
16+
import java.util.ArrayList;
17+
import java.util.HashSet;
18+
import java.util.List;
19+
import java.util.Optional;
20+
import java.util.Set;
21+
22+
import org.apache.jena.rdf.model.Property;
23+
import org.apache.jena.rdf.model.RDFList;
24+
import org.apache.jena.rdf.model.RDFNode;
25+
import org.apache.jena.rdf.model.Resource;
26+
import org.apache.jena.rdf.model.Statement;
27+
import org.apache.jena.rdf.model.StmtIterator;
28+
import org.apache.jena.vocabulary.RDF;
29+
30+
import io.openmanufacturing.sds.aspectmodel.vocabulary.BAMM;
31+
32+
/**
33+
* This class provides utilities to retrieve attribute values from model elements, e.g., a bamm:Property's bamm:characteristic.
34+
* It knows how to handle:
35+
* <ul>
36+
* <li>optionality (e.g., bamm-c:upperBoundDefinition on a bamm-c:RangeConstraint is not mandatory)</li>
37+
* <li>n-ary attributes (e.g., bamm:preferredName and bamm:description can appear multiple times)</li>
38+
* <li>abstract Properties (i.e., a bnode with bamm:extends used as a Property)</li>
39+
* <li>Property references (i.e., a bnode with bamm:property used as a Property)</li>
40+
* </ul>
41+
*/
42+
public class AttributeValueRetriever {
43+
protected final BAMM bamm;
44+
45+
public AttributeValueRetriever( final BAMM bamm ) {
46+
this.bamm = bamm;
47+
}
48+
49+
/**
50+
* Returns the value of an attribute on a model element (or its super elements)
51+
*
52+
* @param modelElement the model element
53+
* @param attribute the given attribute
54+
* @return the statement asserting the value
55+
* @throws AspectLoadingException when the attribute is not present or is present multiple times, or if bamm:extends is used wrong
56+
*/
57+
protected Statement attributeValue( final Resource modelElement, final Property attribute ) {
58+
return optionalAttributeValue( modelElement, attribute ).orElseThrow(
59+
() -> new AspectLoadingException( "Missing attribute " + attribute + " on " + modelElement ) );
60+
}
61+
62+
/**
63+
* Returns the optional value of an attribute on a model element (or its super elements)
64+
*
65+
* @param modelElement the model element
66+
* @param attribute the given attribute
67+
* @return the optional statement asserting the value
68+
*/
69+
protected Optional<Statement> optionalAttributeValue( final Resource modelElement, final Property attribute ) {
70+
return attributeValues( modelElement, attribute ).stream().sequential().findFirst();
71+
}
72+
73+
private boolean isRdfList( final Resource resource ) {
74+
return resource.isAnon() && (resource.hasProperty( RDF.rest ) || resource.equals( RDF.nil ));
75+
}
76+
77+
/**
78+
* Returns the values of n-ary attributes on a model element (or its super elements), or if a given attribute is an rdf:List, the list elements.
79+
* The list will be ordered by precedence, e.g., if a Property is present on both the current element and it's superelement, the assertion on the
80+
* current element will be on a lower list index.
81+
* Duplicate attribute assertions are removed and only the assertion with the highest precedence will be returned (bottom-most in the inheritance tree),
82+
* this includes multiple assertions for the same attribute with rdf:langString values with the same language tag. For example:
83+
*
84+
* :SuperEntity a bamm:AbstractEntity ;
85+
* bamm:description "I'm abstract"@en ;
86+
* bamm:description "Ich bin abstrakt"@de ;
87+
* bamm:properties () .
88+
*
89+
* :MyEntity a bamm:Entity ;
90+
* bamm:extends :SuperEntity ;
91+
* bamm:description "I'm concrete"@en ;
92+
* bamm:properties () .
93+
*
94+
* Here, attributeValues( :MyEntity, bamm:description ) will return:
95+
* List( Statement( :MyEntity bamm:description "I'm contrete"@en ),
96+
* Statement( :SuperEntity bamm:description "Ich bin abstrakt"@de ) )
97+
*
98+
* The attribute that is overridden with a new value takes precedence, the one that is not overridden is inherited.
99+
*
100+
* @param modelElement the model element
101+
* @param attribute the given attribute
102+
* @return the list of statements asserting values for the attribute
103+
*/
104+
protected List<Statement> attributeValues( final Resource modelElement, final Property attribute ) {
105+
// Attribute values defined directly on the resource go into the result
106+
final List<Statement> result = new ArrayList<>();
107+
for ( final StmtIterator iterator = modelElement.listProperties( attribute ); iterator.hasNext(); ) {
108+
final Statement statement = iterator.next();
109+
// If the value happens to be an rdf:List, unroll the list
110+
if ( statement.getObject().isResource() && isRdfList( statement.getObject().asResource() ) ) {
111+
statement.getObject().as( RDFList.class )
112+
.mapWith( listValue -> modelElement.getModel().createStatement( modelElement, attribute, listValue ) )
113+
.forEach( result::add );
114+
} else {
115+
result.add( statement );
116+
}
117+
}
118+
119+
// If the model element is a bnode with bamm:property given, it's a Property reference. Follow it to retrieve the sought-for attribute assertions.
120+
final StmtIterator referenceIterator = modelElement.listProperties( bamm.property() );
121+
if ( referenceIterator.hasNext() ) {
122+
final RDFNode referencedElement = referenceIterator.next().getObject();
123+
if ( !referencedElement.isResource() ) {
124+
throw new AspectLoadingException( "bamm:property on " + modelElement + " must point to a Property" );
125+
}
126+
result.addAll( attributeValues( referencedElement.asResource(), attribute ) );
127+
return result;
128+
}
129+
130+
// If the model element is bamm:extends another element, retrieve attribute assertions from this supertype as well.
131+
final StmtIterator extendsIterator = modelElement.listProperties( bamm._extends() );
132+
if ( extendsIterator.hasNext() ) {
133+
final RDFNode superElementNode = extendsIterator.next().getObject();
134+
if ( !superElementNode.isResource() ) {
135+
throw new AspectLoadingException( "bamm:extends on " + modelElement + " must point to a valid model element" );
136+
}
137+
result.addAll( attributeValues( superElementNode.asResource(), attribute ) );
138+
}
139+
140+
// Filter duplicate assertions by precedence
141+
final List<Statement> filteredResult = new ArrayList<>();
142+
final Set<String> assertedLanguageTags = new HashSet<>();
143+
for ( final Statement statement : result ) {
144+
if ( statement.getObject().isLiteral() ) {
145+
final String languageTag = statement.getObject().asLiteral().getLanguage();
146+
if ( !languageTag.isEmpty() ) {
147+
if ( assertedLanguageTags.contains( languageTag ) ) {
148+
continue;
149+
}
150+
assertedLanguageTags.add( languageTag );
151+
}
152+
}
153+
filteredResult.add( statement );
154+
}
155+
156+
return filteredResult;
157+
}
158+
}

0 commit comments

Comments
 (0)