Created
May 19, 2019 16:20
-
-
Save aranega/0be5b3847d39058d8da56b7aba4af587 to your computer and use it in GitHub Desktop.
A WIP about model validation for PyEcore
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from pyecore.ecore import * | |
from pyecore.resources import ResourceSet | |
from pyecore.utils import dispatch | |
from enum import unique, Enum | |
from functools import wraps | |
import pyecore.ecore as ecore | |
from collections import namedtuple | |
# Definition of error levels and diagnostics | |
@unique | |
class Level(Enum): | |
OK = 0 | |
INFO = 1 | |
WARNING = 2 | |
ERROR = 3 | |
CRITIC = 4 | |
diagnostic = namedtuple('diagnostic', ['rule_id', 'level', 'elements', 'message']) | |
OK_diagnostic = diagnostic(None, Level.OK, set(), None) | |
# definition of some rules for ecore files | |
def no_name(named_element): | |
""" | |
Check if the name feature of an object is set | |
""" | |
if named_element.name: | |
return OK_diagnostic | |
eclass_name = named_element.eClass.name | |
return diagnostic('no_name', Level.ERROR, {named_element}, | |
'{} instances *must* have a name'.format(eclass_name)) | |
def same_name_in_namespace(obj, container): | |
""" | |
Check if two objects own the same name in a same namespace | |
""" | |
problematic_elements = set() | |
for element in (e for e in container if e is not obj): | |
if element.name == obj.name: | |
problematic_elements.add(element) | |
if not problematic_elements: | |
return OK_diagnostic | |
problematic_elements.add(obj) | |
message = 'These objects have the exact same name in the namespace: {}'.format(problematic_elements) | |
return diagnostic('same_name', Level.ERROR, problematic_elements, message) | |
def no_type(typed_element): | |
""" | |
Check if the type of an element is well set | |
""" | |
if typed_element.eType: | |
return OK_diagnostic | |
eclass_name = typed_element.eClass.name | |
message = '{} instances *must* have a type.'.format(eclass_name) | |
return diagnostic('no_type', Level.ERROR, {typed_element}, message) | |
# Ecore validator, it binds rules with specific EObjects | |
class EcoreValidator(object): | |
@dispatch | |
def do_switch(self, obj): | |
return set() | |
def apply_rules(self, obj, rules): | |
diagnostics = [] | |
for rule in rules: | |
diagnostic = rule(obj) | |
if diagnostic.level is not Level.OK: | |
diagnostics.append(diagnostic) | |
return diagnostics | |
@do_switch.register(EClass) | |
def validate_EClass(self, eclass): | |
eclassifiers = eclass.ePackage.eClassifiers | |
same_name = lambda obj: same_name_in_namespace(obj, eclassifiers) | |
return self.apply_rules(eclass, [no_name, same_name]) | |
@do_switch.register(EStructuralFeature) | |
def validate_EStructuralFeature(self, feature): | |
all_features = feature.eContainingClass.eAllStructuralFeatures() | |
same_name = lambda obj: same_name_in_namespace(obj, all_features) | |
return self.apply_rules(feature, [no_name, same_name, no_type]) | |
@do_switch.register(ETypedElement) | |
def validate_ETypedElement(self, typed): | |
return self.apply_rules(typed, [no_type]) | |
@do_switch.register(EPackage) | |
def validate_EPackage(self, epackage): | |
rules = [no_name] | |
if epackage.eSuperPackage: | |
parent_package = epackage.eSuperPackage.eSubpackages | |
same_name = lambda obj: same_name_in_namespace(obj, parent_package) | |
rules.append(same_name) | |
return self.apply_rules(epackage, rules) | |
# The generic validate function that applies a validator to a model root | |
def validate(root, validator): | |
diagnostics = [*validator.do_switch(root)] | |
for element in root.eAllContents(): | |
for diagnostic in validator.do_switch(element): | |
if diagnostic not in diagnostics: | |
diagnostics.extend(diagnostic) | |
return diagnostics | |
# A dedicated validator for ecore | |
def validate_ecore(root): | |
return validate(root, EcoreValidator()) | |
# A small example model | |
pack = EPackage(name='test') | |
A = EClass('A') | |
A.eStructuralFeatures.append(EAttribute('name')) | |
A.eStructuralFeatures.append(EAttribute('name', EString)) | |
B = EClass('A') | |
pack.eClassifiers.extend([A, B]) | |
# We launch the validation | |
from pprint import pprint | |
pprint(validate_ecore(pack)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment