#
# $Id: Ownable.py,v 1.6.16.1 2005/03/10 18:47:18 snyder Exp $
#
# File: Ownership/Ownable.py
# Purpose: Something which can be owned.
# Created: Oct, 1998, sss
#
# See __init__.py for a summary of this package.
#

class Ownable:

    # This represents some object which can be owned.
    # Such an object can be in one of four ownership states:
    #  FREE: Not owned by anyone.
    #  EXCLUSIVE: Owned by exactly one owner.
    #  SHARED: Owned, but possibly by more than one owner.
    #  PARASITIC: Like SHARED, except that someone else may allocate
    #   the object in either PARASITIC, SHARED, or EXCLUSIVE mode.
    #   The other owner will be permitted to change some attributes
    #   of the object.
    #

    # state:
    # __ownmode
    #   The current ownership mode of the object.
    #
    # __owners
    #   A tuple of the owners of this object.
    #
    # __parasitic_owners
    #   A tuple of the owners that have allocated this object parasitically.
    #
    # __shareable
    #   True if this object may be shared between owners.
    #
    # __parasitic_allowed
    #   True if this object may be allocated in parasitic mode.

    #
    # Strings representing the ownership modes.
    #
    FREE = "free"
    SHARED = "shared"
    EXCLUSIVE = "exclusive"
    PARASITIC = "parasitic"

    #
    # Constructor.
    # The object starts off free.
    # If SHAREABLE is false, this object may not be shared between owners.
    # If PARASITIC_ALLOWED is true, this object may be allocated
    # in parasitic mode.
    #
    def __init__ (self, shareable = 1, parasitic_allowed = 0):
        self.__ownmode = Ownable.FREE
        self.__owners = ()
        self.__parasitic_owners = ()
        self.__shareable = shareable
        self.__parasitic_allowed = parasitic_allowed
        # If parasitic, must also be shareable.
        assert not (not shareable and parasitic_allowed)
        return

    #
    # Try to acquire this object for OWNER using OWNMODE.
    # Returns:
    #   1 if successful, and we didn't previously own the item.
    #   2 if successful, and we previously owned the item.
    #   0 if unsuccessful.
    #
    def acquire_for (self, owner, ownmode):

        # OWNMODE must be either shared, exclusive, or parasitic
        assert (ownmode == Ownable.SHARED or
                ownmode == Ownable.EXCLUSIVE or
                ownmode == Ownable.PARASITIC)

        # If requested, disallow sharing (and parasitic allocation).
        if not self.__shareable:
            ownmode = Ownable.EXCLUSIVE

        # Fail for parasitic allocation if this object doesn't allow it.
        if ownmode == Ownable.PARASITIC and not self.__parasitic_allowed:
            return 0

        if self.owned_by (owner):
            # Trying to acquire already owned object.
            # Make sure the ownership mode agrees.
            if ownmode == self.__ownmode:
                # Same as current state.  Ok.
                return 2

            if ownmode == Ownable.EXCLUSIVE and len (self.__owners) == 1:
                # Allow upgrading to exclusive if we're the only owner.
                self.__ownmode = ownmode
                return 2

            # Otherwise, not allowed.
            return 0
                

        # Verify ownership mode.
        if ownmode == Ownable.PARASITIC or self.__ownmode == Ownable.PARASITIC:
            # Parasitic is compatible with both shared and exclusive.
            pass
        elif (((ownmode == Ownable.EXCLUSIVE) and 
             (self.__ownmode != Ownable.FREE)) or
            self.__ownmode == Ownable.EXCLUSIVE):
            return 0

        # Ok.  Add this owner to the list.
        self.__owners = self.__owners + (owner,)

        # Handle parasitic allocation.
        # Add to the parasitic owner list, and adjust the ownership
        # mode if needed.
        if ownmode == Ownable.PARASITIC:
            self.__parasitic_owners = self.__parasitic_owners + (owner,)
            if self.__ownmode == Ownable.FREE:
                self.__ownmode = ownmode
            return 1

        # For non-parasitic allocations, the new mode is always the
        # requested mode.
        self.__ownmode = ownmode

        return 1

    #
    # Precondition: OWNER owns this object.
    # Release this object, so that it is no longer owned by OWNER.
    #
    def release_by (self, owner):
        # It's an error if we're not owned by OWNER.
        assert (self.owned_by (owner))

        # Remove the owner from the list.
        self.__owners = filter (lambda x, owner=owner: x != owner,
                                self.__owners)
        self.__parasitic_owners = filter (lambda x, owner=owner: x != owner,
                                          self.__parasitic_owners)

        # If there are no more owners, the object becomes free.
        if len (self.__owners) == 0:
            self.__ownmode = Ownable.FREE

        # Otherwise, if there are no non-parasitic owners, the object changes
        # to parasitic mode.
        elif len (self.__owners) == len (self.__parasitic_owners):
            self.__ownmode = Ownable.PARASITIC
        return

    #
    # Return true if this object is currently free.
    #
    def is_free (self):
        return self.__ownmode == Ownable.FREE

    #
    # Return the number of owners of this object.
    #
    def n_owners (self):
        return len (self.__owners)

    #
    # Return the number of non-parasitic owners of this object.
    #
    def n_nonparasitic_owners (self):
        return len (self.__owners) - len (self.__parasitic_owners)

    #
    # Return true if this object is owned by OWNER.
    #
    def owned_by (self, owner):
        return owner in self.__owners

    #
    # Return true if this object is parasitically owned by OWNER.
    #
    def owned_by_parasitically (self, owner):
        return owner in self.__parasitic_owners

    #
    # Return TRUE if OWNER is the only non-parasitic owner of this object.
    def only_nonparasitic_owner (self, owner):
        return (self.owned_by (owner) and
                not self.owned_by_parasitically (owner) and
                len (self.__owners) == len (self.__parasitic_owners) + 1)

    #
    # Return the current ownership mode for this object.
    #
    def ownmode (self):
        return self.__ownmode

    #
    # Return the list of owners of this object.
    #
    def owners (self):
        return self.__owners

    #
    # Return the shareable flag.
    #
    def shareable (self):
        return self.__shareable
    

    #
    # Return the parasitic_allowed flag.
    #
    def parasitic_allowed (self):
        return self.__parasitic_allowed