#  ___________________________________________________________________________
#
#  Pyomo: Python Optimization Modeling Objects
#  Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC
#  Under the terms of Contract DE-NA0003525 with National Technology and
#  Engineering Solutions of Sandia, LLC, the U.S. Government retains certain
#  rights in this software.
#  This software is distributed under the 3-clause BSD License.
#  ___________________________________________________________________________

import logging
import sys
from copy import deepcopy
from pickle import PickleError
from weakref import ref as weakref_ref

import pyomo.common
from pyomo.common.deprecation import deprecated, relocated_module_attribute
from pyomo.common.factory import Factory
from pyomo.common.fileutils import StreamIndenter
from pyomo.common.formatting import tabular_writer
from pyomo.common.modeling import NOTSET
from pyomo.common.sorting import sorted_robust
from pyomo.core.pyomoobject import PyomoObject
from pyomo.core.base.component_namer import name_repr, index_repr

logger = logging.getLogger('pyomo.core')

relocated_module_attribute(
    'ComponentUID', 'pyomo.core.base.componentuid.ComponentUID',
    version='5.7.2')


class ModelComponentFactoryClass(Factory):

    def register(self, doc=None):
        def fn(cls):
            return super(ModelComponentFactoryClass, self).register(
                cls.__name__, doc)(cls)
        return fn

ModelComponentFactory = ModelComponentFactoryClass('model component')


def name(component, index=NOTSET, fully_qualified=False, relative_to=None):
    """
    Return a string representation of component for a specific
    index value.
    """
    base = component.getname(
        fully_qualified=fully_qualified, relative_to=relative_to
    )
    if index is NOTSET:
        return base
    else:
        if index not in component.index_set():
            raise KeyError( "Index %s is not valid for component %s"
                            % (index, component.name) )
        return base + index_repr( index )


@deprecated(msg="The cname() function has been renamed to name()",
            version='5.6.9')
def cname(*args, **kwds):
    return name(*args, **kwds)


class CloneError(pyomo.common.errors.PyomoException):
    pass

