# -*- 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.


## FIXME: Remove comments once Glasnost will be Python2.3 based.
## from __future__ import division


__doc__ = """Glasnost Common Jails

Jails are secure wrappers used to hide the intrisics of Glasnost objects and
slots.

They are used in user defined formulas.

For example a formula may be:
    self.title = self.firstName + ' ' + self.lastName
instead of:
    self.getSlot('title').setValue(self.getSlot('firstName').getValue() + ' '
                                   + self.getSlot('lastName').getValue())

FIXME: Jails could also be used in templates (TAL, ...)
"""

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


import sys
import types

import faults


class Jail:
    _modelSlot = None

    def __cmp__(self, other):
        return cmp(self._getModel(), other)

    def __eq__(self, other):
        return self._getModel() == other

    def __ge__(self, other):
        return self._getModel() >= other

    def __gt__(self, other):
        return self._getModel() > other

    def __hash__(self):
        return hash(self._getModel())

    def __init__(self, modelSlot):
        self.__dict__['_modelSlot'] = modelSlot

    def __le__(self, other):
        return self._getModel() <= other

    def __lt__(self, other):
        return self._getModel() < other

    def __ne__(self, other):
        return self._getModel() != other

    def __nonzero__(self):
        # For Python2.3, could be replaced by:
        # return bool(self._getModel())
        if self._getModel():
            return 1
        else:
            return 0

    def __repr__(self):
        return repr(self._getModel())

    def __str__(self):
        return str(self._getModel())

    def __unicode__(self):
        return unicode(self._getModel())

    def _getModel(self):
        return self._modelSlot.getValue()


class Numeric(Jail):
    def __abs__(self):
        return abs(self._getModel())

    def __add__(self, other):
        return self._getModel() + other

    def __and__(self, other):
        return self._getModel() & other

    def __coerce__(self, other):
        model = self._getModel()
        if hasattr(model, '__coerce__'):
            return self._getModel().__coerce__(other)
        else:
            return None

    def __complex__(self):
        return complex(self._getModel())

    def __div__(self, other):
        return self._getModel() / other

    def __divmod__(self, other):
        return divmod(self._getModel(), other)

    def __float__(self):
        return float(self._getModel())

##     def __floordiv__(self, other):
##         return self._getModel() // other

    def __hex__(self):
        return hex(self._getModel())

    def __int__(self):
        return int(self._getModel())

    def __invert__(self):
        return ~self._getModel()

    def __long__(self):
        return long(self._getModel())

    def __lshift__(self, other):
        return self._getModel() << other

    def __mod__(self, other):
        return self._getModel() % other

    def __mul__(self, other):
        return self._getModel() * other

    def __neg__(self):
        return -self._getModel()

    def __oct__(self):
        return oct(self._getModel())

    def __or__(self, other):
        return self._getModel() | other

    def __pos__(self):
        return +self._getModel()

    def __pow__(self, other, modulo = None):
        if modulo is None:
            return pow(self._getModel(), other)
        else:
            return pow(self._getModel(), other, modulo)

    def __radd__(self, other):
        return other + self._getModel()

    def __rand__(self, other):
        return other & self._getModel()

    def __rdiv__(self, other):
        return other / self._getModel()

    def __rdivmod__(self, other):
        return divmod(other, self._getModel())

##     def __rfloordiv__(self, other):
##         return other // self._getModel()

    def __rlshift__(self, other):
        return other << self._getModel()

    def __rmod__(self, other):
        return other % self._getModel()

    def __rmul__(self, other):
        return other * self._getModel()

    def __ror__(self, other):
        return other | self._getModel()

    def __rpow__(self, other):
        return pow(other, self._getModel())

    def __rrshift__(self, other):
        return other >> self._getModel()

    def __rshift__(self, other):
        return self._getModel() >> other

    def __rsub__(self, other):
        return other - self._getModel()

    def __rtruediv__(self, other):
        return other / self._getModel()

    def __rxor__(self, other):
        return other ^ self._getModel()

    def __sub__(self, other):
        return self._getModel() - other

    def __truediv__(self, other):
        return self._getModel() / other

    def __xor__(self, other):
        return self._getModel() ^ other


class Container(Jail):
    def __contains__(self, item):
        return item in self._getModel()

    def __delitem__(self, key):
        raise NotImplementedError

    def __getitem__(self, key):
        raise NotImplementedError

    def __iter__(self):
        raise NotImplementedError

    def __len__(self):
        return len(self._getModel())

    def __setitem__(self, key, value):
        raise NotImplementedError


class Mapping(Container):
    def __delitem__(self, key):
        # FIXME: To do.
        raise NotImplementedError

    def __getitem__(self, key):
        # FIXME: To do.
        raise NotImplementedError

    def __iter__(self):
        # FIXME: To do.
        raise NotImplementedError

    def __setitem__(self, key, value):
        # FIXME: To do.
        raise NotImplementedError

    def clear(self):
        # FIXME: To do.
        raise NotImplementedError

    def copy(self):
        # FIXME: To do.
        raise NotImplementedError

    def get(self, key, value = None):
        # FIXME: To do.
        raise NotImplementedError

    def has_key(self, key):
        # FIXME: To do.
        raise NotImplementedError

    def items(self):
        # FIXME: To do.
        raise NotImplementedError

    def iteritems(self):
        # FIXME: To do.
        raise NotImplementedError

    def iterkeys(self):
        # FIXME: To do.
        raise NotImplementedError

    def itervalues(self):
        # FIXME: To do.
        raise NotImplementedError

    def keys(self):
        # FIXME: To do.
        raise NotImplementedError

    def pop(self, key, value = None):
        # FIXME: To do.
        raise NotImplementedError

    def popitem(self):
        # FIXME: To do.
        raise NotImplementedError

    def setdefault(self, key, value = None):
        # FIXME: To do.
        raise NotImplementedError

    def update(self, mapping):
        # FIXME: To do.
        raise NotImplementedError

    def values(self):
        # FIXME: To do.
        raise NotImplementedError


class Sequence(Container):
    def __add__(self, other):
        return self._getModel() + other

    def __getitem__(self, key):
        if type(key) == types.SliceType:
            sliceStart = key.start
            if sliceStart is None:
                sliceStart = 0
            sliceStop = key.stop
            if sliceStop is None:
                sliceStop = len(self)
            sliceStep = key.step
            if sliceStep is None:
                sliceStep = 1
            return [self[i] for i in range(sliceStart, sliceStop, sliceStep)]
        kind = self._modelSlot.getKind()
        itemSlot = kind.getItemSlot(self._modelSlot, key)
        itemModel = itemSlot.getValue()
        if itemModel is None:
            return None
        else:
            return itemSlot.newJail()

    def __iter__(self):
        # FIXME: To do.
        raise NotImplementedError

    def __mul__(self, other):
        return self._getModel() * other

    def __radd__(self, other):
        return other + self._getModel()

    def __rmul__(self, other):
        return other * self._getModel()


