Skip to content

Commit 310f89b

Browse files
authored
Merge pull request #712 from bci-oss/666-import-and-export-namespace-packages
Enable import and export of Namepace Packages
2 parents 8ce592a + 5421b13 commit 310f89b

File tree

100 files changed

+1672
-942
lines changed

Some content is hidden

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

100 files changed

+1672
-942
lines changed

core/esmf-aspect-meta-model-interface/src/main/java/org/eclipse/esmf/aspectmodel/AspectModelFile.java

+29
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import java.util.List;
1818
import java.util.Optional;
1919

20+
import org.eclipse.esmf.aspectmodel.urn.AspectModelUrn;
2021
import org.eclipse.esmf.metamodel.ModelElement;
2122
import org.eclipse.esmf.metamodel.ModelElementGroup;
2223
import org.eclipse.esmf.metamodel.Namespace;
@@ -68,6 +69,25 @@ default Optional<String> spdxLicenseIdentifier() {
6869
*/
6970
Optional<URI> sourceLocation();
7071

72+
/**
73+
* Returns the local file name ("something.ttl") of this AspectModelFile, based on its source location.
74+
*
75+
* @return the local Aspect Model file
76+
*/
77+
default Optional<String> filename() {
78+
return sourceLocation().flatMap( uri -> {
79+
final String path = uri.toString();
80+
if ( !path.contains( "/" ) ) {
81+
return Optional.empty();
82+
}
83+
final String filename = path.substring( path.lastIndexOf( "/" ) + 1 );
84+
if ( filename.endsWith( ".ttl" ) && filename.length() >= 5 ) {
85+
return Optional.of( filename );
86+
}
87+
return Optional.empty();
88+
} );
89+
}
90+
7191
/**
7292
* Returns the {@link Namespace} this AspectModelFile is a part of
7393
*
@@ -77,6 +97,15 @@ default Namespace namespace() {
7797
throw new UnsupportedOperationException( "Uninitialized Aspect Model" );
7898
}
7999

100+
/**
101+
* Returns the URN of the namespace of this file
102+
*
103+
* @return the namespace URN
104+
*/
105+
default AspectModelUrn namespaceUrn() {
106+
return namespace().urn();
107+
}
108+
80109
/**
81110
* Lists the model elements that are contained in this AspectModelFile
82111
*

core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/edit/AspectChangeManager.java

+115-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH
2+
* Copyright (c) 2025 Robert Bosch Manufacturing Solutions GmbH
33
*
44
* See the AUTHORS file(s) distributed with this work for additional
55
* information regarding authorship.
@@ -13,16 +13,25 @@
1313

1414
package org.eclipse.esmf.aspectmodel.edit;
1515

16+
import java.io.File;
17+
import java.io.IOException;
18+
import java.net.URI;
19+
import java.nio.file.Files;
20+
import java.nio.file.Paths;
1621
import java.util.ArrayDeque;
22+
import java.util.ArrayList;
1723
import java.util.Deque;
1824
import java.util.HashMap;
25+
import java.util.List;
1926
import java.util.Map;
2027
import java.util.Optional;
2128
import java.util.stream.Stream;
2229

2330
import org.eclipse.esmf.aspectmodel.AspectModelFile;
2431
import org.eclipse.esmf.aspectmodel.loader.AspectModelLoader;
2532
import org.eclipse.esmf.aspectmodel.resolver.modelfile.RawAspectModelFile;
33+
import org.eclipse.esmf.aspectmodel.serializer.AspectSerializer;
34+
import org.eclipse.esmf.aspectmodel.serializer.SerializationException;
2635
import org.eclipse.esmf.metamodel.AspectModel;
2736
import org.eclipse.esmf.metamodel.impl.DefaultAspectModel;
2837

@@ -38,8 +47,8 @@
3847
* Note the following points:
3948
* <ul>
4049
* <li>Only one AspectChangeManager must wrap a given AspectModel at any time</li>
41-
* <li>All changes are done <i>in-memory</i>. In order to write them to the file system, use the
42-
* {@link org.eclipse.esmf.aspectmodel.serializer.AspectSerializer}</li>
50+
* <li>All changes are done <i>in-memory</i>. In order to write them to the file system, use {@link #writeChangesToDisk(WriteConfig)}
51+
* </li>
4352
* <li>After performing an {@link #applyChange(Change)}, {@link #undoChange()} or {@link #redoChange()} operation, and until the
4453
* next call of one of them, the methods {@link #modifiedFiles()}, {@link #createdFiles()} and {@link #removedFiles()} indicate
4554
* corresponding changes in the AspectModel's files.
@@ -185,4 +194,107 @@ public void indicateFileHasChanged( final AspectModelFile file ) {
185194
fileState.put( file, FileState.CHANGED );
186195
}
187196
}
197+
198+
/**
199+
* Syncs all queued changes to the file system. This is the operation that acutally performs operations such as deleting, creating and
200+
* writing files.
201+
*/
202+
public synchronized WriteResult writeChangesToDisk( final WriteConfig config ) {
203+
final WriteResult writeResult = checkFileSystemConsistency( config );
204+
if ( writeResult instanceof WriteResult.PreconditionsNotMet ) {
205+
return writeResult;
206+
}
207+
208+
final WriteResult result = performFileSystemWrite();
209+
if ( result instanceof WriteResult.Success ) {
210+
resetFileStates();
211+
}
212+
return result;
213+
}
214+
215+
protected WriteResult performFileSystemWrite() {
216+
final List<String> messages = new ArrayList<>();
217+
removedFiles()
218+
.map( fileToRemove -> Paths.get( fileToRemove.sourceLocation().orElseThrow() ).toFile() )
219+
.forEach( file -> {
220+
try {
221+
Files.delete( file.toPath() );
222+
} catch ( final IOException exception ) {
223+
messages.add( "Could not delete file: " + file );
224+
}
225+
} );
226+
227+
createdFiles().forEach( fileToCreate -> {
228+
final File file = Paths.get( fileToCreate.sourceLocation().orElseThrow() ).toFile();
229+
if ( !file.getParentFile().exists() && !file.getParentFile().mkdirs() ) {
230+
messages.add( "Target path to write file could not be created: " + file );
231+
} else {
232+
try {
233+
AspectSerializer.INSTANCE.write( fileToCreate );
234+
} catch ( final SerializationException exception ) {
235+
messages.add( exception.getMessage() );
236+
}
237+
}
238+
} );
239+
240+
modifiedFiles().forEach( aspectModelFile -> {
241+
try {
242+
AspectSerializer.INSTANCE.write( aspectModelFile );
243+
} catch ( final SerializationException exception ) {
244+
messages.add( exception.getMessage() );
245+
}
246+
} );
247+
248+
return messages.isEmpty()
249+
? new WriteResult.Success()
250+
: new WriteResult.WriteFailure( messages );
251+
}
252+
253+
protected WriteResult checkFileSystemConsistency( final WriteConfig config ) {
254+
final List<String> messages = new ArrayList<>();
255+
final boolean[] canBeFixedByOverwriting = new boolean[1];
256+
removedFiles().map( AspectSerializer.INSTANCE::aspectModelFileUrl ).forEach( url -> {
257+
if ( !url.getProtocol().equals( "file" ) ) {
258+
messages.add( "File should be removed, but it is not identified by a file: URL: " + url );
259+
}
260+
final File file = new File( URI.create( url.toString() ) );
261+
if ( !file.exists() ) {
262+
messages.add( "File should be removed, but it does not exist: " + file );
263+
}
264+
} );
265+
266+
createdFiles().map( AspectSerializer.INSTANCE::aspectModelFileUrl ).forEach( url -> {
267+
if ( !url.getProtocol().equals( "file" ) ) {
268+
messages.add( "New file should be written, but it is not identified by a file: URL: " + url );
269+
}
270+
final File file = new File( URI.create( url.toString() ) );
271+
if ( file.exists() && !config.forceOverwrite() ) {
272+
messages.add( "New file should be written, but it already exists: " + file );
273+
canBeFixedByOverwriting[0] = true;
274+
}
275+
if ( file.exists() && config.forceOverwrite() && !file.canWrite() ) {
276+
messages.add( "New file should be written, but it is not writable: " + file );
277+
}
278+
} );
279+
280+
modifiedFiles().map( AspectSerializer.INSTANCE::aspectModelFileUrl ).forEach( url -> {
281+
if ( !url.getProtocol().equals( "file" ) ) {
282+
messages.add( "File should be modified, but it is not identified by a file: URL: " + url );
283+
}
284+
final File file = new File( URI.create( url.toString() ) );
285+
if ( !file.exists() ) {
286+
messages.add( "File should be modified, but it does not exist: " + file );
287+
}
288+
if ( !file.canWrite() ) {
289+
messages.add( "File should be modified, but it is not writable: " + file );
290+
}
291+
if ( !file.isFile() ) {
292+
messages.add( "File should be modified, but it is not a regular file: " + file );
293+
}
294+
} );
295+
296+
return messages.isEmpty()
297+
? new WriteResult.Success()
298+
: new WriteResult.PreconditionsNotMet( messages, canBeFixedByOverwriting[0] );
299+
}
188300
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright (c) 2025 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.edit;
15+
16+
import io.soabase.recordbuilder.core.RecordBuilder;
17+
18+
/**
19+
* Configures the file system writing operation in {@link AspectChangeManager}
20+
*
21+
* @param forceOverwrite determines whether existing files should be overwritten
22+
*/
23+
@RecordBuilder
24+
public record WriteConfig(
25+
boolean forceOverwrite
26+
) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright (c) 2025 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.edit;
15+
16+
import java.util.List;
17+
18+
/**
19+
* Represents the result of the operation of writing changes in the {@link AspectChangeManager} to the file system.
20+
*/
21+
public sealed interface WriteResult {
22+
interface Visitor<T> {
23+
T visitSuccess( Success success );
24+
25+
T visitWriteFailure( WriteFailure failure );
26+
27+
T visitPreconditionsNotMet( PreconditionsNotMet preconditionsNotMet );
28+
}
29+
30+
<T> T accept( Visitor<T> visitor );
31+
32+
record PreconditionsNotMet(
33+
List<String> errorMessages,
34+
boolean canBeFixedByOverwriting
35+
) implements WriteResult {
36+
@Override
37+
public <T> T accept( final Visitor<T> visitor ) {
38+
return visitor.visitPreconditionsNotMet( this );
39+
}
40+
}
41+
42+
record WriteFailure(
43+
List<String> errorMessages
44+
) implements WriteResult {
45+
@Override
46+
public <T> T accept( final Visitor<T> visitor ) {
47+
return visitor.visitWriteFailure( this );
48+
}
49+
}
50+
51+
final class Success implements WriteResult {
52+
@Override
53+
public <T> T accept( final Visitor<T> visitor ) {
54+
return visitor.visitSuccess( this );
55+
}
56+
}
57+
}

0 commit comments

Comments
 (0)