class _ComponentBase(PyomoObject):
    """A base class for Component and ComponentData

    This class defines some fundamental methods and properties that are
    expected for all Component-like objects.  They are centralized here
    to avoid repeated code in the Component and ComponentData classes.
    """
    __slots__ = ()

    _PPRINT_INDENT = "    "

    def is_component_type(self):
        """Return True if this class is a Pyomo component"""
        return True

    def __deepcopy__(self, memo):
        # The problem we are addressing is when we want to clone a
        # sub-block in a model.  In that case, the block can have
        # references to both child components and to external
        # ComponentData (mostly through expressions pointing to Vars
        # and Params outside this block).  For everything stored beneath
        # this block, we want to clone the Component (and all
        # corresponding ComponentData objects).  But for everything
        # stored outside this Block, we want to do a simple shallow
        # copy.
        #
        # Nominally, expressions only point to ComponentData
        # derivatives.  However, with the development of Expression
        # Templates (and the corresponding _GetItemExpression object),
        # expressions can refer to container (non-Simple) components, so
        # we need to override __deepcopy__ for both Component and
        # ComponentData.

        #try:
        #    print("Component: %s" % (self.name,))
        #except:
        #    print("DANGLING ComponentData: %s on %s" % (
        #        type(self),self.parent_component()))

        # Note: there is an edge case when cloning a block: the initial
        # call to deepcopy (on the target block) has __block_scope__
        # defined, however, the parent block of self is either None, or
        # is (by definition) out of scope.  So we will check that
        # id(self) is not in __block_scope__: if it is, then this is the
        # top-level block and we need to do the normal deepcopy.
        if '__block_scope__' in memo and \
                id(self) not in memo['__block_scope__']:
            _known = memo['__block_scope__']
            _new = []
            tmp = self.parent_block()
            tmpId = id(tmp)
            # Note: normally we would need to check that tmp does not
            # end up being None.  However, since clone() inserts
            # id(None) into the __block_scope__ dictionary, we are safe
            while tmpId not in _known:
                _new.append(tmpId)
                tmp = tmp.parent_block()
                tmpId = id(tmp)

            # Remember whether all newly-encountered blocks are in or
            # out of scope (prevent duplicate work)
            for _id in _new:
                _known[_id] = _known[tmpId]

            if not _known[tmpId]:
                # component is out-of-scope.  shallow copy only
                ans = memo[id(self)] = self
                return ans

        #
        # There is a particularly subtle bug with 'uncopyable'
        # attributes: if the exception is thrown while copying a complex
        # data structure, we can be in a state where objects have been
        # created and assigned to the memo in the try block, but they
        # haven't had their state set yet.  When the exception moves us
        # into the except block, we need to effectively "undo" those
        # partially copied classes.  The only way is to restore the memo
        # to the state it was in before we started.  Right now, our
        # solution is to make a (shallow) copy of the memo before each
        # operation and restoring it in the case of exception.
        # Unfortunately that is a lot of usually unnecessary work.
        # Since *most* classes are copyable, we will avoid that
        # "paranoia" unless the naive clone generated an error - in
        # which case Block.clone() will switch over to the more
        # "paranoid" mode.
        #
        paranoid = memo.get('__paranoid__', None)

        ans = memo[id(self)] = self.__class__.__new__(self.__class__)
        # We can't do the "obvious", since this is a (partially)
        # slot-ized class and the __dict__ structure is
        # nonauthoritative:
        #
        # for key, val in self.__dict__.iteritems():
        #     object.__setattr__(ans, key, deepcopy(val, memo))
        #
        # Further, __slots__ is also nonauthoritative (this may be a
        # singleton component -- in which case it also has a __dict__).
        # Plus, as this may be a derived class with several layers of
        # slots.  So, we will resort to partially "pickling" the object,
        # deepcopying the state dict, and then restoring the copy into
        # the new instance.
        #
        # [JDS 7/7/14] I worry about the efficiency of using both
        # getstate/setstate *and* deepcopy, but we need deepcopy to
        # update the _parent refs appropriately, and since this is a
        # slot-ized class, we cannot overwrite the __deepcopy__
        # attribute to prevent infinite recursion.
        state = self.__getstate__()
        try:
            if paranoid:
                saved_memo = dict(memo)
            new_state = deepcopy(state, memo)
        except:
            if paranoid:
                # Note: memo is intentionally pass-by-reference.  We
                # need to clear and reset the object we were handed (and
                # not overwrite it)
                memo.clear()
                memo.update(saved_memo)
            elif paranoid is not None:
                raise PickleError()
            new_state = {}
            for k, v in state.items():
                try:
                    if paranoid:
                        saved_memo = dict(memo)
                    new_state[k] = deepcopy(v, memo)
                except CloneError:
                    raise
                except:
                    if paranoid:
                        memo.clear()
                        memo.update(saved_memo)
                    elif paranoid is None:
                        logger.warning("""
                            Uncopyable field encountered when deep
                            copying outside the scope of Block.clone().
                            There is a distinct possibility that the new
                            copy is not complete.  To avoid this
                            situation, either use Block.clone() or set
                            'paranoid' mode by adding '__paranoid__' ==
                            True to the memo before calling
                            copy.deepcopy.""")
                    if self.model() is self:
                        what = 'Model'
                    else:
                        what = 'Component'
                    logger.error(
                        "Unable to clone Pyomo component attribute.\n"
                        "%s '%s' contains an uncopyable field '%s' (%s)"
                        % ( what, self.name, k, type(v) ))
                    # If this is an abstract model, then we are probably
                    # in the middle of create_instance, and the model
                    # that will eventually become the concrete model is
                    # missing initialization data.  This is an
                    # exceptional event worthy of a stronger (and more
                    # informative) error.
                    if not self.parent_component()._constructed:
                        raise CloneError(
                            "Uncopyable attribute (%s) encountered when "
                            "cloning component %s on an abstract block.  "
                            "The resulting instance is therefore "
                            "missing data from the original abstract model "
                            "and likely will not construct correctly.  "
                            "Consider changing how you initialize this "
                            "component or using a ConcreteModel."
                            % ( k, self.name ))
        ans.__setstate__(new_state)
        return ans

    @deprecated("""The cname() method has been renamed to getname().
    The preferred method of obtaining a component name is to use the
    .name property, which returns the fully qualified component name.
    The .local_name property will return the component name only within
    the context of the immediate parent container.""", version='5.0')
    def cname(self, *args, **kwds):
        return self.getname(*args, **kwds)

    def pprint(self, ostream=None, verbose=False, prefix=""):
        """Print component information

        Note that this method is generally only reachable through
        ComponentData objects in an IndexedComponent container.
        Components, including unindexed Component derivatives and both
        scalar and indexed IndexedComponent derivatives will see
        :py:meth:`Component.pprint()`
        """
        comp = self.parent_component()
        _attr, _data, _header, _fcn = comp._pprint()
        if isinstance(type(_data), str):
            # If the component _pprint only returned a pre-formatted
            # result, then we have no way to only emit the information
            # for this _data object.
            _name = comp.local_name
        else:
            # restrict output to only this data object
            _data = iter( ((self.index(), self),) )
            _name = "{Member of %s}" % (comp.local_name,)
        self._pprint_base_impl(
            ostream, verbose, prefix, _name, comp.doc,
            comp.is_constructed(), _attr, _data, _header, _fcn)

    @property
    def name(self):
        """Get the fully qualifed component name."""
        return self.getname(fully_qualified=True)

    # Adding a setter here to help users adapt to the new
    # setting. The .name attribute is now ._name. It should
    # never be assigned to by user code.
    @name.setter
    def name(self, val):
        raise ValueError(
            "The .name attribute is now a property method "
            "that returns the fully qualified component name. "
            "Assignment is not allowed.")

    @property
    def local_name(self):
        """Get the component name only within the context of
        the immediate parent container."""
        return self.getname(fully_qualified=False)

    @property
    def active(self):
        """Return the active attribute"""
        # Normal components cannot be deactivated
        return True

    @active.setter
    def active(self, value):
        """Set the active attribute to the given value"""
        raise AttributeError(
            "Setting the 'active' flag on a component that does not "
            "support deactivation is not allowed.")

    def _pprint_base_impl(self, ostream, verbose, prefix, _name, _doc,
                          _constructed, _attr, _data, _header, _fcn):
        if ostream is None:
            ostream = sys.stdout
        if prefix:
            ostream = StreamIndenter(ostream, prefix)

        # FIXME: HACK for backwards compatability with suppressing the
        # header for the top block
        if not _attr and self.parent_block() is None:
            _name = ''

        # We only indent everything if we printed the header
        if _attr or _name or _doc:
            ostream = StreamIndenter(ostream, self._PPRINT_INDENT)
            # The first line should be a hanging indent (i.e., not indented)
            ostream.newline = False

        if _name:
            ostream.write(_name+" : ")
        if _doc:
            ostream.write(_doc+'\n')
        if _attr:
            ostream.write(", ".join("%s=%s" % (k,v) for k,v in _attr))
        if _attr or _name or _doc:
            ostream.write("\n")

        if not _constructed:
            # HACK: for backwards compatability, Abstract blocks will
            # still print their assigned components.  Should we instead
            # always pprint unconstructed components (possibly
            # suppressing the table header if the table is empty)?
            if self.parent_block() is not None:
                ostream.write("Not constructed\n")
                return

        if type(_fcn) is tuple:
            _fcn, _fcn2 = _fcn
        else:
            _fcn2 = None

        if _header is not None:
            if _fcn2 is not None:
                _data_dict = dict(_data)
                _data = _data_dict.items()
            tabular_writer( ostream, '', _data, _header, _fcn )
            if _fcn2 is not None:
                for _key in sorted_robust(_data_dict):
                    _fcn2(ostream, _key, _data_dict[_key])
        elif _fcn is not None:
            _data_dict = dict(_data)
            for _key in sorted_robust(_data_dict):
                _fcn(ostream, _key, _data_dict[_key])
        elif _data is not None:
            ostream.write(_data)


