Skip to content

Commit 3e03a5c

Browse files
committed
Enable model resolver to load models with legacy urn:bamm: URNs
1 parent a0940bf commit 3e03a5c

File tree

8 files changed

+242
-110
lines changed

8 files changed

+242
-110
lines changed

core/esmf-aspect-meta-model-version-migrator/src/main/java/org/eclipse/esmf/aspectmodel/versionupdate/migrator/AbstractUriRewriter.java

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
package org.eclipse.esmf.aspectmodel.versionupdate.migrator;
1515

16-
import java.util.AbstractMap;
1716
import java.util.HashMap;
1817
import java.util.Map;
1918
import java.util.Optional;
@@ -25,12 +24,11 @@
2524
import org.apache.jena.rdf.model.RDFNode;
2625
import org.apache.jena.rdf.model.Resource;
2726
import org.apache.jena.rdf.model.ResourceFactory;
27+
import org.eclipse.esmf.aspectmodel.vocabulary.Namespace;
28+
import org.eclipse.esmf.samm.KnownVersion;
2829

2930
import com.google.common.collect.Streams;
3031

31-
import org.eclipse.esmf.samm.KnownVersion;
32-
import org.eclipse.esmf.aspectmodel.vocabulary.Namespace;
33-
3432
/**
3533
* Abstract migration function that is used to apply a change to all URIs in a model
3634
*/
@@ -39,6 +37,12 @@ protected AbstractUriRewriter( final KnownVersion sourceVersion, final KnownVers
3937
super( sourceVersion, targetVersion, 100 );
4038
}
4139

40+
/**
41+
* The URI rewriter implementation decides whether a URI needs to be rewritten, given the map of old to new namespaces
42+
* @param oldUri the URI to rewrite
43+
* @param oldToNewNamespaces the map of old to new namespaces
44+
* @return empty if the URI should be kept as-is, the replacement URI otherwise
45+
*/
4246
protected abstract Optional<String> rewriteUri( String oldUri, Map<String, String> oldToNewNamespaces );
4347

