Skip to content

Commit 332365c

Browse files
committed
Refactor model file creation and validation logic
Introduces improved file creation methods in ModelUtils, adds duplicate element checks, and refactors ModelService to use new validation and file creation utilities. Exception handling and violation checks are now more robust and modular.
1 parent 284d510 commit 332365c

File tree

3 files changed

+103
-16
lines changed

3 files changed

+103
-16
lines changed

aspect-model-editor-core/src/main/java/org/eclipse/esmf/ame/exceptions/CreateFileException.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ public class CreateFileException extends RuntimeException {
1919
@Serial
2020
private static final long serialVersionUID = 1L;
2121

22+
/**
23+
* Constructs a CreateFileException with message and cause.
24+
*
25+
* @param message the message of the exception
26+
*/
27+
public CreateFileException( final String message ) {
28+
super( message );
29+
}
30+
2231
/**
2332
* Constructs a CreateFileException with message and cause.
2433
*

aspect-model-editor-service/src/main/java/org/eclipse/esmf/ame/services/ModelService.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,21 @@ private void validateModel( final AspectModel aspectModel ) {
105105
public void createOrSaveModel( final String turtleData, final AspectModelUrn aspectModelUrn, final String fileName,
106106
final Path storagePath ) {
107107
try {
108-
final File newFile = ModelUtils.createFile( aspectModelUrn, fileName, storagePath );
108+
final Path newFile = ModelUtils.createFilePath( aspectModelUrn, fileName, storagePath );
109109

110-
final Supplier<AspectModel> aspectModelSupplier = ModelUtils.getAspectModelSupplier( turtleData, newFile, aspectModelLoader );
110+
final Supplier<AspectModel> aspectModelSupplier = ModelUtils.getAspectModelSupplier( turtleData, newFile.toFile(),
111+
aspectModelLoader );
111112
final List<Violation> violations = aspectModelValidator.validateModel( aspectModelSupplier );
112113

113-
if ( violations.stream().anyMatch( ValidationUtils.isInvalidSyntaxViolation() ) ) {
114-
throw new FileReadException( "Aspect Model syntax is not valid" );
115-
}
114+
ModelUtils.throwIfViolationPresent( violations, ValidationUtils.isInvalidSyntaxViolation(), new FileReadException(
115+
violations.stream().filter( ValidationUtils.isInvalidSyntaxViolation() ).findFirst().map( Violation::message )
116+
.orElse( "Aspect Model is not valid" ) ) );
117+
118+
ModelUtils.throwIfViolationPresent( violations, ValidationUtils.isProcessingViolation(), new CreateFileException(
119+
violations.stream().filter( ValidationUtils.isProcessingViolation() ).findFirst().map( Violation::message )
120+
.orElse( "Processing violation" ) ) );
116121

122+
ModelUtils.createFile( newFile );
117123
AspectSerializer.INSTANCE.write( aspectModelSupplier.get().files().getFirst() );
118124
} catch ( final IOException e ) {
119125
throw new CreateFileException( String.format( "Cannot create file %s on workspace", aspectModelUrn ), e );

aspect-model-editor-service/src/main/java/org/eclipse/esmf/ame/services/utils/ModelUtils.java

Lines changed: 83 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,22 @@
2121
import java.nio.file.Files;
2222
import java.nio.file.Path;
2323
import java.nio.file.Paths;
24+
import java.util.List;
25+
import java.util.function.Predicate;
2426
import java.util.function.Supplier;
2527
import java.util.stream.StreamSupport;
2628

29+
import org.eclipse.esmf.ame.exceptions.CreateFileException;
2730
import org.eclipse.esmf.ame.exceptions.FileHandlingException;
2831
import org.eclipse.esmf.ame.exceptions.FileReadException;
32+
import org.eclipse.esmf.aspectmodel.AspectLoadingException;
2933
import org.eclipse.esmf.aspectmodel.AspectModelFile;
3034
import org.eclipse.esmf.aspectmodel.loader.AspectModelLoader;
35+
import org.eclipse.esmf.aspectmodel.resolver.exceptions.ModelResolutionException;
36+
import org.eclipse.esmf.aspectmodel.shacl.violation.Violation;
3137
import org.eclipse.esmf.aspectmodel.urn.AspectModelUrn;
3238
import org.eclipse.esmf.metamodel.AspectModel;
39+
import org.eclipse.esmf.metamodel.ModelElement;
3340

3441
import io.micronaut.http.multipart.CompletedFileUpload;
3542
import jakarta.annotation.Nonnull;
@@ -77,22 +84,32 @@ public static void deleteFileSafely( @Nonnull final File file ) {
7784
}
7885

7986
/**
80-
* Creates a file based on the given AspectModelUrn.
87+
* Creates a new file for the given AspectModelUrn and file name in the specified storage path.
88+
* If necessary, parent directories are created.
8189
*
82-
* @param aspectModelUrn - The Aspect Model URN.
83-
* @param fileName - The name of the Aspect Model file.
84-
* @return The created file.
85-
* @throws IOException if an I/O error occurs.
90+
* @param aspectModelUrn the Aspect Model URN
91+
* @param fileName the name of the file to create
92+
* @param storagePath the base storage path
93+
* @throws IOException if an I/O error occurs during creation
8694
*/
87-
public static File createFile( final AspectModelUrn aspectModelUrn, final String fileName, final Path storagePath ) throws IOException {
95+
public static void createFile( final AspectModelUrn aspectModelUrn, final String fileName, final Path storagePath ) throws IOException {
8896
final Path filePath = createFilePath( aspectModelUrn, fileName, storagePath );
97+
createFile( filePath );
98+
}
8999

100+
/**
101+
* Creates a file at the specified path, including any necessary parent directories.
102+
* Logs the creation process.
103+
*
104+
* @param filePath the path of the file to create
105+
* @throws IOException if an I/O error occurs during creation
106+
*/
107+
public static void createFile( final Path filePath ) throws IOException {
90108
try {
91109
if ( Files.notExists( filePath.getParent() ) ) {
92110
Files.createDirectories( filePath.getParent() );
93111
LOG.info( "Directories created: {}", filePath.getParent() );
94112
}
95-
96113
if ( Files.notExists( filePath ) ) {
97114
Files.createFile( filePath );
98115
LOG.info( "File created: {}", filePath );
@@ -103,8 +120,6 @@ public static File createFile( final AspectModelUrn aspectModelUrn, final String
103120
LOG.error( "Failed to create file: {}", filePath, e );
104121
throw e;
105122
}
106-
107-
return filePath.toFile();
108123
}
109124

110125
/**
@@ -160,9 +175,37 @@ public static InputStream openInputStreamFromUpload( final CompletedFileUpload a
160175
*/
161176
public static Supplier<AspectModel> getAspectModelSupplier( final String turtleData, final File newFile,
162177
final AspectModelLoader aspectModelLoader ) {
163-
final ByteArrayInputStream inputStream = new ByteArrayInputStream( turtleData.getBytes( StandardCharsets.UTF_8 ) );
178+
return createLazySupplier( () -> {
179+
try ( final ByteArrayInputStream inputStream = new ByteArrayInputStream( turtleData.getBytes( StandardCharsets.UTF_8 ) ) ) {
180+
final AspectModel aspectModel = aspectModelLoader.load( inputStream, newFile.toURI() );
181+
checkForDuplicateFiles( aspectModel, newFile.getName() );
182+
return aspectModel;
183+
} catch ( final IOException e ) {
184+
throw new CreateFileException( "Failed to process Turtle data", e );
185+
}
186+
} );
187+
}
188+
189+
private static void checkForDuplicateFiles( final AspectModel aspectModel,
190+
final String sourceFilename ) {
191+
192+
final boolean hasDifferentFile = aspectModel.elements().stream()
193+
.filter( modelElement -> modelElement.getSourceFile().filename().orElse( "" ).equals( sourceFilename ) )
194+
.anyMatch( modelElement -> hasDifferentFileForElement( modelElement, sourceFilename ) );
195+
196+
if ( hasDifferentFile ) {
197+
LOG.warn( "Some elements are already defined in the same namespace" );
198+
throw new CreateFileException( "Some elements are already defined in the same namespace" );
199+
}
200+
}
164201

165-
return createLazySupplier( () -> aspectModelLoader.load( inputStream, newFile.toURI() ) );
202+
private static boolean hasDifferentFileForElement( final ModelElement modelElement, final String sourceFilename ) {
203+
try {
204+
final String modelSourceFileName = modelElement.getSourceFile().filename().orElse( "" );
205+
return !modelSourceFileName.equals( sourceFilename );
206+
} catch ( final ModelResolutionException | AspectLoadingException ex ) {
207+
return false;
208+
}
166209
}
167210

168211
private static Supplier<AspectModel> createLazySupplier( final Supplier<AspectModel> loader ) {
@@ -181,14 +224,43 @@ public AspectModel get() {
181224
};
182225
}
183226

227+
/**
228+
* Loads an {@link AspectModel} from a file path by constructing the full model path.
229+
*
230+
* @param modelPath the base path to the model storage directory
231+
* @param filePath the relative file path (namespace/version/modelName)
232+
* @param aspectModelLoader the loader to load the {@link AspectModel}
233+
* @return the loaded {@link AspectModel}
234+
*/
184235
public static AspectModel loadModelFromFile( final Path modelPath, final String filePath, final AspectModelLoader aspectModelLoader ) {
185236
final Path path = Paths.get( filePath ).normalize();
186237
final String[] pathParts = StreamSupport.stream( path.spliterator(), false ).map( Path::toString ).toArray( String[]::new );
187238
final Path aspectModelPath = constructModelPath( modelPath, pathParts[0], pathParts[1], pathParts[2] );
188239
return aspectModelLoader.load( aspectModelPath.toFile() );
189240
}
190241

242+
/**
243+
* Constructs a model file path from the given components.
244+
*
245+
* @param modelPath the base path to the model storage directory
246+
* @param namespace the namespace part of the model
247+
* @param version the version part of the model
248+
* @param modelName the name of the model file
249+
* @return the constructed {@link Path}
250+
*/
191251
public static Path constructModelPath( final Path modelPath, final String namespace, final String version, final String modelName ) {
192252
return Path.of( modelPath.toString(), namespace, version, modelName );
193253
}
254+
255+
/**
256+
* Throws the given exception if any violation in the list matches the predicate.
257+
*
258+
* @param violations the list of {@link Violation} objects to check
259+
* @param predicate the predicate to filter violations
260+
* @param exception the exception to throw if a matching violation is found
261+
*/
262+
public static void throwIfViolationPresent( final List<Violation> violations, final Predicate<Violation> predicate,
263+
final RuntimeException exception ) {
264+
violations.stream().filter( predicate ).findFirst().ifPresent( v -> {throw exception;} );
265+
}
194266
}

0 commit comments

Comments
 (0)