# -*- coding: iso-8859-15 -*-


# Glasnost
# By: Odile Bénassy <obenassy@entrouvert.com>
#     Romain Chantereau <rchantereau@entrouvert.com>
#     Nicolas Clapiès <nclapies@easter-eggs.org>
#     Pierre-Antoine Dejace <padejace@entrouvert.be>
#     Thierry Dulieu <tdulieu@easter-eggs.com>
#     Florent Monnier <monnier@codelutin.com>
#     Cédric Musso <cmusso@easter-eggs.org>
#     Frédéric Péters <fpeters@entrouvert.be>
#     Benjamin Poussin <poussin@codelutin.com>
#     Emmanuel Raviart <eraviart@entrouvert.com>
#     Sébastien Régnier <regnier@codelutin.com>
#     Emmanuel Saracco <esaracco@easter-eggs.com>
#
# Copyright (C) 2000, 2001 Easter-eggs & Emmanuel Raviart
# Copyright (C) 2002 Odile Bénassy, Code Lutin, Thierry Dulieu, Easter-eggs,
#     Entr'ouvert, Frédéric Péters, Benjamin Poussin, Emmanuel Raviart,
#     Emmanuel Saracco & Théridion
# Copyright (C) 2003 Odile Bénassy, Romain Chantereau, Nicolas Clapiès,
#     Code Lutin, Pierre-Antoine Dejace, Thierry Dulieu, Easter-eggs,
#     Entr'ouvert, Florent Monnier, Cédric Musso, Ouvaton, Frédéric Péters,
#     Benjamin Poussin, Rodolphe Quiédeville, Emmanuel Raviart, Sébastien
#     Régnier, Emmanuel Saracco, Théridion & Vecam
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.


__doc__ = """Glasnost Common Slots"""

__version__ = '$Revision$'[11:-2]


import copy
import types

import faults
import tools_new as commonTools


class BaseSlot:
    
    """Slot superclass.
    
    This class must be specialized in order to produce a specific slot
    (Attribute, Property...).
    
    Attributes:
    ===========

    *container*:
        The object class instance containing the slot.

    *parent*:
        This is the slot where the object is (if applicable). Default = None.
    
    *value*:
        The slot value.
        
    Constructor:
    ============

        Keyword arguments:
        ------------------
        
        *container*:
            The object class instance containing the slot.

        *parent*:
            This is the slot where the object is (if applicable). Default =
            None.

        Exception:
        ----------

        *AssertionError*:
            The container is None, and the parent too. That's meaning that the
            slot is neither in a class, neither in another slot, so nowhere.
            
    """
    
    container = None
    parent = None
    value = None

    def __init__(self, container = None, parent = None):
        if parent is None:
            assert container is not None
            self.container = container
        else:
            if container is not None:
                self.container = container
            self.parent = parent

    def __str__(self):
        return self.getPath()

    def delField(self, fields):
        fieldName = self.getFieldName()
        del fields[fieldName]

    def delFieldOption(self, fields, optionName):
        fieldOptionName = self.getFieldOptionName(optionName)
        del fields[fieldOptionName]

    def delValue(self):
        if self.__dict__.has_key('value'):
            del self.value
        raise NotImplementedError

    def detach(self, value):
        return Detached(self, value)

    def getContainer(self, setIfNone = 0):
        if self.container is not None:
            return self.container
        else:
            return self.parent.getValue(setIfNone = setIfNone)

    def getContainerFieldName(self):
        if self.parent is None:
            return ''
        else:
            return self.parent.getFieldName()

    def getContainerFilePathSplitted(self):
        if self.container is not None:
            return []
        else:
            return self.parent.getFilePathSplitted()

    def getContainerPath(self):
        if self.parent is None:
            return 'self'
        else:
            return self.parent.getPath()

    def getField(self, fields, default = None):
        fieldName = self.getFieldName()
        if not fields.has_key(fieldName):
            return default
        return fields[fieldName]

    def getFieldName(self):
        raise NotImplementedError

    def getFieldOption(self, fields, optionName, default = None):
        fieldOptionName = self.getFieldOptionName(optionName)
        if not fields or not fields.has_key(fieldOptionName):
            return default
        return fields[fieldOptionName]

    def getFieldOptionName(self, optionName):
        return '_'.join([self.getFieldName(), optionName])

    def getFilePathSplitted(self):
        raise NotImplementedError

    def getKind(self):
        raise NotImplementedError

    def getLabel(self):
        raise NotImplementedError

    def getLocalValue(self, setIfNone = 0):
        raise NotImplementedError

    def getObject(self):
        if self.container is not None and \
           self.container.thingCategory == 'object':
            return self.container
        elif self.parent is not None:
            return self.parent.getObject()
        else:
            return None

    def getObjectSlot(self):
        if self.container is not None and \
           self.container.thingCategory == 'object':
            return self.parent
        elif self.parent is not None:
            return self.parent.getObjectSlot()
        else:
            return None

    def getPath(self):
        raise NotImplementedError

    def getRootContainer(self):
        if self.parent is None:
            return self.container
        else:
            return self.parent.getRootContainer()

    def getValue(self, setIfNone = 0):
        if self.value is not None:
            return self.value
        return self.getLocalValue(setIfNone = setIfNone)

    def getWidget(self):
        return self.getKind().getModelWidget(self)

    def hasField(self, fields):
        return fields.has_key(self.getFieldName())

    def hasFieldOption(self, fields, optionName):
        return fields.has_key(self.getFieldOptionName(optionName))

    def hasLocalValue(self):
        raise NotImplementedError

    def hasValue(self):
        if self.value is not None:
            return 1
        return self.hasLocalValue()

    def isDefaultValue(self, value):
        return 0

    def newJail(self):
        return self.getKind().newJail(self)

    def setField(self, fields, value):
        fieldName = self.getFieldName()
        fields[fieldName] = value

    def setFieldOption(self, fields, optionName, value):
        fieldOptionName = self.getFieldOptionName(optionName)
        fields[fieldOptionName] = value

    def setValue(self, value):
        if self.__dict__.has_key('value'):
            del self.value
        raise NotImplementedError