class Component(_ComponentBase):
    """
    This is the base class for all Pyomo modeling components.

    Constructor arguments:
        ctype           The class type for the derived subclass
        doc             A text string describing this component
        name            A name for this component

    Public class attributes:
        doc             A text string describing this component

    Private class attributes:
        _constructed    A boolean that is true if this component has been
                            constructed
        _parent         A weakref to the parent block that owns this component
        _ctype          The class type for the derived subclass
    """

    def __init__ (self, **kwds):
        #
        # Get arguments
        #
        self._ctype = kwds.pop('ctype', None)
        self.doc    = kwds.pop('doc', None)
        self._name  = kwds.pop('name', str(type(self).__name__))
        if kwds:
            raise ValueError(
                "Unexpected keyword options found while constructing '%s':\n\t%s"
                % ( type(self).__name__, ','.join(sorted(kwds.keys())) ))
        #
        # Verify that ctype has been specified.
        #
        if self._ctype is None:
            raise pyomo.common.DeveloperError(
                "Must specify a component type for class %s!"
                % ( type(self).__name__, ) )
        #
        self._constructed   = False
        self._parent        = None    # Must be a weakref

    def __getstate__(self):
        """
        This method must be defined to support pickling because this class
        owns weakrefs for '_parent'.
        """
        #
        # Nominally, __getstate__() should return:
        #
        # state = super(Class, self).__getstate__()
        # for i in Class.__dict__:
        #     state[i] = getattr(self,i)
        # return state
        #
        # However, in this case, the (nominal) parent class is 'object',
        # and object does not implement __getstate__.  So, we will check
        # to make sure that there is a base __getstate__() to call...
        #
        _base = super(Component,self)
        if hasattr(_base, '__getstate__'):
            state = _base.__getstate__()
            for key,val in self.__dict__.items():
                if key not in state:
                    state[key] = val
        else:
            state = dict(self.__dict__)
        if self._parent is not None:
            state['_parent'] = self._parent()
        return state

    def __setstate__(self, state):
        """
        This method must be defined to support pickling because this class
        owns weakrefs for '_parent'.
        """
        if state['_parent'] is not None and \
                type(state['_parent']) is not weakref_ref:
            state['_parent'] = weakref_ref(state['_parent'])
        #
        # Note: our model for setstate is for derived classes to modify
        # the state dictionary as control passes up the inheritance
        # hierarchy (using super() calls).  All assignment of state ->
        # object attributes is handled at the last class before 'object'
        # (which may -- or may not (thanks to MRO) -- be here.
        #
        _base = super(Component,self)
        if hasattr(_base, '__setstate__'):
            _base.__setstate__(state)
        else:
            for key, val in state.items():
                # Note: per the Python data model docs, we explicitly
                # set the attribute using object.__setattr__() instead
                # of setting self.__dict__[key] = val.
                object.__setattr__(self, key, val)

    @property
    def ctype(self):
        """Return the class type for this component"""
        return self._ctype

    @deprecated("Component.type() method has been replaced by the "
                ".ctype property.", version='5.7')
    def type(self):
        """Return the class type for this component"""
        return self.ctype

    def construct(self, data=None):                     #pragma:nocover
        """API definition for constructing components"""
        pass

    def is_constructed(self):                           #pragma:nocover
        """Return True if this class has been constructed"""
        return self._constructed

    def reconstruct(self, data=None):
        """REMOVED: reconstruct() was removed in Pyomo 6.0.

        Re-constructing model components was fragile and did not
        correctly update instances of the component used in other
        components or contexts (this was particularly problemmatic for
        Var, Param, and Set).  Users who wish to reproduce the old
        behavior of reconstruct(), are comfortable manipulating
        non-public interfaces, and who take the time to verify that the
        correct thing happens to their model can approximate the old
        behavior of reconstruct with:

            component.clear()
            component._constructed = False
            component.construct()

        """
        raise AttributeError(self.reconstruct.__doc__)

    def valid_model_component(self):
        """Return True if this can be used as a model component."""
        return True

    def pprint(self, ostream=None, verbose=False, prefix=""):
        """Print component information"""
        self._pprint_base_impl(
            ostream, verbose, prefix, self.local_name, self.doc,
            self.is_constructed(), *self._pprint()
        )

    def display(self, ostream=None, verbose=False, prefix=""):
        self.pprint(ostream=ostream, prefix=prefix)

    def parent_component(self):
        """Returns the component associated with this object."""
        return self

    def parent_block(self):
        """Returns the parent of this object."""
        if self._parent is None:
            return None
        else:
            return self._parent()

    def model(self):
        """Returns the model associated with this object."""
        # This is a re-implementation of Component.parent_block(),
        # duplicated for effficiency to avoid the method call
        if self._parent is None:
            return None
        ans = self._parent()

        if ans is None:
            return None
        # Defer to the (simple) block's model() method to walk up the
        # hierarchy. This is because the top-level block can be a model,
        # but nothing else (e.g., calling model() on a Var not attached
        # to a model should return None, but calling model() on a Block
        # not attached to anything else should return the Block)
        return ans.model()

    def root_block(self):
        """Return self.model()"""
        return self.model()

    def __str__(self):
        """Return the component name"""
        return self.name

    def to_string(self, verbose=None, labeler=None, smap=None, compute_values=False):
        """Return the component name"""
        if compute_values:
            try:
                return str(self())
            except:
                pass
        return self.name

    def getname(self, fully_qualified=False, name_buffer=None, relative_to=None):
        """Returns the component name associated with this object.

        Parameters
        ----------
        fully_qualified: bool
            Generate full name from nested block names

        name_buffer: dict
            A dictionary that caches encountered names and indices.
            Providing a ``name_buffer`` can significantly speed up
            iterative name generation

        relative_to: Block
            Generate fully_qualified names reletive to the specified block.
        """
        local_name = self._name
        if fully_qualified:
            pb = self.parent_block()
            if relative_to is None:
                relative_to = self.model()
            if pb is not None and pb is not relative_to:
                ans = pb.getname(fully_qualified, name_buffer, relative_to) \
                      + "." + name_repr(local_name)
            elif pb is None and relative_to != self.model():
                raise RuntimeError(
                    "The relative_to argument was specified but not found "
                    "in the block hierarchy: %s" % str(relative_to))
            else:
                ans = name_repr(local_name)
        else:
            # Note: we want "getattr(x.parent_block(), x.local_name) == x"
            # so we do not want to call _safe_name_str, as that could
            # add quotes or otherwise escape the string.
            ans = local_name
        if name_buffer is not None:
            name_buffer[id(self)] = ans
        return ans

    @property
    def name(self):
        """Get the fully qualifed component name."""
        return self.getname(fully_qualified=True)

    # Allow setting a componet's name if it is not owned by a parent
    # block (this supports, e.g., naming a model)
    @name.setter
    def name(self, val):
        if self.parent_block() is None:
            self._name = val
        else:
            raise ValueError(
                "The .name attribute is not settable when the component "
                "is assigned to a Block.\nTriggered by attempting to set "
                "component '%s' to name '%s'" % (self.name,val))

    def is_indexed(self):
        """Return true if this component is indexed"""
        return False

    def clear_suffix_value(self, suffix_or_name, expand=True):
        """Clear the suffix value for this component data"""
        if isinstance(suffix_or_name, str):
            import pyomo.core.base.suffix
            for name_, suffix_ in pyomo.core.base.suffix.active_suffix_generator(self.model()):
                if suffix_or_name == name_:
                    suffix_.clear_value(self, expand=expand)
                    break
        else:
            suffix_or_name.clear_value(self, expand=expand)

    def set_suffix_value(self, suffix_or_name, value, expand=True):
        """Set the suffix value for this component data"""
        if isinstance(suffix_or_name, str):
            import pyomo.core.base.suffix
            for name_, suffix_ in pyomo.core.base.suffix.active_suffix_generator(self.model()):
                if suffix_or_name == name_:
                    suffix_.set_value(self, value, expand=expand)
                    break
        else:
            suffix_or_name.set_value(self, value, expand=expand)

    def get_suffix_value(self, suffix_or_name, default=None):
        """Get the suffix value for this component data"""
        if isinstance(suffix_or_name, str):
            import pyomo.core.base.suffix
            for name_, suffix_ in pyomo.core.base.suffix.active_suffix_generator(self.model()):
                if suffix_or_name == name_:
                    return suffix_.get(self, default)
        else:
            return suffix_or_name.get(self, default)