class MutableSequence(Sequence):
    def __delitem__(self, key):
        if type(key) == types.SliceType:
            sliceStart = key.start
            if sliceStart is None:
                sliceStart = 0
            sliceStop = key.stop
            if sliceStop is None:
                sliceStop = len(self)
            sliceStep = key.step
            if sliceStep is None:
                sliceStep = 1
            # The items must be deleted, beginning from the end.
            indexesToDelete = [
                    i
                    for i in range(sliceStart, sliceStop, sliceStep)]
            indexesToDelete.sort()
            indexesToDelete.reverse()
            for i in indexesToDelete:
                del self[i]
            return
        kind = self._modelSlot.getKind()
        itemSlot = kind.getItemSlot(self._modelSlot, key)
        itemSlot.delValue()

    def __setitem__(self, key, value):
        if type(key) == types.SliceType:
            sliceStart = key.start
            if sliceStart is None:
                sliceStart = 0
            sliceStop = key.stop
            if sliceStop is None:
                sliceStop = len(self)
            sliceStep = key.step
            if sliceStep is None:
                sliceStep = 1
            indexes = [
                    i
                    for i in range(sliceStart, sliceStop, sliceStep)]
            valueLength = len(value)
            indexesLength = len(indexes)
            if indexesLength < valueLength:
                indexesToDelete = []
                indexesToModify = indexes
                itemsToModify = value[:indexesLength]
                itemsToAdd = value[indexesLength:]
            elif indexesLength == valueLength:
                indexesToDelete = []
                indexesToModify = indexes
                itemsToModify = value
                itemsToAdd = []
            else:
                # The items must be deleted, beginning from the end.
                indexesToDelete = indexes[valueLength:]
                indexesToDelete.sort()
                indexesToDelete.reverse()
                indexesToModify = indexes[:valueLength]
                itemsToModify = value
                itemsToAdd = []
            if indexesToModify:
                for i, v in zip(indexesToModify, itemsToModify):
                    self[i] = v
            if indexesToDelete:
                for i in indexesToDelete:
                    del self[i]
            elif itemsToAdd:
                i = sliceStop
                for item in itemsToAdd:
                    self.insert(i, item)
                    i += 1
            return
        kind = self._modelSlot.getKind()
        itemSlot = kind.getItemSlot(self._modelSlot, key)
        itemKind = itemSlot.getKind()
        if isinstance(value, Jail):
            valueSlot = value._modelSlot
            valueKind = valueSlot.getKind()
            if not itemKind.accepts(valueKind):
                raise faults.KindsIncompatibility(itemKind, valueKind)
            itemValue = valueSlot.getValue()
        else:
            itemValue = value
        itemKind.checkModelValue(itemSlot, itemValue)
        itemSlot.setValue(itemValue)

    def append(self, item):
        # self[length:length] = [item]
        self.insert(len(self), item)

    def count(self, item):
        return self._getModel().count(item)

    def extend(self, sequence):
        length = len(self)
        self[length:length] = sequence

    def index(self, item, i = None, j = None):
        if i is None and j is None:
            # To be compatible for Python < 2.3.
            return self._getModel().index(item)
        else:
            return self._getModel().index(item, i, j)

    def insert(self, i, item):
        kind = self._modelSlot.getKind()
        itemSlot = kind.getItemSlot(self._modelSlot, i)
        itemKind = itemSlot.getKind()
        if isinstance(item, Jail):
            valueSlot = item._modelSlot
            valueKind = valueSlot.getKind()
            if not itemKind.accepts(valueKind):
                raise faults.KindsIncompatibility(itemKind, valueKind)
            itemValue = valueSlot.getValue()
        else:
            itemValue = item
        itemKind.checkModelValue(itemSlot, itemValue)
        itemSlot.insertValue(itemValue)

    def pop(self, i = -1):
        return self._getModel().pop(i)

    def remove(self, item):
        self._getModel().remove(item)

    def reverse(self):
        self._getModel().reverse()

    def sort(self, compareFunction = None):
        self._getModel().sort(compareFunction)