class Attribute(BaseSlot): # Instance Attribute.
    
    """Define a attribute slot.
    
    Attribute:
    ==========

    *name*:
        The attribute slot name.

    Constructor:
    ============
        
        Keyword arguments:
        ------------------

        *name*:
            The attribute slot name.

        *container*:
            The object class instance containing the slot.

        *parent*:
            This is the slot where the object is (if applicable). Default =
            None.
            
        Exception:
        ----------

        *AssertionError*:
            The container is None, and the parent too. That's meaning that the
            slot is neither in a class, neither in another slot, so nowhere.
        
    """
    
    name = None

    def __init__(self, name, container = None, parent = None):
        BaseSlot.__init__(self, container = container, parent = parent)
        self.name = name

    def delValue(self):
        container = self.getContainer()
        if container.__dict__.has_key(self.name):
            del container.__dict__[self.name]
        if self.__dict__.has_key('value'):
            del self.value

    def getFieldName(self):
        return '_'.join(filter(None, [
            self.getContainerFieldName(),
            self.name]))

    def getFilePathSplitted(self):
        return self.getContainerFilePathSplitted() + [self.name]

    def getKind(self):
        # To simplify when converting to Python 2.2 new-style objects.
        container = self.getContainer()
        if hasattr(container, self.name + '_kindGetter'):
            return getattr(container, self.name + '_kindGetter')(self)
        return getattr(container, self.name + '_kind')

    def getLabel(self):
        return self.name

    def getLocalValue(self, setIfNone = 0):
        container = self.getContainer(setIfNone = setIfNone)
# FIXME: Shall we uncomment the next lines?
##         if hasattr(container, self.name + 'Getter'):
##             return getattr(container, self.name + 'Getter')(self)
        return container.__dict__[self.name]

    def getPath(self):
        return '%s.%s' % (self.getContainerPath(), self.name)

    def getValue(self, setIfNone = 0):
        # To simplify when converting to Python 2.2 new-style objects.
        if self.value is not None:
            return self.value
        kind = self.getKind()
        if kind.getter is not None:
            return kind.getter(self)
        container = self.getContainer(setIfNone = setIfNone)
        # FIXME: To remove, since it is now easy to use the kind.getter method.
        if hasattr(container, self.name + 'Getter'):
            return getattr(container, self.name + 'Getter')(self)
        return getattr(container, self.name)

    def hasLocalValue(self):
        container = self.getContainer()
        return container.__dict__.has_key(self.name)

    def hasValue(self):
        if self.value is not None:
            return 1
        return hasattr(self.getContainer(), self.name)

    def isDefaultValue(self, value):
        containerClass = self.getContainer().__class__
        return hasattr(containerClass, self.name) \
               and getattr(containerClass, self.name) == value

    def setValue(self, value):
        container = self.getContainer(setIfNone = 1)
        if hasattr(container.__class__, self.name) \
           and getattr(container.__class__, self.name) == value:
            if container.__dict__.has_key(self.name):
                del container.__dict__[self.name]
        else:
            setattr(container, self.name, value)
        if self.__dict__.has_key('value'):
            del self.value