class ActiveComponent(Component):
    """A Component that makes semantic sense to activate or deactivate
    in a model.

    Private class attributes:
        _active         A boolean that is true if this component will be
                            used in model operations
    """

    def __init__(self, **kwds):
        self._active = True
        super(ActiveComponent, self).__init__(**kwds)

    @property
    def active(self):
        """Return the active attribute"""
        return self._active

    @active.setter
    def active(self, value):
        """Set the active attribute to the given value"""
        raise AttributeError(
            "Assignment not allowed. Use the (de)activate methods." )

    def activate(self):
        """Set the active attribute to True"""
        self._active=True

    def deactivate(self):
        """Set the active attribute to False"""
        self._active=False


class ComponentData(_ComponentBase):
    """
    This is the base class for the component data used
    in Pyomo modeling components.  Subclasses of ComponentData are
    used in indexed components, and this class assumes that indexed
    components are subclasses of IndexedComponent.  Note that
    ComponentData instances do not store their index.  This makes
    some operations significantly more expensive, but these are (a)
    associated with I/O generation and (b) this cost can be managed
    with caches.

    Constructor arguments:
        owner           The component that owns this data object

    Private class attributes:
        _component      A weakref to the component that owns this data object
        """

    __pickle_slots__ = ('_component',)
    __slots__ = __pickle_slots__ + ('__weakref__',)

    def __init__(self, component):
        #
        # ComponentData objects are typically *private* objects for
        # indexed / sparse indexed components.  As such, the (derived)
        # class needs to make sure that the owning component is *always*
        # passed as the owner (and that owner is never None).  Not validating
        # this assumption is significantly faster.
        #
        self._component = weakref_ref(component)

    def __getstate__(self):
        """Prepare a picklable state of this instance for pickling.

        Nominally, __getstate__() should return:

            state = super(Class, self).__getstate__()
            for i in Class.__slots__:
                state[i] = getattr(self,i)
            return state

        However, in this case, the (nominal) parent class is 'object',
        and object does not implement __getstate__.  So, we will check
        to make sure that there is a base __getstate__() to call...
        You might think that there is nothing to check, but multiple
        inheritance could mean that another class got stuck between
        this class and "object" in the MRO.

        This method must be defined to support pickling because this
        class owns weakrefs for '_component', which must be either
        removed or converted to hard references prior to pickling.

        Further, since there is only a single slot, and that slot
        (_component) requires special processing, we will just deal with
        it explicitly.  As _component is a weakref (not pickable), we
        need to resolve it to a concrete object.
        """
        _base = super(ComponentData,self)
        if hasattr(_base, '__getstate__'):
            state = _base.__getstate__()
        else:
            state = {}
        #
        if self._component is None:
            state['_component'] = None
        else:
            state['_component'] = self._component()
        return state

    def __setstate__(self, state):
        """Restore a pickled state into this instance

        Note: our model for setstate is for derived classes to modify
        the state dictionary as control passes up the inheritance
        hierarchy (using super() calls).  All assignment of state ->
        object attributes is handled at the last class before 'object'
        (which may -- or may not (thanks to MRO) -- be here.

        This method must be defined to support unpickling because this
        class owns weakrefs for '_component', which must be restored
        from the hard references used in the piclke.
        """
        #
        # FIXME: We shouldn't have to check for weakref.ref here, but if
        # we don't the model cloning appears to fail (in the Benders
        # example)
        #
        if state['_component'] is not None and \
                type(state['_component']) is not weakref_ref:
            state['_component'] = weakref_ref(state['_component'])
        #
        # Note: our model for setstate is for derived classes to modify
        # the state dictionary as control passes up the inheritance
        # hierarchy (using super() calls).  All assignment of state ->
        # object attributes is handled at the last class before 'object'
        # (which may -- or may not (thanks to MRO) -- be here.
        #
        _base = super(ComponentData,self)
        if hasattr(_base, '__setstate__'):
            _base.__setstate__(state)
        else:
            for key, val in state.items():
                # Note: per the Python data model docs, we explicitly
                # set the attribute using object.__setattr__() instead
                # of setting self.__dict__[key] = val.
                object.__setattr__(self, key, val)

    @property
    def ctype(self):
        """Return the class type for this component"""
        _parent = self.parent_component()
        if _parent is None:
            return None
        return _parent._ctype

    @deprecated("Component.type() method has been replaced by the "
                ".ctype property.", version='5.7')
    def type(self):
        """Return the class type for this component"""
        return self.ctype

    def parent_component(self):
        """Returns the component associated with this object."""
        if self._component is None:
            return None
        return self._component()

    def parent_block(self):
        """Return the parent of the component that owns this data. """
        # This is a re-implementation of parent_component(), duplicated
        # for effficiency to avoid the method call
        if self._component is None:
            return None
        comp = self._component()

        # This is a re-implementation of Component.parent_block(),
        # duplicated for effficiency to avoid the method call
        if comp._parent is None:
            return None
        return comp._parent()

    def model(self):
        """Return the model of the component that owns this data. """
        ans = self.parent_block()
        if ans is None:
            return None
        # Defer to the (simple) block's model() method to walk up the
        # hierarchy. This is because the top-level block can be a model,
        # but nothing else (e.g., calling model() on a Var not attached
        # to a model should return None, but calling model() on a Block
        # not attached to anything else should return the Block)
        return ans.model()

    def index(self):
        """
        Returns the index of this ComponentData instance relative
        to the parent component index set. None is returned if
        this instance does not have a parent component, or if
        - for some unknown reason - this instance does not belong
        to the parent component's index set. This method is not
        intended to be a fast method;  it should be used rarely,
        primarily in cases of label formulation.
        """
        self_component = self.parent_component()
        if self_component is None:
            return None
        for idx, component_data in self_component.items():
            if component_data is self:
                return idx
        return None

    def __str__(self):
        """Return a string with the component name and index"""
        return self.name

    def to_string(self, verbose=None, labeler=None, smap=None, compute_values=False):
        """
        Return a string representation of this component,
        applying the labeler if passed one.
        """
        if compute_values:
            try:
                return str(self())
            except:
                pass
        if smap:
            return smap.getSymbol(self, labeler)
        if labeler is not None:
            return labeler(self)
        else:
            return self.__str__()

    def getname(self, fully_qualified=False, name_buffer=None, relative_to=None):
        """Return a string with the component name and index"""
        #
        # Using the buffer, which is a dictionary:  id -> string
        #
        if name_buffer is not None and id(self) in name_buffer:
            # Return the name if it is in the buffer
            return name_buffer[id(self)]

        c = self.parent_component()
        if c is self:
            #
            # This is a scalar component, so call the
            # Component.getname() method
            #
            return super(ComponentData, self).getname(
                fully_qualified, name_buffer, relative_to)
        elif c is not None:
            #
            # Get the name of the parent component
            #
            base = c.getname(fully_qualified, name_buffer, relative_to)
        else:
            #
            # Defensive: this is a ComponentData without a valid
            # parent_component.  As this usually occurs when handling
            # exceptions during model construction, we need to ensure
            # that this method doesn't itself raise another exception.
            #
            return '[Unattached %s]' % (type(self).__name__,)

        if name_buffer is not None:
            # Iterate through the dictionary and generate all names in
            # the buffer
            for idx, obj in c.items():
                name_buffer[id(obj)] = base + index_repr(idx)
            if id(self) in name_buffer:
                # Return the name if it is in the buffer
                return name_buffer[id(self)]
        else:
            #
            # No buffer, so we iterate through the component _data
            # dictionary until we find this object.  This can be much
            # more expensive than if a buffer is provided.
            #
            for idx, obj in c.items():
                if obj is self:
                    return base + index_repr(idx)
        #
        raise RuntimeError("Fatal error: cannot find the component data in "
                           "the owning component's _data dictionary.")

    def is_indexed(self):
        """Return true if this component is indexed"""
        return False

    def clear_suffix_value(self, suffix_or_name, expand=True):
        """Set the suffix value for this component data"""
        if isinstance(suffix_or_name, str):
            import pyomo.core.base.suffix
            for name_, suffix_ in pyomo.core.base.suffix.active_suffix_generator(self.model()):
                if suffix_or_name == name_:
                    suffix_.clear_value(self, expand=expand)
                    break
        else:
            suffix_or_name.clear_value(self, expand=expand)

    def set_suffix_value(self, suffix_or_name, value, expand=True):
        """Set the suffix value for this component data"""
        if isinstance(suffix_or_name, str):
            import pyomo.core.base.suffix
            for name_, suffix_ in pyomo.core.base.suffix.active_suffix_generator(self.model()):
                if suffix_or_name == name_:
                    suffix_.set_value(self, value, expand=expand)
                    break
        else:
            suffix_or_name.set_value(self, value, expand=expand)

    def get_suffix_value(self, suffix_or_name, default=None):
        """Get the suffix value for this component data"""
        if isinstance(suffix_or_name, str):
            import pyomo.core.base.suffix
            for name_, suffix_ in pyomo.core.base.suffix.active_suffix_generator(self.model()):
                if suffix_or_name == name_:
                    return suffix_.get(self, default)
        else:
            return suffix_or_name.get(self, default)


