"""
This module gathers utility classes and functions that can ease metamodel and
model manipulation.
"""
from .ecore import EPackage, BadValueError
from .notification import EObserver, Kind


class DynamicEPackage(EObserver):
    """A DynamicEPackage gives the ability to directly handle metaclasses
    from a metamodel as if it were a statically generated EPackage.

    Usage from an existing dynamic EPackage named 'myroot' that defines two
    EClass: 'A' and 'B'
    >>> from pyecore.utils import DynamicEPackage
    >>> MyAPI = DynamicEPackage(myroot)
    >>> MyAPI.A
    <EClass name="A">
    >>> a = MyAPI.A()
    >>> a
    <pyecore.ecore.A object at 0x7f118de363c8>
    """

    def __init__(self, package):
        if not isinstance(package, EPackage):
            raise BadValueError(got=epackage, expected=EPackage)
        super().__init__(notifier=package)

        for eclass in package.eClassifiers:
            setattr(self, eclass.name, eclass)
        for subpackage in package.eSubpackages:
            setattr(self, subpackage.name, DynamicEPackage(subpackage))

    def notifyChanged(self, notification):
        kind = notification.kind
        if notification.feature is EPackage.eClassifiers:
            if kind == Kind.ADD:
                new = notification.new
                setattr(self, new.name, new)
            elif kind == Kind.ADD_MANY:
                for new in notification.new:
                    setattr(self, new.name, new)
            elif kind == Kind.REMOVE and notification.old.eResource is None:
                try:
                    delattr(self, notification.old.name)
                except AttributeError:
                    pass
            # REMOVE_MANY is not yet well supported

Add new class to ease dynamic metamodel handling
Add new class to ease dynamic metamodel handling

The dynamic metamodel manipulation is a little bit cumbersome when it
comes to extract all the existing EClass from a loaded
EPackage. Currently, a solution is to gather all the elements by hand
in dedicated variables:

>>> A = dynamic_epackage.getEClassifier('A')
>>> B = dynamic_epackage.getEClassifier('B')
>>> a1 = A()
>>> b1 = B()

This commit introduces a new class: 'DynamicEPackage' which
constructs, using reflection, an object that has direct references to
each EClass/sub-EPackage by name:

>>> from pyecore.utils import DynamicEPackage
>>> mylib = DynamicEPackage(dynamic_epackage)
>>> a1 = mylib.A()
>>> b1 = mylib.B()

This greatly helps the user to easily call and get EClass from a
freshly loaded dynamic EPackage.