4448
protected Resource updateResource( final Resource resource, final Map<String, String> oldToNewNamespaces ) {
@@ -60,38 +64,51 @@ protected RDFNode updateRdfNode( final RDFNode rdfNode, final Map<String, String
6064
return rdfNode;
6165
}
6266

63-
@Override
64-
public Model migrate( final Model sourceModel ) {
65-
final Model targetModel = ModelFactory.createDefaultModel();
66-
67-
final Map<String, String> targetPrefixes = Namespace.createPrefixMap( getTargetKnownVersion() );
68-
67+
protected Map<String, String> buildReplacementPrefixMap( final Model sourceModel, final Map<String, String> targetPrefixes ) {
6968
final Map<String, String> sourcePrefixes = Namespace.createPrefixMap( getSourceKnownVersion() );
7069
final Map<String, String> oldToNewNamespaces = new HashMap<>();
7170
for ( final Map.Entry<String, String> targetEntry : targetPrefixes.entrySet() ) {
7271
final String prefix = targetEntry.getKey();
7372
if ( prefix != null ) {
7473
final String sourceUri = sourcePrefixes.get( prefix );
75-
if ( sourceUri != null && !sourceUri.equals( targetEntry.getValue() )) {
74+
if ( sourceUri != null && !sourceUri.equals( targetEntry.getValue() ) ) {
7675
oldToNewNamespaces.put( sourceUri, targetEntry.getValue() );
7776
}
7877
}
7978
}
8079

80+
return oldToNewNamespaces;
81+
}
82+
83+
/**
84+
* Builds the map of RDF prefixes to set in the migrated model, e.g. "xsd" -> XSD.NS
85+
*
86+
* @param sourceModel the source model
87+
* @param targetPrefixes the target prefix map, containing e.g. "samm" -> samm.getNamespace()
88+
* @param oldToNewNamespaces the map of old RDF namespaces to their new counterparts
89+
* @return the prefix map
90+
*/
91+
protected Map<String, String> buildPrefixMap( final Model sourceModel, final Map<String, String> targetPrefixes, final Map<String, String> oldToNewNamespaces ) {
92+
return sourceModel.getNsPrefixMap().keySet().stream()
93+
.map( prefix -> Map.<String, String> entry( prefix, targetPrefixes.getOrDefault( prefix, sourceModel.getNsPrefixURI( prefix ) ) ) )
94+
.collect( Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue ) );
95+
}
96+
97+
@Override
98+
public Model migrate( final Model sourceModel ) {
99+
final Model targetModel = ModelFactory.createDefaultModel();
100+
101+
final Map<String, String> targetPrefixes = Namespace.createPrefixMap( getTargetKnownVersion() );
102+
final Map<String, String> oldToNewNamespaces = buildReplacementPrefixMap( sourceModel, targetPrefixes );
103+
81104
Streams.stream( sourceModel.listStatements() ).map( statement -> targetModel
82105
.createStatement(
83106
updateResource( statement.getSubject(), oldToNewNamespaces ),
84107
updateProperty( statement.getPredicate(), oldToNewNamespaces ),
85108
updateRdfNode( statement.getObject(), oldToNewNamespaces )
86109
) ).forEach( targetModel::add );
87110

88-
final Map<String, String> newPrefixMap =
89-
sourceModel.getNsPrefixMap().keySet().stream()
90-
.map( prefix -> new AbstractMap.SimpleEntry<>( prefix,
91-
Optional.ofNullable( targetPrefixes.get( prefix ) ).orElse( sourceModel.getNsPrefixURI( prefix ) ) ) )
92-
.collect( Collectors.toMap( AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue ) );
93-
targetModel.setNsPrefixes( newPrefixMap );
94-
111+
targetModel.setNsPrefixes( buildPrefixMap( sourceModel, targetPrefixes, oldToNewNamespaces ) );
95112
return targetModel;
96113
}
97114
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (c) 2023 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 org.eclipse.esmf.aspectmodel.versionupdate.migrator;
15+
16+
import java.util.Map;
17+
import java.util.Optional;
18+
import java.util.stream.Collectors;
19+
20+
import org.apache.jena.rdf.model.Model;
21+
import org.eclipse.esmf.samm.KnownVersion;
22+
23+
/**
24+
* A {@link AbstractUriRewriter} that replaces all references to the legacy BAMM Aspect Meta Model to their corresponding SAMM counterparts
25+
*/
26+
public class BammUriRewriter extends AbstractUriRewriter {
27+
public BammUriRewriter() {
28+
super( KnownVersion.getLatest(), KnownVersion.getLatest() );
29+
}
30+
31+
@Override
32+
protected Map<String, String> buildPrefixMap( final Model sourceModel, final Map<String, String> targetPrefixes,
33+
final Map<String, String> oldToNewNamespaces ) {
34+
return sourceModel.getNsPrefixMap().keySet().stream()
35+
.map( prefix -> switch ( prefix ) {
36+
case "bamm" -> Map.entry( "samm", targetPrefixes.get( "samm" ) );
37+
case "bamm-c" -> Map.entry( "samm-c", targetPrefixes.get( "samm-c" ) );
38+
case "bamm-e" -> Map.entry( "samm-e", targetPrefixes.get( "samm-e" ) );
39+
case "unit" -> Map.entry( "unit", targetPrefixes.get( "unit" ) );
40+
default -> Map.entry( prefix, rewriteUri( sourceModel.getNsPrefixURI( prefix ), oldToNewNamespaces )
41+
.orElse( sourceModel.getNsPrefixURI( prefix ) ) );
42+
} )
43+
.collect( Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue ) );
44+
}
45+
46+
@Override
47+
protected Map<String, String> buildReplacementPrefixMap( final Model sourceModel, final Map<String, String> targetPrefixes ) {
48+
// The mapping of the URNs of the legacy BAMM Aspect Meta model to their corresponding SAMM counterparts
49+
return Map.of(
50+
"urn:bamm:io.openmanufacturing:meta-model:2.0.0#", targetPrefixes.get( "samm" ),
51+
"urn:bamm:io.openmanufacturing:characteristic:2.0.0#", targetPrefixes.get( "samm-c" ),
52+
"urn:bamm:io.openmanufacturing:entity:2.0.0#", targetPrefixes.get( "samm-c" ),
53+
"urn:bamm:io.openmanufacturing:unit:2.0.0#", targetPrefixes.get( "unit" )
54+
);
55+
}
56+
57+
@Override
58+
protected Optional<String> rewriteUri( final String oldUri, final Map<String, String> oldToNewNamespaces ) {
59+
if ( !oldUri.startsWith( "urn:bamm:" ) ) {
60+
return Optional.empty();
61+
}
62+
String result = oldUri;
63+
for ( final Map.Entry<String, String> mapEntry : oldToNewNamespaces.entrySet() ) {
64+
result = result.replace( mapEntry.getKey(), mapEntry.getValue() );
65+
}
66+
// This catches the regular (i.e., non meta-model) URNs
67+
result = result.replace( "urn:bamm:", "urn:samm:" );
68+
return Optional.of( result );
69+
}
70+
}

core/esmf-aspect-model-resolver/src/main/java/org/eclipse/esmf/aspectmodel/resolver/AspectModelResolver.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,19 @@
4040
import org.apache.jena.vocabulary.RDF;
4141
import org.apache.jena.vocabulary.XSD;
4242
import org.eclipse.esmf.aspectmodel.VersionNumber;
43+
import org.eclipse.esmf.aspectmodel.resolver.services.SammAspectMetaModelResourceResolver;
44+
import org.eclipse.esmf.aspectmodel.resolver.services.TurtleLoader;
4345
import org.eclipse.esmf.aspectmodel.resolver.services.VersionedModel;
4446
import org.eclipse.esmf.aspectmodel.urn.AspectModelUrn;
4547
import org.eclipse.esmf.aspectmodel.urn.ElementType;
4648
import org.eclipse.esmf.aspectmodel.urn.UrnSyntaxException;
4749
import org.eclipse.esmf.aspectmodel.versionupdate.MigratorFactory;
4850
import org.eclipse.esmf.aspectmodel.versionupdate.MigratorService;
4951
import org.eclipse.esmf.aspectmodel.versionupdate.MigratorServiceLoader;
52+
import org.eclipse.esmf.aspectmodel.versionupdate.migrator.BammUriRewriter;
5053

5154
import com.google.common.collect.Streams;
5255

53-
import org.eclipse.esmf.aspectmodel.resolver.services.SammAspectMetaModelResourceResolver;
54-
import org.eclipse.esmf.aspectmodel.resolver.services.TurtleLoader;
55-
5656
import io.vavr.CheckedFunction1;
5757
import io.vavr.Value;
5858
import io.vavr.control.Option;
@@ -65,6 +65,7 @@
6565
public class AspectModelResolver {
6666

6767
private final MigratorService migratorService = MigratorServiceLoader.getInstance().getMigratorService();
68+
private final BammUriRewriter bammUriRewriter = new BammUriRewriter();
6869

6970
/**
7071
* Returns all valid model URNs for Aspects and model elements in a model
@@ -166,7 +167,8 @@ public Try<VersionedModel> resolveAspectModel( final ResolutionStrategy resoluti
166167
* @return the resolved model on success
167168
*/
168169
public Try<VersionedModel> resolveAspectModel( final Model initialModel, final ResolutionStrategy resolutionStrategy, final List<AspectModelUrn> input ) {
169-
final Try<Model> mergedModel = resolve( initialModel, input, resolutionStrategy );
170+
final Try<Model> mergedModel = resolve( initialModel, input, resolutionStrategy )
171+
.map( bammUriRewriter::migrate );
170172

171173
if ( mergedModel.isFailure() ) {
172174
if ( mergedModel.getCause() instanceof FileNotFoundException ) {
@@ -301,6 +303,7 @@ private Try<Model> getModelForUrn( final String urn, final ResolutionStrategy re
301303
}
302304

303305
try {
306+
// final AspectModelUrn aspectModelUrn = AspectModelUrn.fromUrn( replaceLegacyBammUrn( urn ) );
304307
final AspectModelUrn aspectModelUrn = AspectModelUrn.fromUrn( urn );
305308
if ( aspectModelUrn.getElementType() != ElementType.NONE ) {
306309
return Try.success( EMPTY_MODEL );
@@ -319,6 +322,18 @@ private Try<Model> getModelForUrn( final String urn, final ResolutionStrategy re
319322
}
320323
}
321324

325+
/**
326+
* Adapter that enables the resolver to handle URNs with the legacy "urn:bamm:" prefix
327+
* @param urn the URN to clean up
328+
* @return the original URN (if using valid urn:samm: scheme) or the the cleaned up URN
329+
*/
330+
private String replaceLegacyBammUrn( final String urn ) {
331+
if ( urn.startsWith( "urn:bamm:" ) ) {
332+
return urn.replace( "urn:bamm:", "urn:samm:" );
333+
}
334+
return urn;
335+
}
336+
322337
/**
323338
* Merge a model into an existing target model. Prefixes are only added when they are not already present, i.e.,
324339
* a model won't overwrite the empty prefix of the target model.

0 commit comments

Comments
 (0)