class RecipeContainer(Observable, Configurable, Validatable):
    """Base class for organizing pieces of a FitRecipe.

    RecipeContainers are hierarchical organizations of Parameters and other
    RecipeContainers. This class provides attribute-access to these contained
    objects.  Parameters and other RecipeContainers can be found within the
    hierarchy with the _locateManagedObject method.

    A RecipeContainer can manage dictionaries for that store various objects.
    These dictionaries can be added to the RecipeContainer using the _manage
    method. RecipeContainer methods that add, remove or retrieve objects will
    work with any managed dictionary. This makes it easy to add new types of
    objects to be contained by a RecipeContainer. By default, the
    RecipeContainer is configured to manage an OrderedDict of Parameter
    objects.

    RecipeContainer is an Observable, and observes its managed objects and
    Parameters. This allows hierarchical calculation elements, such as
    ProfileGenerator, to detect changes in Parameters and Restraints on which
    it may depend.

    Attributes
    name            --  A name for this RecipeContainer. Names should be unique
                        within a RecipeContainer and should be valid attribute
                        names.
    _parameters     --  A managed OrderedDict of contained Parameters.
    __managed       --  A list of managed dictionaries. This is used for
                        attribute access, addition and removal.
    _configobjs     --  A set of configurable objects that must know of
                        configuration changes within this object.

    Properties
    names           --  Variable names (read only). See getNames.
    values          --  Variable values (read only). See getValues.
    """

    names = property(lambda self: self.getNames())
    values = property(lambda self: self.getValues())

    def __init__(self, name):
        Observable.__init__(self)
        Configurable.__init__(self)
        validateName(name)
        self.name = name
        self._parameters = OrderedDict()

        self.__managed = []
        self._manage(self._parameters)

        return

    def _manage(self, d):
        """Manage a dictionary of objects.

        This adds the dictionary to the __managed list. Dictionaries in
        __managed are used for attribute access, addition, and removal.
        """
        self.__managed.append(d)
        return

    def _iterManaged(self):
        """Get iterator over managed objects."""
        return chain(*(d.values() for d in self.__managed))

    def iterPars(self, name=".", recurse=True):
        """Iterate over Parameters.

        name    --  Select parameters with this name (regular expression,
                    default ".").
        recurse --  Recurse into managed objects (default True)
        """
        for par in self._parameters.itervalues():
            if re.match(name, par.name):
                yield par

        if not recurse:
            return

        # Iterate over objects within the managed dictionaries.
        managed = self.__managed[:]
        managed.remove(self._parameters)
        for m in managed:
            for obj in m.values():
                if hasattr(obj, "iterPars"):
                    for par in obj.iterPars(name=name):
                        yield par

        return

    def __iter__(self):
        """Iterate over top-level parameters."""
        return self._parameters.itervalues()

    def __len__(self):
        """Get number of top-level parameters."""
        return len(self._parameters)

    def __getitem__(self, idx):
        """Get top-level parameters by index."""
        return self._parameters.values()[idx]

    def __getattr__(self, name):
        """Gives access to the contained objects as attributes."""
        arg = self.get(name)
        if arg is None:
            raise AttributeError(name)
        return arg

    # Needed by __setattr__
    _parameters = {}
    __managed = {}

    def __setattr__(self, name, value):
        """Parameter access and object checking."""
        if name in self._parameters:
            par = self._parameters[name]
            if isinstance(value, Parameter):
                par.value = value.value
            else:
                par.value = value
            return

        m = self.get(name)
        if m is not None:
            raise AttributeError("Cannot set '%s'" % name)

        super(RecipeContainer, self).__setattr__(name, value)
        return

    def __delattr__(self, name):
        """Delete parameters with del.

        This does not allow deletion of non-parameters, as this may require
        configuration changes that are not yet handled in a general way.
        """
        if name in self._parameters:
            self._removeParameter(self._parameters[name])
            return

        m = self.get(name)
        if m is not None:
            raise AttributeError("Cannot delete '%s'" % name)

        super(RecipeContainer, self).__delattr__(name)
        return

    def get(self, name, default=None):
        """Get a managed object."""
        for d in self.__managed:
            arg = d.get(name)
            if arg is not None:
                return arg

        return default

    def getNames(self):
        """Get the names of managed parameters."""
        return [p.name for p in self._parameters.values()]

    def getValues(self):
        """Get the values of managed parameters."""
        return [p.value for p in self._parameters.values()]

    def _addObject(self, obj, d, check=True):
        """Add an object to a managed dictionary.

        obj     --  The object to be stored.
        d       --  The managed dictionary to store the object in.
        check   --  If True (default), a ValueError is raised an object of the
                    given name already exists.

        Raises ValueError if the object has no name.
        Raises ValueError if the object has the same name as some other managed
        object.
        """

        # Check name
        if not obj.name:
            message = "%s has no name" % obj.__class__.__name__
            raise ValueError(message)

        # Check for extant object in d with same name
        oldobj = d.get(obj.name)
        if check and oldobj is not None:
            message = "%s with name '%s' already exists" % (obj.__class__.__name__, obj.name)
            raise ValueError(message)

        # Check for object with same name in other dictionary.
        if oldobj is None and self.get(obj.name) is not None:
            message = "Non-%s with name '%s' already exists" % (obj.__class__.__name__, obj.name)
            raise ValueError(message)

        # Detach the old object, if there is one
        if oldobj is not None:
            oldobj.removeObserver(self._flush)

        # Add the object
        d[obj.name] = obj

        # Observe the object
        obj.addObserver(self._flush)

        # Store this as a configurable object
        self._storeConfigurable(obj)
        return

    def _removeObject(self, obj, d):
        """Remove an object from a managed dictionary.

        Raises ValueError if obj is not part of the dictionary.
        """
        if obj not in d.values():
            m = "'%s' is not part of the %s" % (obj, self.__class__.__name__)
            raise ValueError(m)

        del d[obj.name]
        obj.removeObserver(self._flush)

        return

    def _locateManagedObject(self, obj):
        """Find the location a managed object within the hierarchy.

        obj     --  The object to find.

        Returns a list of objects. The first member of the list is this object,
        and each subsequent member is a sub-object of the previous one.  The
        last entry in the list is obj. If obj cannot be found, the list is
        empty.
        """
        loc = [self]

        # This handles the case that an object is asked to locate itself.
        if obj is self:
            return loc

        for m in self._iterManaged():

            # Check locally for the object
            if m is obj:
                loc.append(obj)
                return loc

            # Check within managed objects
            if hasattr(m, "_locateManagedObject"):

                subloc = m._locateManagedObject(obj)
                if subloc:
                    return loc + subloc

        return []

    def _flush(self, other):
        """Invalidate cached state.

        This will force any observer to invalidate its state. By default this
        does nothing.
        """
        self.notify()
        return

    def _validate(self):
        """Validate my state.

        This validates that contained Parameters and managed objects are valid.

        Raises AttributeError if validation fails.
        """
        iterable = chain(self.__iter__(), self._iterManaged())
        self._validateOthers(iterable)
        return
