Skip to content

Commit

Permalink
Merge pull request #476 from bci-oss/475-allow-loading-custom-quantit…
Browse files Browse the repository at this point in the history
…y-kinds

Fix loading of custom quantity kind definitions
  • Loading branch information
atextor authored Nov 7, 2023
2 parents c615b53 + 074e7f5 commit 007ac12
Show file tree
Hide file tree
Showing 11 changed files with 334 additions and 124 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* 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
*/

package org.eclipse.esmf.metamodel.impl;

import java.util.Objects;
import java.util.StringJoiner;

import org.eclipse.esmf.metamodel.QuantityKind;
import org.eclipse.esmf.metamodel.loader.MetaModelBaseAttributes;
import org.eclipse.esmf.metamodel.visitor.AspectVisitor;

public class DefaultQuantityKind extends ModelElementImpl implements QuantityKind {
private final String label;

public DefaultQuantityKind( final MetaModelBaseAttributes metaModelBaseAttributes, final String label ) {
super( metaModelBaseAttributes );
this.label = label;
}

@Override
public String getLabel() {
return label;
}

@Override
public boolean equals( final Object o ) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
if ( !super.equals( o ) ) {
return false;
}
final DefaultQuantityKind that = (DefaultQuantityKind) o;
return Objects.equals( label, that.label );
}

@Override
public int hashCode() {
return Objects.hash( super.hashCode(), label );
}

/**
* Accepts an Aspect visitor
*
* @param visitor The visitor to accept
* @param <T> The result type of the traversal operation
* @param <C> The context of the visitor traversal
*/
@Override
public <T, C> T accept( final AspectVisitor<T, C> visitor, final C context ) {
return visitor.visitQuantityKind( this, context );
}