class Detached(BaseSlot): # Detached Value.
    fieldName = None
    filePathSplitted = None
    kind = None
    label = None
    path = None
    value = None

    def __init__(self, slot, value):
        container = slot.getRootContainer()
        BaseSlot.__init__(self, container = container)
        self.fieldName = slot.getFieldName()
        # Do not fail when the slot to detach does not exist.
        try:
            self.filePathSplitted = slot.getFilePathSplitted()
        except:
            pass
        self.kind = slot.getKind()
        try:
            self.label = slot.getLabel()
        except:
            pass
        try:
            self.path = slot.getPath()
        except:
            pass
        self.value = value

    def delValue(self):
        if self.__dict__.has_key('value'):
            del self.value

    def getFieldName(self):
        return self.fieldName

    def getFilePathSplitted(self):
        return self.filePathSplitted

    def getKind(self):
        return self.kind

    def getLabel(self):
        return self.label

    def getLocalValue(self, setIfNone = 0):
        return self.value

    def getPath(self):
        return self.path

    def hasLocalValue(self):
        return 1

    def setValue(self, value):
        self.value = value


class Dummy(BaseSlot): # To use for None slots.
    kind = None

    def __init__(self, kind, container = None):
        self.kind = kind
        if container is not None:
            self.container = container

    def delValue(self):
        del self.container
        if self.__dict__.has_key('value'):
            del self.value

    def getFieldName(self):
        return ''

    def getFilePathSplitted(self):
        return []

    def getKind(self):
        return self.kind

    def getLabel(self):
        return 'Dummy'

    def getLocalValue(self, setIfNone = 0):
        return self.container

    def getPath(self):
        return 'self'

    def hasLocalValue(self):
        return self.container is not None

    def isDefaultValue(self, value):
        return value != None

    def setValue(self, value):
        self.container = value
        if self.__dict__.has_key('value'):
            del self.value


class Item(BaseSlot): # Sequence Item.
    index = None

    def __init__(self, index, container = None, parent = None):
        BaseSlot.__init__(self, container = container, parent = parent)
        self.index = index

    def delValue(self):
        # Maybe we should split this method into:
        #  - eraseValue() which sets the value to None without shrinking the
        #    sequence.
        #  - deleteValue() which deletes the value and shrinks the sequence.
        if  self.parent.getKind().canInsertOrDelete:
            del self.getContainer()[self.index]
        else:
            self.getContainer()[self.index] = None
        if self.__dict__.has_key('value'):
            del self.value

    def getFieldName(self):
        return '_'.join(filter(None, [
            self.getContainerFieldName(),
            str(self.index)]))

    def getFilePathSplitted(self):
        return self.getContainerFilePathSplitted() + [str(self.index)]

    def getKind(self):
        return self.parent.getKind().getItemKind(self.index)

    def getLabel(self):
        return str(self.index)

    def getLocalValue(self, setIfNone = 0):
        container = self.getContainer(setIfNone = setIfNone)
        if container is None:
            return None
        return container[self.index]

    def getPath(self):
        return '%s[%s]' % (self.getContainerPath(), self.index)

    def hasLocalValue(self):
        container = self.getContainer()
        return container is not None and 0 <= self.index < len(container)

    def insertValue(self, value):
        if not self.parent.getKind().canInsertOrDelete:
            raise Exception('Can\'t insert item "%s" in sequence %s'
                            % (value, self.parent.getKind()))
        self.getContainer().insert(self.index, value)
        if self.__dict__.has_key('value'):
            del self.value

    def setValue(self, value):
        self.getContainer(setIfNone = 1)[self.index] = value
        if self.__dict__.has_key('value'):
            del self.value