class RecipeContainer(Observable, Configurable, Validatable):
    """Base class for organizing pieces of a FitRecipe.

    RecipeContainers are hierarchical organizations of Parameters and other
    RecipeContainers. This class provides attribute-access to these contained
    objects.  Parameters and other RecipeContainers can be found within the
    hierarchy with the _locateManagedObject method.

    A RecipeContainer can manage dictionaries for that store various objects.
    These dictionaries can be added to the RecipeContainer using the _manage
    method. RecipeContainer methods that add, remove or retrieve objects will
    work with any managed dictionary. This makes it easy to add new types of
    objects to be contained by a RecipeContainer. By default, the
    RecipeContainer is configured to manage an OrderedDict of Parameter
    objects.

    RecipeContainer is an Observable, and observes its managed objects and
    Parameters. This allows hierarchical calculation elements, such as
    ProfileGenerator, to detect changes in Parameters and Restraints on which
    it may depend.

    Attributes
    name            --  A name for this RecipeContainer. Names should be unique
                        within a RecipeContainer and should be valid attribute
                        names.
    _parameters     --  A managed OrderedDict of contained Parameters.
    __managed       --  A list of managed dictionaries. This is used for
                        attribute access, addition and removal.
    _configobjs     --  A set of configurable objects that must know of
                        configuration changes within this object.

    Properties
    names           --  Variable names (read only). See getNames.
    values          --  Variable values (read only). See getValues.
    """

    names = property(lambda self: self.getNames())
    values = property(lambda self: self.getValues())

    def __init__(self, name):
        Observable.__init__(self)
        Configurable.__init__(self)
        validateName(name)
        self.name = name
        self._parameters = OrderedDict()

        self.__managed = []
        self._manage(self._parameters)

        return

    def _manage(self, d):
        """Manage a dictionary of objects.

        This adds the dictionary to the __managed list. Dictionaries in
        __managed are used for attribute access, addition, and removal.
        """
        self.__managed.append(d)
        return

    def _iterManaged(self):
        """Get iterator over managed objects."""
        return chain(*(d.values() for d in self.__managed))

    def iterPars(self, name=".", recurse=True):
        """Iterate over Parameters.

        name    --  Select parameters with this name (regular expression,
                    default ".").
        recurse --  Recurse into managed objects (default True)
        """
        for par in self._parameters.itervalues():
            if re.match(name, par.name):
                yield par

        if not recurse:
            return

        # Iterate over objects within the managed dictionaries.
        managed = self.__managed[:]
        managed.remove(self._parameters)
        for m in managed:
            for obj in m.values():
                if hasattr(obj, "iterPars"):
                    for par in obj.iterPars(name=name):
                        yield par

        return

    def __iter__(self):
        """Iterate over top-level parameters."""
        return self._parameters.itervalues()

    def __len__(self):
        """Get number of top-level parameters."""
        return len(self._parameters)

    def __getitem__(self, idx):
        """Get top-level parameters by index."""
        return self._parameters.values()[idx]

    def __getattr__(self, name):
        """Gives access to the contained objects as attributes."""
        arg = self.get(name)
        if arg is None:
            raise AttributeError(name)
        return arg

    # Ensure there is no __dir__ override in the base class.
    assert (getattr(Observable, '__dir__', None) is getattr(
        Configurable, '__dir__', None) is getattr(Validatable, '__dir__', None)
            is getattr(object, '__dir__', None))

    def __dir__(self):
        "Return sorted list of attributes for this object."
        rv = set(dir(type(self)))
        rv.update(self.__dict__)
        # self.get fetches looks up for items in all managed dictionaries.
        # Add keys from each dictionary in self.__managed.
        rv.update(*self.__managed)
        rv = sorted(rv)
        return rv

    # Needed by __setattr__
    _parameters = {}
    __managed = {}

    def __setattr__(self, name, value):
        """Parameter access and object checking."""
        if name in self._parameters:
            par = self._parameters[name]
            if isinstance(value, Parameter):
                par.value = value.value
            else:
                par.value = value
            return

        m = self.get(name)
        if m is not None:
            raise AttributeError("Cannot set '%s'" % name)

        super(RecipeContainer, self).__setattr__(name, value)
        return

    def __delattr__(self, name):
        """Delete parameters with del.

        This does not allow deletion of non-parameters, as this may require
        configuration changes that are not yet handled in a general way.
        """
        if name in self._parameters:
            self._removeParameter(self._parameters[name])
            return

        m = self.get(name)
        if m is not None:
            raise AttributeError("Cannot delete '%s'" % name)

        super(RecipeContainer, self).__delattr__(name)
        return

    def get(self, name, default=None):
        """Get a managed object."""
        for d in self.__managed:
            arg = d.get(name)
            if arg is not None:
                return arg

        return default

    def getNames(self):
        """Get the names of managed parameters."""
        return [p.name for p in self._parameters.values()]

    def getValues(self):
        """Get the values of managed parameters."""
        return [p.value for p in self._parameters.values()]

    def _addObject(self, obj, d, check=True):
        """Add an object to a managed dictionary.

        obj     --  The object to be stored.
        d       --  The managed dictionary to store the object in.
        check   --  If True (default), a ValueError is raised an object of the
                    given name already exists.

        Raises ValueError if the object has no name.
        Raises ValueError if the object has the same name as some other managed
        object.
        """

        # Check name
        if not obj.name:
            message = "%s has no name" % obj.__class__.__name__
            raise ValueError(message)

        # Check for extant object in d with same name
        oldobj = d.get(obj.name)
        if check and oldobj is not None:
            message = "%s with name '%s' already exists"%\
                    (obj.__class__.__name__, obj.name)
            raise ValueError(message)

        # Check for object with same name in other dictionary.
        if oldobj is None and self.get(obj.name) is not None:
            message = "Non-%s with name '%s' already exists"%\
                    (obj.__class__.__name__, obj.name)
            raise ValueError(message)

        # Detach the old object, if there is one
        if oldobj is not None:
            oldobj.removeObserver(self._flush)

        # Add the object
        d[obj.name] = obj

        # Observe the object
        obj.addObserver(self._flush)

        # Store this as a configurable object
        self._storeConfigurable(obj)
        return

    def _removeObject(self, obj, d):
        """Remove an object from a managed dictionary.

        Raises ValueError if obj is not part of the dictionary.
        """
        if obj not in d.values():
            m = "'%s' is not part of the %s" % (obj, self.__class__.__name__)
            raise ValueError(m)

        del d[obj.name]
        obj.removeObserver(self._flush)

        return

    def _locateManagedObject(self, obj):
        """Find the location a managed object within the hierarchy.

        obj     --  The object to find.

        Returns a list of objects. The first member of the list is this object,
        and each subsequent member is a sub-object of the previous one.  The
        last entry in the list is obj. If obj cannot be found, the list is
        empty.
        """
        loc = [self]

        # This handles the case that an object is asked to locate itself.
        if obj is self:
            return loc

        for m in self._iterManaged():

            # Check locally for the object
            if m is obj:
                loc.append(obj)
                return loc

            # Check within managed objects
            if hasattr(m, "_locateManagedObject"):

                subloc = m._locateManagedObject(obj)
                if subloc:
                    return loc + subloc

        return []

    def _flush(self, other):
        """Invalidate cached state.

        This will force any observer to invalidate its state. By default this
        does nothing.
        """
        self.notify(other)
        return

    def _validate(self):
        """Validate my state.

        This validates that contained Parameters and managed objects are valid.

        Raises AttributeError if validation fails.
        """
        iterable = chain(self.__iter__(), self._iterManaged())
        self._validateOthers(iterable)
        return