Skip to content

Commit 3cd1630

Browse files
committed
Fix model resolution
1 parent 92871d0 commit 3cd1630

File tree

6 files changed

+148
-171
lines changed

6 files changed

+148
-171
lines changed

core/sds-aspect-model-resolver/src/main/java/io/openmanufacturing/sds/aspectmodel/resolver/AspectModelResolver.java

Lines changed: 81 additions & 90 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
@@ -15,31 +15,31 @@
1515

1616
import java.io.FileNotFoundException;
1717
import java.util.Collections;
18+
import java.util.HashSet;
19+
import java.util.Map;
1820
import java.util.Set;
21+
import java.util.Stack;
1922
import java.util.function.Function;
2023
import java.util.stream.Collectors;
2124
import java.util.stream.Stream;
2225

2326
import org.apache.jena.rdf.model.Model;
2427
import org.apache.jena.rdf.model.ModelFactory;
25-
import org.apache.jena.rdf.model.Resource;
26-
import org.apache.jena.rdf.model.ResourceFactory;
28+
import org.apache.jena.rdf.model.RDFNode;
2729
import org.apache.jena.rdf.model.Statement;
30+
import org.apache.jena.rdf.model.StmtIterator;
2831
import org.apache.jena.vocabulary.RDF;
32+
import org.apache.jena.vocabulary.XSD;
2933

30-
import com.google.common.collect.Sets;
3134
import com.google.common.collect.Streams;
3235

3336
import io.openmanufacturing.sds.aspectmodel.VersionNumber;
3437
import io.openmanufacturing.sds.aspectmodel.resolver.services.VersionedModel;
3538
import io.openmanufacturing.sds.aspectmodel.urn.AspectModelUrn;
36-
import io.openmanufacturing.sds.aspectmodel.urn.UrnSyntaxException;
39+
import io.openmanufacturing.sds.aspectmodel.urn.ElementType;
3740
import io.openmanufacturing.sds.aspectmodel.versionupdate.MigratorFactory;
3841
import io.openmanufacturing.sds.aspectmodel.versionupdate.MigratorService;
3942
import io.openmanufacturing.sds.aspectmodel.versionupdate.MigratorServiceLoader;
40-
import io.vavr.Tuple;
41-
import io.vavr.Tuple2;
42-
import io.vavr.Value;
4343
import io.vavr.control.Option;
4444
import io.vavr.control.Try;
4545

