2
2
* Copyright (c) 2021 Robert Bosch Manufacturing Solutions GmbH
3
3
*
4
4
* See the AUTHORS file(s) distributed with this work for additional
5
- * information regarding authorship.
5
+ * information regarding authorship.
6
6
*
7
7
* This Source Code Form is subject to the terms of the Mozilla Public
8
8
* License, v. 2.0. If a copy of the MPL was not distributed with this
15
15
16
16
import java .io .FileNotFoundException ;
17
17
import java .util .Collections ;
18
+ import java .util .HashSet ;
19
+ import java .util .Map ;
18
20
import java .util .Set ;
21
+ import java .util .Stack ;
19
22
import java .util .function .Function ;
20
23
import java .util .stream .Collectors ;
21
24
import java .util .stream .Stream ;
22
25
23
26
import org .apache .jena .rdf .model .Model ;
24
27
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 ;
27
29
import org .apache .jena .rdf .model .Statement ;
30
+ import org .apache .jena .rdf .model .StmtIterator ;
28
31
import org .apache .jena .vocabulary .RDF ;
32
+ import org .apache .jena .vocabulary .XSD ;
29
33
30
- import com .google .common .collect .Sets ;
31
34
import com .google .common .collect .Streams ;
32
35
33
36
import io .openmanufacturing .sds .aspectmodel .VersionNumber ;
34
37
import io .openmanufacturing .sds .aspectmodel .resolver .services .VersionedModel ;
35
38
import io .openmanufacturing .sds .aspectmodel .urn .AspectModelUrn ;
36
- import io .openmanufacturing .sds .aspectmodel .urn .UrnSyntaxException ;
39
+ import io .openmanufacturing .sds .aspectmodel .urn .ElementType ;
37
40
import io .openmanufacturing .sds .aspectmodel .versionupdate .MigratorFactory ;
38
41
import io .openmanufacturing .sds .aspectmodel .versionupdate .MigratorService ;
39
42
import io .openmanufacturing .sds .aspectmodel .versionupdate .MigratorServiceLoader ;
40
- import io .vavr .Tuple ;
41
- import io .vavr .Tuple2 ;
42
- import io .vavr .Value ;
43
43
import io .vavr .control .Option ;
44
44
import io .vavr .control .Try ;
45
45
@@ -57,7 +57,7 @@ public class AspectModelResolver {
57
57
* @param model The RDF model
58
58
* @return The set of URNs
59
59
*/
60
- public static Set <AspectModelUrn > getAllUrnsInModel ( final Model model ) {
60
+ public static Set <String > getAllUrnsInModel ( final Model model ) {
61
61
return Streams .stream ( model .listStatements ().mapWith ( statement -> {
62
62
final Stream <String > subjectUri = statement .getSubject ().isURIResource () ?
63
63
Stream .of ( statement .getSubject ().getURI () ) : Stream .empty ();
@@ -66,26 +66,10 @@ public static Set<AspectModelUrn> getAllUrnsInModel( final Model model ) {
66
66
Stream .of ( statement .getObject ().asResource ().getURI () ) : Stream .empty ();
67
67
68
68
return Stream .of ( subjectUri , propertyUri , objectUri )
69
- .flatMap ( Function .identity () )
70
- .map ( AspectModelResolver ::getAspectModelUrn )
71
- .flatMap ( Value ::toJavaStream );
69
+ .flatMap ( Function .identity () );
72
70
} ) ).flatMap ( Function .identity () ).collect ( Collectors .toSet () );
73
71
}
74
72
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
-
89
73
/**
90
74
* Method to resolve a given {@link AspectModelUrn} using a suitable {@link ResolutionStrategy}.
91
75
* 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 ) {
95
79
* @return The resolved model on success
96
80
*/
97
81
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 );
102
83
103
84
if ( mergedModel .isFailure () ) {
104
85
if ( mergedModel .getCause () instanceof FileNotFoundException ) {
@@ -112,7 +93,7 @@ public Try<VersionedModel> resolveAspectModel( final ResolutionStrategy resolver
112
93
113
94
final Set <VersionNumber > usedMetaModelVersions =
114
95
mergedModel .map ( resourceResolver ::getUsedMetaModelVersions )
115
- .getOrElse ( Collections .emptySet () );
96
+ .getOrElse ( Collections .emptySet () );
116
97
117
98
if ( usedMetaModelVersions .isEmpty () ) {
118
99
return Try .failure ( new ModelResolutionException ( "Could not determine used meta model version" ) );
@@ -121,7 +102,7 @@ public Try<VersionedModel> resolveAspectModel( final ResolutionStrategy resolver
121
102
if ( usedMetaModelVersions .size () == 1 && migratorService .getMigratorFactory ().isEmpty () ) {
122
103
return mergedModel .flatMap ( model ->
123
104
migratorService .getSdsMigratorFactory ().createAspectMetaModelResourceResolver ()
124
- .mergeMetaModelIntoRawModel ( model , usedMetaModelVersions .iterator ().next () ) );
105
+ .mergeMetaModelIntoRawModel ( model , usedMetaModelVersions .iterator ().next () ) );
125
106
}
126
107
127
108
final Try <VersionNumber > oldestVersion =
@@ -130,85 +111,95 @@ public Try<VersionedModel> resolveAspectModel( final ResolutionStrategy resolver
130
111
return mergedModel .flatMap ( model ->
131
112
oldestVersion .flatMap ( oldest ->
132
113
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 ) ) );
141
122
}
142
123
143
124
/**
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.
146
126
*
147
127
* @param model the model
148
128
* @param urn the URN of the model element
149
129
* @return true if the model contains the definition of the model element
150
130
*/
151
131
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 );
162
133
}
163
134
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 ();
167
148
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
+ }
170
161
}
171
162
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
+ }
176
165
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 );
180
171
}
181
172
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
+ } );
195
183
}
196
184
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
+ }
200
190
}
201
191
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 ();
209
194
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
+ }
213
204
}
214
205
}
0 commit comments