Validating Aspect Models
Aspect Models are validated using the AspectModelValidator. Validation returns a list of
Violation`s. A violation has a human-readable message and a unique error code and provides access
to the `EvaluationContext which contains references to the model element that caused the
violation and the SHACL shape that triggered the violation.
Each possible type of violation is a subtype of the Violation interface and provides additional
context information specific to this type, for example, the MinLengthViolation provides int min
(the allowed length) and int actual (the length that was encountered in the model).
Consider the following example:
Show used imports
import java.io.File;
import java.util.List;
import org.eclipse.esmf.aspectmodel.loader.AspectModelLoader;
import org.eclipse.esmf.aspectmodel.shacl.fix.Fix;
import org.eclipse.esmf.aspectmodel.shacl.violation.EvaluationContext;
import org.eclipse.esmf.aspectmodel.shacl.violation.Violation;
import org.eclipse.esmf.aspectmodel.validation.services.AspectModelValidator;
import org.eclipse.esmf.aspectmodel.validation.services.DetailedViolationFormatter;
import org.eclipse.esmf.aspectmodel.validation.services.ViolationFormatter;
import org.eclipse.esmf.metamodel.AspectModel;
// AspectModel as returned by the AspectModelLoader
final AspectModel aspectModel = // ...
final List<Violation> violations = new AspectModelValidator().validateModel( aspectModel );
if ( violations.isEmpty() ) {
// Aspect Model is valid!
return;
} else {
for (Violation violation : violations) {
String errorCode = violation.errorCode();
String message = violation.message();
EvaluationContext context = violation.context();
List<Fix> fixes = violation.fixes();
}
}
final String validationReport = new ViolationFormatter().apply( violations ); (1)
final String detailedReport = new DetailedViolationFormatter().apply( violations );
class MyViolationVisitor implements Violation.Visitor<String> { (2)
// ...
}
// Turn the list of Violations into a list of custom descriptions
final Violation.Visitor<String> visitor = new MyViolationVisitor();
final List<String> result = violations.stream()
.map( violation -> violation.accept( visitor ) )
.toList();
| 1 | To format the validation result into an human-readable form, use the ViolationFormatter or the
DetailedViolationFormatter. Note that those are only intended for text-based interfaces such as
CLIs. |
| 2 | Every application dealing with validation results that needs to transform the results into some
different structure can implement the Violation.Visitor Interface with a suitable target
type (String used as an example here) and use the visitor to handle the different types of
Violations in a type-safe way. |
The AspectModelValidator also provides the loadModel(Supplier<AspectModel>) method that can be used in
conjunction with () → new AspectModeLoader().load(…) to have exceptions that might be created during the
Aspect Model loading process, for example due to model resolution failures or syntax errors in the input
files, be turned into a List<Violation> that can be passed to the aforementioned violation formatters. This
allows for custom structured handling of problems that occur during model loading.
To include the model validator, use the following dependencies:
- Maven
-
<dependency> <groupId>org.eclipse.esmf</groupId> <artifactId>esmf-aspect-model-validator</artifactId> <version>2.12.0</version> </dependency> - Gradle Groovy DSL
-
implementation 'org.eclipse.esmf:esmf-aspect-model-validator:2.12.0' - Gradle Kotlin DSL
-
implementation("org.eclipse.esmf:esmf-aspect-model-validator:2.12.0")
Custom Error Formatting
In order to access validation violations in a type-safe way, implement the interface
org.eclipse.esmf.aspectmodel.shacl.violation.Violation.Visitor. This allows you to handle each
violation type specifically and format error messages according to your application’s requirements.
Show used imports
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.eclipse.esmf.aspectmodel.shacl.violation.DatatypeViolation;
import org.eclipse.esmf.aspectmodel.shacl.violation.MaxCountViolation;
import org.eclipse.esmf.aspectmodel.shacl.violation.Violation;
// Implement other visitor methods for different violation types
@Override
public String visit(Violation violation) {
// Generic fallback for unhandled violation types
return String.format("Validation error: %s", violation.message());
}
Programmatic Access
When using the Java API, errors are available through the Violation interface:
Show used imports
import java.io.File;
import java.util.List;
import org.eclipse.esmf.aspectmodel.loader.AspectModelLoader;
import org.eclipse.esmf.aspectmodel.shacl.fix.Fix;
import org.eclipse.esmf.aspectmodel.shacl.violation.EvaluationContext;
import org.eclipse.esmf.aspectmodel.shacl.violation.Violation;
import org.eclipse.esmf.aspectmodel.validation.services.AspectModelValidator;
import org.eclipse.esmf.aspectmodel.validation.services.DetailedViolationFormatter;
import org.eclipse.esmf.aspectmodel.validation.services.ViolationFormatter;
import org.eclipse.esmf.metamodel.AspectModel;
final List<Violation> violations = new AspectModelValidator().validateModel( aspectModel );
if ( violations.isEmpty() ) {
// Aspect Model is valid!
return;
} else {
for (Violation violation : violations) {
String errorCode = violation.errorCode();
String message = violation.message();
EvaluationContext context = violation.context();
List<Fix> fixes = violation.fixes();
}
}
Error Aggregation
For applications that need to process multiple violations efficiently, you can group errors by type for batch processing:
public void aggregateErrors(List<Violation> violations) {
// Group violations by error code for batch processing
Map<String, List<Violation>> errorsByCode = violations.stream()
.collect(Collectors.groupingBy(Violation::errorCode));
// Handle each error type separately
List<Violation> dataTypeErrors = errorsByCode.get("ERR_TYPE");
List<Violation> cardinalityErrors = errorsByCode.get("ERR_MAX_COUNT");
// Process each error type with specific handling logic
if (dataTypeErrors != null && !dataTypeErrors.isEmpty()) {
System.out.println("Found " + dataTypeErrors.size() + " data type errors");
// Handle data type errors...
}
if (cardinalityErrors != null && !cardinalityErrors.isEmpty()) {
System.out.println("Found " + cardinalityErrors.size() + " cardinality errors");
// Handle cardinality errors...
}
}