@@ -57,7 +57,7 @@ public class AspectModelResolver {
5757
* @param model The RDF model
5858
* @return The set of URNs
5959
*/
60-
public static Set<AspectModelUrn> getAllUrnsInModel( final Model model ) {
60+
public static Set<String> getAllUrnsInModel( final Model model ) {
6161
return Streams.stream( model.listStatements().mapWith( statement -> {
6262
final Stream<String> subjectUri = statement.getSubject().isURIResource() ?
6363
Stream.of( statement.getSubject().getURI() ) : Stream.empty();
@@ -66,26 +66,10 @@ public static Set<AspectModelUrn> getAllUrnsInModel( final Model model ) {
6666
Stream.of( statement.getObject().asResource().getURI() ) : Stream.empty();
6767

6868
return Stream.of( subjectUri, propertyUri, objectUri )
69-
.flatMap( Function.identity() )
70-
.map( AspectModelResolver::getAspectModelUrn )
71-
.flatMap( Value::toJavaStream );
69+
.flatMap( Function.identity() );
7270
} ) ).flatMap( Function.identity() ).collect( Collectors.toSet() );
7371
}
7472

75-
/**
76-
* Parses an Aspect (meta) model URN into an {@link AspectModelUrn}
77-
*
78-
* @param uri The Aspect (meta) model URN
79-
* @return The {@link AspectModelUrn} if parsing succeeds, an {@link UrnSyntaxException} otherwise
80-
*/
81-
private static Try<AspectModelUrn> getAspectModelUrn( final String uri ) {
82-
try {
83-
return Try.success( AspectModelUrn.fromUrn( uri ) );
84-
} catch ( final UrnSyntaxException exception ) {
85-
return Try.failure( exception );
86-
}
87-
}
88-
8973
/**
9074
* Method to resolve a given {@link AspectModelUrn} using a suitable {@link ResolutionStrategy}.
9175
* This creates the closure (merged model) of all referenced models and the corresponding meta model.
@@ -95,10 +79,7 @@ private static Try<AspectModelUrn> getAspectModelUrn( final String uri ) {
9579
* @return The resolved model on success
9680
*/
9781
public Try<VersionedModel> resolveAspectModel( final ResolutionStrategy resolver, final AspectModelUrn input ) {
98-
99-
final Tuple2<Try<Model>, Set<AspectModelUrn>> resolutionResult =
100-
resolve( ModelFactory.createDefaultModel(), input, resolver, Set.of() );
101-
final Try<Model> mergedModel = resolutionResult._1();
82+
final Try<Model> mergedModel = resolve( input.toString(), resolver );
10283

10384
if ( mergedModel.isFailure() ) {
10485
if ( mergedModel.getCause() instanceof FileNotFoundException ) {
@@ -112,7 +93,7 @@ public Try<VersionedModel> resolveAspectModel( final ResolutionStrategy resolver
11293

11394
final Set<VersionNumber> usedMetaModelVersions =
11495
mergedModel.map( resourceResolver::getUsedMetaModelVersions )
115-
.getOrElse( Collections.emptySet() );
96+
.getOrElse( Collections.emptySet() );
11697

11798
if ( usedMetaModelVersions.isEmpty() ) {
11899
return Try.failure( new ModelResolutionException( "Could not determine used meta model version" ) );
@@ -121,7 +102,7 @@ public Try<VersionedModel> resolveAspectModel( final ResolutionStrategy resolver
121102
if ( usedMetaModelVersions.size() == 1 && migratorService.getMigratorFactory().isEmpty() ) {
122103
return mergedModel.flatMap( model ->
123104
migratorService.getSdsMigratorFactory().createAspectMetaModelResourceResolver()
124-
.mergeMetaModelIntoRawModel( model, usedMetaModelVersions.iterator().next() ) );
105+
.mergeMetaModelIntoRawModel( model, usedMetaModelVersions.iterator().next() ) );
125106
}
126107

127108
final Try<VersionNumber> oldestVersion =
@@ -130,85 +111,95 @@ public Try<VersionedModel> resolveAspectModel( final ResolutionStrategy resolver
130111
return mergedModel.flatMap( model ->
131112
oldestVersion.flatMap( oldest ->
132113
migratorService.getSdsMigratorFactory()
133-
.createAspectMetaModelResourceResolver()
134-
.mergeMetaModelIntoRawModel( model, oldest )
135-
.orElse( () -> migratorService.getMigratorFactory()
136-
.map( MigratorFactory::createAspectMetaModelResourceResolver )
137-
.map( Try::success )
138-
.orElseThrow()
139-
.flatMap( metaResolver -> metaResolver.mergeMetaModelIntoRawModel( model, oldest ) ) )
140-
.flatMap( migratorService::updateMetaModelVersion ) ) );
114+
.createAspectMetaModelResourceResolver()
115+
.mergeMetaModelIntoRawModel( model, oldest )
116+
.orElse( () -> migratorService.getMigratorFactory()
117+
.map( MigratorFactory::createAspectMetaModelResourceResolver )
118+
.map( Try::success )
119+
.orElseThrow()
120+
.flatMap( metaResolver -> metaResolver.mergeMetaModelIntoRawModel( model, oldest ) ) )
121+
.flatMap( migratorService::updateMetaModelVersion ) ) );
141122
}
142123

143124
/**
144-
* Checks if a given model contains the definition of a model element. This is determined by checking whether the
145-
* statement "modelElement rdf:type *" or "modelElement bamm:refines *" exists.
125+
* Checks if a given model contains the definition of a model element.
146126
*
147127
* @param model the model
148128
* @param urn the URN of the model element
149129
* @return true if the model contains the definition of the model element
150130
*/
151131
public static boolean containsDefinition( final Model model, final AspectModelUrn urn ) {
152-
return Streams.stream( model.listStatements() )
153-
.anyMatch( statement -> isBammElementDefinition( urn, statement ) );
154-
}
155-
156-
private static boolean isBammElementDefinition( final AspectModelUrn urn, final Statement statement ) {
157-
final Resource resource = ResourceFactory.createResource( urn.getUrn().toString() );
158-
final String bammRefinesRegex = ".*\\bmeta-model:\\b.*\\b#refines";
159-
return resource.equals( statement.getSubject() ) &&
160-
(RDF.type.equals( statement.getPredicate() ) ||
161-
statement.getPredicate().toString().matches( bammRefinesRegex ));
132+
return model.contains( model.createResource( urn.toString() ), RDF.type, (RDFNode) null );
162133
}
163134

164-
private Tuple2<Try<Model>, Set<AspectModelUrn>> resolve( final Model targetModel,
165-
final AspectModelUrn urn,
166-
final ResolutionStrategy resolver, final Set<AspectModelUrn> resolvedUrns ) {
135+
private Try<Model> resolve( final String urn, final ResolutionStrategy resolutionStrategy ) {
136+
final Model result = ModelFactory.createDefaultModel();
137+
final Stack<String> unresolvedUrns = new Stack<>();
138+
final Set<Model> mergedModels = new HashSet<>();
139+
unresolvedUrns.push( urn );
140+
141+
while ( !unresolvedUrns.isEmpty() ) {
142+
final String urnToResolve = unresolvedUrns.pop();
143+
final Try<Model> resolvedModel = getModelForUrn( urnToResolve, resolutionStrategy );
144+
if ( resolvedModel.isFailure() ) {
145+
return resolvedModel;
146+
}
147+
final Model model = resolvedModel.get();
167148

168-
if ( resolvedUrns.contains( urn ) ) {
169-
return Tuple.of( Try.success( targetModel ), resolvedUrns );
149+
// Merge the resolved model into the target if it was not already merged before
150+
// (because the model contains more than one definition)
151+
if ( !mergedModels.contains( model ) ) {
152+
mergeModels( result, model );
153+
mergedModels.add( model );
154+
}
155+
for ( final String element : getAllUrnsInModel( model ) ) {
156+
if ( !result.contains( model.createResource( element ), RDF.type, (RDFNode) null )
157+
&& !unresolvedUrns.contains( element ) ) {
158+
unresolvedUrns.push( element );
159+
}
160+
}
170161
}
171162

172-
final Set<AspectModelUrn> updatedResolvedUrns = Sets.union( resolvedUrns, Set.of( urn ) );
173-
if ( containsDefinition( targetModel, urn ) ) {
174-
return Tuple.of( Try.success( targetModel ), updatedResolvedUrns );
175-
}
163+
return Try.success( result );
164+
}
176165

177-
final Try<Model> loadedModel = resolver.apply( urn );
178-
if ( loadedModel.isFailure() ) {
179-
return Tuple.of( loadedModel, updatedResolvedUrns );
166+
private final Model EMPTY_MODEL = ModelFactory.createDefaultModel();
167+
168+
private Try<Model> getModelForUrn( final String urn, final ResolutionStrategy resolutionStrategy ) {
169+
if ( urn.startsWith( RDF.getURI() ) || urn.startsWith( XSD.getURI() ) ) {
170+
return Try.success( EMPTY_MODEL );
180171
}
181172

182-
addModelWithoutOverwritingEmptyPrefix( targetModel, loadedModel.get() );
183-
return getAllUrnsInModel( loadedModel.get() )
184-
.stream()
185-
.filter( modelUrn -> !modelUrn.isBammUrn() )
186-
.reduce( Tuple.of( Try.success( targetModel ), updatedResolvedUrns ),
187-
(( tuple, aspectModelUrn ) ->
188-
resolve( targetModel, aspectModelUrn, resolver, updatedResolvedUrns )),
189-
( tuple, tuple2 ) -> {
190-
final Try<Model> mergedModels = tuple._1().flatMap( model ->
191-
tuple2._1().map( model2 -> addModelWithoutOverwritingEmptyPrefix( model, model2 ) ) );
192-
final Set<AspectModelUrn> mergedSets = Sets.union( tuple._2(), tuple2._2() );
193-
return Tuple.of( mergedModels, mergedSets );
194-
} );
173+
final AspectModelUrn aspectModelUrn = AspectModelUrn.fromUrn( urn );
174+
if ( aspectModelUrn.getElementType() != ElementType.NONE ) {
175+
return Try.success( EMPTY_MODEL );
176+
}
177+
return resolutionStrategy.apply( aspectModelUrn ).flatMap( model -> {
178+
if ( !model.contains( model.createResource( urn ), RDF.type, (RDFNode) null ) ) {
179+
return Try.failure( new ModelResolutionException( "Resolution strategy returned a model which does contain element definition for " + urn ) );
180+
}
181+
return Try.success( model );
182+
} );
195183
}
196184

197-
private Model addModelWithoutOverwritingEmptyPrefix( final Model target, final Model modelToAdd ) {
198-
if ( !target.getNsPrefixMap().containsKey( "" ) && modelToAdd.getNsPrefixMap().containsKey( "" ) ) {
199-
target.setNsPrefix( "", modelToAdd.getNsPrefixURI( "" ) );
185+
private void mergeModels( final Model target, final Model other ) {
186+
for ( final Map.Entry<String, String> prefixEntry : other.getNsPrefixMap().entrySet() ) {
187+
if ( !target.getNsPrefixMap().containsKey( prefixEntry.getKey() ) ) {
188+
target.setNsPrefix( prefixEntry.getKey(), prefixEntry.getValue() );
189+
}
200190
}
201191

202-
modelToAdd.getNsPrefixMap().entrySet().stream().filter( entry -> !"".equals( entry.getKey() ) )
203-
.forEach( entry -> target.setNsPrefix( entry.getKey(), entry.getValue() ) );
204-
205-
migratorService.getMigratorFactory().map( MigratorFactory::createAspectMetaModelResourceResolver ).stream()
206-
.flatMap(
207-
aspectMetaModelResourceResolver -> aspectMetaModelResourceResolver
208-
.listAspectStatements( modelToAdd, target ) ).forEach( target::add );
192+
for ( final StmtIterator it = other.listStatements(); it.hasNext(); ) {
193+
final Statement statement = it.next();
209194

210-
migratorService.getSdsMigratorFactory().createAspectMetaModelResourceResolver()
211-
.listAspectStatements( modelToAdd, target ).forEach( target::add );
212-
return target;
195+
if ( target.contains( statement.getSubject(), statement.getPredicate(), (RDFNode) null ) ) {
196+
// Only add the assertion that is already present in the target model if the value is a language string
197+
if ( statement.getObject().isLiteral() && !statement.getLiteral().getLanguage().isEmpty() ) {
198+
target.add( statement );
199+
}
200+
} else {
201+
target.add( statement );
202+
}
203+
}
213204
}
214205
}

0 commit comments

Comments
 (0)