class ItemKey(BaseSlot): # Mapping Item Key.
    index = None

    def __init__(self, index, container = None, parent = None):
        BaseSlot.__init__(self, container = container, parent = parent)
        self.index = index

    def getFieldName(self):
        return '_'.join(filter(None, [
            self.getContainerFieldName(),
            'keys',
            str(self.index)]))

    def getKind(self):
        return self.parent.getKind().getItemKeyKind(self.index)

    def getLabel(self):
        return str(self.index)

    def getLocalValue(self, setIfNone = 0):
        return self.getContainer(setIfNone = setIfNone).keys()[self.index]

    def getPath(self):
        return '%s.keys()[%s]' % (self.getContainerPath(), self.index)
    
    def getValue(self, setIfNone = 0):
        if self.value is not None:
            return self.value
        if self.hasLocalValue():
            return self.getLocalValue(setIfNone = setIfNone)

    def hasLocalValue(self):
        container = self.getContainer()
        return container is not None \
               and 0 <= self.index < len(container.keys())


class ItemKeyValue(BaseSlot):
    # Mapping Item Value accessed vith ItemKey slot as parent.

    def delValue(self):
        key = self.parent.getValue()
        del self.getContainer()[key]
        if self.__dict__.has_key('value'):
            del self.value

    def getContainer(self, setIfNone = 0):
        return self.parent.getContainer(setIfNone = setIfNone)

    def getContainerFilePathSplitted(self):
        return self.parent.getContainerFilePathSplitted()

    def getContainerPath(self):
        return self.parent.getContainerPath()

    def getFieldName(self):
        return '_'.join(filter(None, [
            self.getContainerFieldName(),
            'value']))

    def getFilePathSplitted(self):
        key = self.parent.getValue()
        return self.getContainerFilePathSplitted() + [str(key)]

    def getKind(self):
        key = self.parent.getValue()
        return self.parent.parent.getKind().getItemValueKind(key)

    def getLabel(self):
        return 'value'

    def getLocalValue(self, setIfNone = 0):
        key = self.parent.getValue()
        return self.getContainer(setIfNone = setIfNone)[key]

    def getPath(self):
        key = self.parent.getValue()
        if type(key) == types.StringType:
            key = '\'%s\'' % key
        elif type(key) == types.UnicodeType:
            key = 'u\'%s\'' % key
        else:
            key = str(key)
        return '%s[%s]' % (self.getContainerPath(), key)

    def hasLocalValue(self):
        key = self.parent.getValue()
        container = self.getContainer()
        return container is not None and container.has_key(key)

    def setValue(self, value):
        key = self.parent.getValue()
        self.getContainer(setIfNone = 1)[key] = value
        if self.__dict__.has_key('value'):
            del self.value


class ItemValue(BaseSlot): # Mapping Item Value.
    key = None

    def __init__(self, key, container = None, parent = None):
        BaseSlot.__init__(self, container = container, parent = parent)
        self.key = key

    def delValue(self):
        del self.getContainer()[self.key]
        if self.__dict__.has_key('value'):
            del self.value

    def getFieldName(self):
        return '_'.join(filter(None, [
            self.getContainerFieldName(),
            'values',
            str(self.key)]))

    def getFilePathSplitted(self):
        return self.getContainerFilePathSplitted() + [str(self.key)]

    def getKind(self):
        return self.parent.getKind().getItemValueKind(self.key)

    def getLabel(self):
        return str(self.key)

    def getLocalValue(self, setIfNone = 0):
        return self.getContainer(setIfNone = setIfNone)[self.key]

    def getPath(self):
        if type(self.key) == types.StringType:
            key = '\'%s\'' % self.key
        elif type(self.key) == types.UnicodeType:
            key = 'u\'%s\'' % self.key
        else:
            key = str(self.key)
        return '%s[%s]' % (self.getContainerPath(), key)

    def hasLocalValue(self):
        container = self.getContainer()
        return container is not None and container.has_key(self.key)

    def setValue(self, value):
        self.getContainer(setIfNone = 1)[self.key] = value
        if self.__dict__.has_key('value'):
            del self.value


class PropertiesItem(Item): # Item of Properties.
    def getFilePathSplitted(self):
        properties = self.getContainer()
        propertyName = properties[self.index].name
        return self.getContainerFilePathSplitted() + [propertyName]

    def getLabel(self):
        properties = self.getContainer()
        propertyName = properties[self.index].name
        return propertyName

    def getPath(self):
        properties = self.getContainer()
        propertyName = properties[self.index].name
        return '%s[\'%s\']' % (self.getContainerPath(), propertyName)