class ActiveComponentData(ComponentData):
    """
    This is the base class for the component data used
    in Pyomo modeling components that can be activated and
    deactivated.

    It's possible to end up in a state where the parent Component
    has _active=True but all ComponentData have _active=False. This
    seems like a reasonable state, though we cannot easily detect
    this situation.  The important thing to avoid is the situation
    where one or more ComponentData are active, but the parent
    Component claims active=False. This class structure is designed
    to prevent this situation.

    Constructor arguments:
        owner           The component that owns this data object

    Private class attributes:
        _component      A weakref to the component that owns this data object
        _active         A boolean that indicates whether this data is active
    """

    __slots__ = ( '_active', )

    def __init__(self, component):
        super(ActiveComponentData, self).__init__(component)
        self._active = True

    def __getstate__(self):
        """
        This method must be defined because this class uses slots.
        """
        result = super(ActiveComponentData, self).__getstate__()
        for i in ActiveComponentData.__slots__:
            result[i] = getattr(self, i)
        return result

    # Since this class requires no special processing of the state
    # dictionary, it does not need to implement __setstate__()

    @property
    def active(self):
        """Return the active attribute"""
        return self._active

    @active.setter
    def active(self, value):
        """Set the active attribute to a specified value."""
        raise AttributeError(
            "Assignment not allowed. Use the (de)activate method" )

    def activate(self):
        """Set the active attribute to True"""
        self._active = self.parent_component()._active = True

    def deactivate(self):
        """Set the active attribute to False"""
        self._active = False