@Override
public String toString() {
return new StringJoiner( ", ", DefaultQuantityKind.class.getSimpleName() + "[", "]" )
.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,6 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.Statement;
import org.apache.jena.vocabulary.RDF;
import org.apache.jena.vocabulary.RDFS;
import org.eclipse.esmf.aspectmodel.urn.AspectModelUrn;
import org.eclipse.esmf.aspectmodel.vocabulary.SAMM;
import org.eclipse.esmf.aspectmodel.vocabulary.SAMMC;
Expand All @@ -41,6 +35,7 @@
import org.eclipse.esmf.metamodel.QuantityKinds;
import org.eclipse.esmf.metamodel.Unit;
import org.eclipse.esmf.metamodel.Units;
import org.eclipse.esmf.metamodel.impl.DefaultQuantityKind;
import org.eclipse.esmf.metamodel.impl.DefaultUnit;
import org.eclipse.esmf.metamodel.loader.instantiator.AbstractEntityInstantiator;
import org.eclipse.esmf.metamodel.loader.instantiator.AspectInstantiator;
Expand Down Expand Up @@ -76,6 +71,12 @@
import org.eclipse.esmf.samm.KnownVersion;

import com.google.common.collect.Streams;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.Statement;
import org.apache.jena.vocabulary.RDF;
import org.apache.jena.vocabulary.RDFS;

public class ModelElementFactory extends AttributeValueRetriever {
private final KnownVersion metaModelVersion;
Expand All @@ -86,7 +87,8 @@ public class ModelElementFactory extends AttributeValueRetriever {
private final Map<Resource, ModelElement> loadedElements = new HashMap<>();
private Set<ModelNamespace> namespaces;

public ModelElementFactory( final KnownVersion metaModelVersion, final Model model, final Map<Resource, Instantiator<?>> additionalInstantiators ) {
public ModelElementFactory( final KnownVersion metaModelVersion, final Model model,
final Map<Resource, Instantiator<?>> additionalInstantiators ) {
super( new SAMM( metaModelVersion ) );
this.metaModelVersion = metaModelVersion;
this.model = model;
Expand Down Expand Up @@ -144,7 +146,7 @@ public <T extends ModelElement> T create( final Class<T> clazz, final Resource m
return (T) findOrCreateUnit( modelElement );
}
if ( samm.QuantityKind().equals( targetType ) ) {
return (T) findQuantityKind( modelElement );
return (T) findOrCreateQuantityKind( modelElement );
}
final Instantiator<T> instantiator = (Instantiator<T>) instantiators.get( targetType );
if ( instantiator != null ) {
Expand All @@ -160,14 +162,15 @@ public <T extends ModelElement> T create( final Class<T> clazz, final Resource m
final Entity entity = create( Entity.class, targetType );
if ( entity == null ) {
throw new AspectLoadingException( "Could not load " + modelElement + ": Expected " + targetType + " to be an Entity" );

}
return (T) new EntityInstanceInstantiator( this, entity ).apply( modelElement );
}

public QuantityKind findQuantityKind( final Resource quantityKindResource ) {
return QuantityKinds.fromName( quantityKindResource.getLocalName() ).orElseThrow( () ->
new AspectLoadingException( "QuantityKind " + quantityKindResource + " is invalid" ) );
public QuantityKind findOrCreateQuantityKind( final Resource quantityKindResource ) {
final Optional<QuantityKind> predefinedQuantityKind = QuantityKinds.fromName( quantityKindResource.getLocalName() );
return predefinedQuantityKind.orElseGet( () -> new DefaultQuantityKind(
MetaModelBaseAttributes.fromModelElement( metaModelVersion, quantityKindResource, model, samm ),
attributeValue( quantityKindResource, samm.preferredName() ).getLiteral().getLexicalForm() ) );
}

public Unit findOrCreateUnit( final Resource unitResource ) {
Expand All @@ -177,15 +180,16 @@ public Unit findOrCreateUnit( final Resource unitResource ) {
new AspectLoadingException( "Unit definition for " + unitUrn + " is invalid" ) );
}

final Set<QuantityKind> quantityKinds = Streams.stream( model.listStatements( unitResource, samm.quantityKind(), (RDFNode) null ) )
.map( quantityKindStatement -> findOrCreateQuantityKind( quantityKindStatement.getObject().asResource() ) )
.collect( Collectors.toSet() );
return new DefaultUnit(
MetaModelBaseAttributes.fromModelElement( metaModelVersion, unitResource, model, samm ),
optionalAttributeValue( unitResource, samm.symbol() ).map( Statement::getString ),
optionalAttributeValue( unitResource, samm.commonCode() ).map( Statement::getString ),
optionalAttributeValue( unitResource, samm.referenceUnit() ).map( Statement::getResource ).map( Resource::getLocalName ),
optionalAttributeValue( unitResource, samm.conversionFactor() ).map( Statement::getString ),
Streams.stream( model.listStatements( unitResource, samm.quantityKind(), (RDFNode) null ) )
.flatMap( quantityKindStatement -> QuantityKinds.fromName( quantityKindStatement.getObject().asResource().getLocalName() ).stream() )
.collect( Collectors.toSet() ) );
quantityKinds );
}

private Resource resourceType( final Resource resource ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.ResourceFactory;
import org.apache.jena.vocabulary.RDF;

import org.eclipse.esmf.aspectmodel.java.exception.CodeGenerationException;
import org.eclipse.esmf.aspectmodel.resolver.services.DataType;
import org.eclipse.esmf.metamodel.CollectionValue;
Expand All @@ -37,7 +38,8 @@
* <li>If the value is (int) 3, it will return "3"</li>
* <li>If the value is (String) "hi", it will return "\"hi\""</li>
* <li>If the value is (LangString) "hi"@en, it will return "new LangString(\"hi\", Locale.forLanguageTag(\"en\"))"</li>
* <li>If the value is a collection, it will return the corresponding collection, e.g. "new ArrayList<>(){{ add(1); add(2); add(3); }}"</></li>
* <li>If the value is a collection, it will return the corresponding collection, e.g. "new ArrayList<>(){{ add(1); add(2); add(3); }}
* "</></li>
* <li>If the value is an Entity, it will return the corresponding constructor call, e.g. "new MyEntity(\"foo\", 2, 3)"</li>
* </ul>
*/
Expand Down Expand Up @@ -70,7 +72,8 @@ private String generateValueExpression( final ScalarValue value, final Context c
context.getCodeGenerationConfig().importTracker().importExplicit( LangString.class );
context.getCodeGenerationConfig().importTracker().importExplicit( Locale.class );
final LangString langStringValue = (LangString) value.as( ScalarValue.class ).getValue();
return String.format( "new LangString(\"%s\", Locale.forLanguageTag(\"%s\"))", AspectModelJavaUtil.escapeForLiteral( langStringValue.getValue() ),
return String.format( "new LangString(\"%s\", Locale.forLanguageTag(\"%s\"))",
AspectModelJavaUtil.escapeForLiteral( langStringValue.getValue() ),
langStringValue.getLanguageTag().toLanguageTag() );
}

Expand Down Expand Up @@ -107,7 +110,8 @@ public String visitEntityInstance( final EntityInstance instance, final Context
if ( property.isOptional() ) {
return "null";
}
throw new CodeGenerationException( "EntityInstance " + instance.getName() + " is missing value for Property " + property.getName() );
throw new CodeGenerationException(
"EntityInstance " + instance.getName() + " is missing value for Property " + property.getName() );
}
final Context newContext = new Context( context.codeGenerationConfig, property.isOptional() );
return value.accept( this, newContext );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,18 @@ public class ValueInitializer {

static {
final BiFunction<Class<?>, String, String> literalExpression = ( type, valueExpression ) -> valueExpression;
final BiFunction<Class<?>, String, String> stringConstructor = ( type, valueExpression ) -> String.format( "new %s( %s )", type.getSimpleName(),
valueExpression );
final BiFunction<Class<?>, String, String> create = ( type, valueExpression ) -> String.format( "%s.create( %s )", type.getSimpleName(),
valueExpression );
final BiFunction<Class<?>, String, String> parseTypeName = ( type, valueExpression ) -> String.format( "%s.parse%s( %s )", type.getSimpleName(),
final BiFunction<Class<?>, String, String> stringConstructor = ( type, valueExpression ) -> String.format( "new %s( %s )",
type.getSimpleName(), valueExpression );
final BiFunction<Class<?>, String, String> valueOf = ( type, valueExpression ) -> String.format( "%s.valueOf( %s )", type.getSimpleName(),
valueExpression );
final BiFunction<Class<?>, String, String> gregorianCalendar = ( type, valueExpression ) -> "_datatypeFactory.newXMLGregorianCalendar( " + valueExpression
final BiFunction<Class<?>, String, String> create = ( type, valueExpression ) -> String.format( "%s.create( %s )",
type.getSimpleName(), valueExpression );
final BiFunction<Class<?>, String, String> parseTypeName = ( type, valueExpression ) -> String.format( "%s.parse%s( %s )",
type.getSimpleName(), type.getSimpleName(), valueExpression );
final BiFunction<Class<?>, String, String> valueOf = ( type, valueExpression ) -> String.format( "%s.valueOf( %s )",
type.getSimpleName(), valueExpression );
final BiFunction<Class<?>, String, String> gregorianCalendar = ( type, valueExpression ) ->
"_datatypeFactory.newXMLGregorianCalendar( " + valueExpression + " )";
final BiFunction<Class<?>, String, String> duration = ( type, valueExpression ) -> "_datatypeFactory.newDuration( " + valueExpression
+ " )";
final BiFunction<Class<?>, String, String> duration = ( type, valueExpression ) -> "_datatypeFactory.newDuration( " + valueExpression + " )";

INITIALIZERS = new HashMap<>();
INITIALIZERS.put( XSD.xstring, literalExpression );
Expand All @@ -59,11 +60,13 @@ public class ValueInitializer {
INITIALIZERS.put( XSD.date, gregorianCalendar );
INITIALIZERS.put( XSD.dateTime, gregorianCalendar );
INITIALIZERS.put( XSD.dateTimeStamp, gregorianCalendar );
INITIALIZERS.put( XSD.gYear, ( type, valueExpression ) -> "_datatypeFactory.newXMLGregorianCalendarDate( Integer.valueOf( " + valueExpression + " )"
+ ", DatatypeConstants.FIELD_UNDEFINED, DatatypeConstants.FIELD_UNDEFINED, DatatypeConstants.FIELD_UNDEFINED )" );
INITIALIZERS.put( XSD.gYear,
( type, valueExpression ) -> "_datatypeFactory.newXMLGregorianCalendarDate( Integer.valueOf( " + valueExpression + " )"
+ ", DatatypeConstants.FIELD_UNDEFINED, DatatypeConstants.FIELD_UNDEFINED, DatatypeConstants.FIELD_UNDEFINED )" );
INITIALIZERS.put( XSD.gMonth,
( type, valueExpression ) -> "_datatypeFactory.newXMLGregorianCalendarDate( DatatypeConstants.FIELD_UNDEFINED, Integer.valueOf( " + valueExpression
+ " )" + ", DatatypeConstants.FIELD_UNDEFINED, DatatypeConstants.FIELD_UNDEFINED )" );
( type, valueExpression ) ->
"_datatypeFactory.newXMLGregorianCalendarDate( DatatypeConstants.FIELD_UNDEFINED, Integer.valueOf( " + valueExpression
+ " )" + ", DatatypeConstants.FIELD_UNDEFINED, DatatypeConstants.FIELD_UNDEFINED )" );
INITIALIZERS.put( XSD.gYearMonth, gregorianCalendar );
INITIALIZERS.put( XSD.gMonthDay, gregorianCalendar );
INITIALIZERS.put( XSD.gDay, gregorianCalendar );
Expand All @@ -88,8 +91,10 @@ public class ValueInitializer {
}

public boolean needInitializationToConstructor( final List<DeconstructionSet> deconstructionSets ) {
return deconstructionSets.stream().flatMap( deconstructionSet -> deconstructionSet.getElementProperties().stream().map( property -> property.getDataType()
.map( type -> DataType.getJavaTypeForMetaModelType( ResourceFactory.createResource( type.getUrn() ), property.getMetaModelVersion() ) ) ) )
return deconstructionSets.stream()
.flatMap( deconstructionSet -> deconstructionSet.getElementProperties().stream().map( property -> property.getDataType()
.map( type -> DataType.getJavaTypeForMetaModelType( ResourceFactory.createResource( type.getUrn() ),
property.getMetaModelVersion() ) ) ) )
.anyMatch( dataType -> dataType.map( type -> type == XMLGregorianCalendar.class ).orElse( false ) );
}

Expand All @@ -114,7 +119,8 @@ public String apply( final Resource rdfType, final String valueExpression, final
* @param valueExpression an expression that, when evaluated, will return the input value <b>as a string</b>.
* @param metaModelVersion the used meta model version
*/
public String apply( final Resource rdfType, final Class<?> javaType, final String valueExpression, final KnownVersion metaModelVersion ) {
public String apply( final Resource rdfType, final Class<?> javaType, final String valueExpression,
final KnownVersion metaModelVersion ) {
final SAMM samm = new SAMM( metaModelVersion );
if ( rdfType.equals( samm.curie() ) ) {
return String.format( "new Curie( %s )", valueExpression );
Expand Down
Loading

0 comments on commit 007ac12

Please sign in to comment.