class Property(Attribute): # Card Property.
    def delValue(self):
        self.setValue(None)

    def getKind(self):
        # This doesn't work, because the kind would always be a common kind.
        # import properties
        # return properties.Property().newKind()
        property = commonTools.newThing('other', 'Property')
        return property.newKind()

    def getLocalValue(self, setIfNone = 0):
        card = self.parent.getContainer(setIfNone = setIfNone)
        kind = card.getDirectPropertyValueKind(self.name)
        if kind is None:
            raise faults.MissingItem(self.name)
        property = commonTools.newThing('other', 'Property')
        property.kind = kind
        property.name = self.name
        if setIfNone:
            self.setValue(property)
        return property

    def getPath(self):
        if type(self.name) == types.StringType:
            name = '\'%s\'' % self.name
        elif type(self.name) == types.UnicodeType:
            name = 'u\'%s\'' % self.name
        else:
            name = str(self.name)
        return '%s[%s]' % (self.getContainerPath(), name)

    def getValue(self, setIfNone = 0):
        if self.value is not None:
            if setIfNone:
                value = self.value
                self.setValue(self.value) # It erases self.value!
                self.value = value
            return self.value
        card = self.parent.getContainer(setIfNone = setIfNone)
        kind = card.getDirectPropertyValueKind(self.name)
        if kind is None:
            kind = card.getPropertyValueKind(self.name)
            kind = copy.deepcopy(kind)
        property = commonTools.newThing('other', 'Property')
        property.kind = kind
        property.name = self.name
        if setIfNone:
            self.setValue(property)
        self.value = property
        return property

    def hasLocalValue(self):
        card = self.parent.getContainer()
        kind = card.getDirectPropertyValueKind(self.name)
        return kind is not None

    def hasValue(self):
        raise Exception('FIXME: To do')

    def isDefaultValue(self, value):
        raise Exception('FIXME: To do')

    def setValue(self, value):
        card = self.parent.getContainer(setIfNone = 1)
        if value is None:
            kind = None
        else:
            kind = value.kind
        card.setDirectPropertyValueKind(self.name, kind)
        if self.__dict__.has_key('value'):
            del self.value


class PropertyValue(Attribute): # Card Property Value.
    def delValue(self):
        values = self.getContainer().values
        if values and values.has_key(self.name):
            del values[self.name]
        if self.__dict__.has_key('value'):
            del self.value

    def getKind(self):
        return self.getContainer().getPropertyValueKind(self.name)

    def getLocalValue(self, setIfNone = 0):
        card = self.getContainer(setIfNone = setIfNone)
        return card.values[self.name]

    def getValue(self, setIfNone = 0):
        if self.value is not None:
            return self.value
        return self.getContainer(setIfNone = setIfNone).getPropertyValue(
                self.name, self.getKind())

    def hasLocalValue(self):
        card = self.getContainer()
        return card.values is not None and card.values.has_key(self.name)

    def hasValue(self):
        return 1

    def isDefaultValue(self, value):
        return self.getContainer().getInheritedPropertyValue(
                self.name, self.getKind()) == value

    def setValue(self, value):
        self.getContainer(setIfNone = 1).setDirectPropertyValue(
                self.name, value)
        if self.__dict__.has_key('value'):
            del self.value


class Root(BaseSlot): # First Instance (rarely needed).
    name = ''

    def __init__(self, container, name = None):
        BaseSlot.__init__(self, container = container, parent = None)
        if name is not None:
            self.name = name

    def delValue(self):
        del self.container
        if self.__dict__.has_key('value'):
            del self.value

    def getFieldName(self):
        return self.name

    def getFilePathSplitted(self):
        return []

    def getKind(self):
        return self.container.newKind()

    def getLabel(self):
        if self.name:
            return self.name
        else:
            return 'Root'

    def getLocalValue(self, setIfNone = 0):
        return self.container

    def getPath(self):
        return 'self'

    def getWidget(self):
        return self.container.newWidget()

    def hasLocalValue(self):
        return self.container is not None

    def isDefaultValue(self, value):
        return value != None

    def setValue(self, value):
        self.container = value
        if self.__dict__.has_key('value'):
            del self.value