class String(Sequence):
    def __add__(self, other):
        return self._getModel() + other

    def __mul__(self, other):
        return self._getModel() * other

    def capitalize(self):
        return self._getModel().capitalize()

    def center(self, width):
        return self._getModel().center(width)

    def count(self, sub, start, end):
        return self._getModel().count(sub, start, end)

    def decode(self, encoding = None, errors = None):
        if encoding:
            if errors:
                return self._getModel().decode(encoding, errors)
            else:
                return self._getModel().decode(encoding)
        else:
            return self._getModel().decode()

    def encode(self, encoding = None, errors = None):
        if encoding:
            if errors:
                return self._getModel().encode(encoding, errors)
            else:
                return self._getModel().encode(encoding)
        else:
            return self._getModel().encode()

    def endswith(self, suffix, start = 0, end = sys.maxint):
        return self._getModel().endswith(suffix, start, end)

    def expandtabs(self, tabsize = 8):
        return self._getModel().expandtabs(tabsize)

    def find(self, sub, start = 0, end = sys.maxint):
        return self._getModel().find(sub, start, end)

    def index(self, sub, start = 0, end = sys.maxint):
        return self._getModel().index(sub, start, end)

    def isalpha(self):
        return self._getModel().isalpha()

    def isalnum(self):
        return self._getModel().isalnum()

    def isdecimal(self):
        return self._getModel().isdecimal()

    def isdigit(self):
        return self._getModel().isdigit()

    def islower(self):
        return self._getModel().islower()

    def isnumeric(self):
        return self._getModel().isnumeric()

    def isspace(self):
        return self._getModel().isspace()

    def istitle(self):
        return self._getModel().istitle()

    def isupper(self):
        return self._getModel().isupper()

    def join(self, seq):
        return self._getModel().join(seq)

    def ljust(self, width):
        return self._getModel().ljust(width)

    def lower(self):
        return self._getModel().lower()

    def lstrip(self, chars = None):
        return self._getModel().lstrip(chars)

    def replace(self, old, new, maxsplit = -1):
        return self._getModel().replace(old, new, maxsplit)

    def rfind(self, sub, start = 0, end = sys.maxint):
        return self._getModel().rfind(sub, start, end)

    def rindex(self, sub, start = 0, end = sys.maxint):
        return self._getModel().rindex(sub, start, end)

    def rjust(self, width):
        return self._getModel().rjust(width)

    def rstrip(self, chars = None):
        return self._getModel().rstrip(chars)

    def split(self, sep = None, maxsplit = -1):
        return self._getModel().split(sep, maxsplit)

    def splitlines(self, keepends = 0):
        return self._getModel().splitlines(keepends)

    def startswith(self, prefix, start = 0, end = sys.maxint):
        return self._getModel().startswith(prefix, start, end)

    def strip(self, chars = None):
        return self._getModel().strip(chars)

    def swapcase(self):
        return self._getModel().swapcase()

    def title(self):
        return self._getModel().title()

    def translate(self, *args):
        return self._getModel().translate(*args)

    def upper(self):
        return self._getModel().upper()

    def zfill(self, width):
        return self._getModel().zfill(width)


class Thing(Jail):
    def __delattr__(self, attributeName):
        if not self._getModel().hasSlotName(
                attributeName, parentSlot = self._modelSlot):
            raise AttributeError('%s instance has no attribute \'%s\'' % (
                    self.__class__.__name__, attributeName))
        attributeSlot = self._getModel().getSlot(
                attributeName, parentSlot = self._modelSlot)
        attributeSlot.delValue()

    def __getattr__(self, attributeName):
        if not self._getModel().hasSlotName(
                attributeName, parentSlot = self._modelSlot):
            raise AttributeError('%s instance has no attribute \'%s\'' % (
                    self.__class__.__name__, attributeName))
        attributeSlot = self._getModel().getSlot(
                attributeName, parentSlot = self._modelSlot)
        attributeModel = attributeSlot.getValue()
        if attributeModel is None:
            return None
        else:
            return attributeSlot.newJail()

    def __repr__(self):
        return '<Class "%s" instance>' % self._getModel().getThingPublicName()

    def __setattr__(self, attributeName, value):
        if not self._getModel().hasSlotName(
                attributeName, parentSlot = self._modelSlot):
            raise AttributeError('%s instance has no attribute \'%s\'' % (
                    self.__class__.__name__, attributeName))
        attributeSlot = self._getModel().getSlot(
                attributeName, parentSlot = self._modelSlot)
        attributeKind = attributeSlot.getKind()
        if isinstance(value, Jail):
            valueSlot = value._modelSlot
            valueKind = valueSlot.getKind()
            if not attributeKind.accepts(valueKind):
                raise faults.KindsIncompatibility(attributeKind, valueKind)
            attributeValue = valueSlot.getValue()
        else:
            attributeValue = value
        attributeKind.checkModelValue(attributeSlot, attributeValue)
        attributeSlot.setValue(attributeValue)

    def __str__(self):
        return '<Class "%s" instance>' % self._getModel().getThingPublicName()


class Object(Thing):
    pass

