#
# This file is part of Dragonfly.
# (c) Copyright 2007, 2008 by Christo Butcher
# Licensed under the LGPL.
#
#   Dragonfly is free software: you can redistribute it and/or modify it 
#   under the terms of the GNU Lesser General Public License as published 
#   by the Free Software Foundation, either version 3 of the License, or 
#   (at your option) any later version.
#
#   Dragonfly 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 
#   Lesser General Public License for more details.
#
#   You should have received a copy of the GNU Lesser General Public 
#   License along with Dragonfly.  If not, see 
#   <http://www.gnu.org/licenses/>.
#
 
"""
    This file implements List and DictList classes which behave
    as built-in Python lists and dicts, but can be used within
    Dragonfly grammars.
"""
 
 
# The overridden methods in the Dragonfly.list.List class were
# automatically generated using the commented-out code immediately below.
#
#def construct_skeleton():
#   instance = list()
#   ignored_attributes = ('__class__', '__contains__', '__doc__',
#       '__eq__', '__ge__', '__getattribute__', '__getitem__',
#       '__getslice__', '__gt__', '__hash__', '__init__', '__iter__',
#       '__le__', '__len__', '__lt__', '__ne__', '__new__', '__repr__',
#       '__str__', 'count', 'index', '__delattr__', '__setattr__')
#   output = []
#   for name in dir(instance):
#       attribute = getattr(instance, name)
#       if not callable(attribute): continue
#       if name in ignored_attributes: continue
#       output.append("""\
#   def %(function)s(self, *args, **kwargs):
#       result = %(class)s.%(function)s(self, *args, **kwargs)
#       self._update(); return result
#""" % {"class": "list", "function": name})
#   return "".join(output)
#print construct_skeleton()
 
 
#===========================================================================
# Base class for dragonfly list objects.
 
class ListBase(object):
 
    _valid_types = (str, unicode)
 
    def __init__(self, name):
        self._name = name
        self._grammar = None
 
    #-----------------------------------------------------------------------
    # Protected attribute access.
 
    valid_types = property(lambda self: self._valid_types,
        doc = "The types of object at a Dragonfly list can contain.")
 
    name = property(lambda self: self._name,
        doc = "Read-only access to a list's name.")
 
    def _get_grammar(self):
        return self._grammar
    def _set_grammar(self, grammar):
        if self._grammar is None:
            self._grammar = grammar
        else:
            raise TypeError("The grammar object a Dragonfly list"
                " is bound to cannot be changed after it has been set.")
    grammar = property(_get_grammar, _set_grammar,
        doc = "Set-once access to a list's grammar object.")
 
    #-----------------------------------------------------------------------
    # Notify the grammar of a list modification.
 
    def _update(self):
        invalid = [i for i in self if not isinstance(i, self._valid_types)]
        if invalid:
            raise TypeError("Dragonfly lists can only contain"
                            " string objects; received: %r" % invalid)
        if self._grammar:
            self._grammar.update_list(self)
 
    #-----------------------------------------------------------------------
    # Accessor for the grammar to retrieve the list items.
    def get_list_items(self):
        raise NotImplementedError("Call to virtual method list_items()")
 
 
#===========================================================================
# Wrapper for Python's built-in list type.
 
class List(ListBase, list):
    """
        Wrapper for Python's built-in list that supports automatic
        Natlink notification of changes.
    """
 
    def __init__(self, name, *args, **kwargs):
        ListBase.__init__(self, name)
        list.__init__(self, *args, **kwargs)
 
    #-----------------------------------------------------------------------
    # Accessor for the grammar to retrieve the list items.
 
    def get_list_items(self):
        return self
 
    #-----------------------------------------------------------------------
    # Custom methods.
 
    def set(self, other):
        """Set the contents of this list to the contents of another."""
        self[:] = other
        self._update()
 
    #-----------------------------------------------------------------------
    # Overridden list methods.
 
    def __add__(self, *args, **kwargs):
        result = list.__add__(self, *args, **kwargs)
        self._update(); return result
    def __delitem__(self, *args, **kwargs):
        result = list.__delitem__(self, *args, **kwargs)
        self._update(); return result
    def __delslice__(self, *args, **kwargs):
        result = list.__delslice__(self, *args, **kwargs)
        self._update(); return result
    def __iadd__(self, *args, **kwargs):
        result = list.__iadd__(self, *args, **kwargs)
        self._update(); return result
    def __imul__(self, *args, **kwargs):
        result = list.__imul__(self, *args, **kwargs)
        self._update(); return result
    def __mul__(self, *args, **kwargs):
        result = list.__mul__(self, *args, **kwargs)
        self._update(); return result
    def __reduce__(self, *args, **kwargs):
        result = list.__reduce__(self, *args, **kwargs)
        self._update(); return result
    def __reduce_ex__(self, *args, **kwargs):
        result = list.__reduce_ex__(self, *args, **kwargs)
        self._update(); return result
    def __rmul__(self, *args, **kwargs):
        result = list.__rmul__(self, *args, **kwargs)
        self._update(); return result
    def __setitem__(self, *args, **kwargs):
        result = list.__setitem__(self, *args, **kwargs)
        self._update(); return result
    def __setslice__(self, *args, **kwargs):
        result = list.__setslice__(self, *args, **kwargs)
        self._update(); return result
    def append(self, *args, **kwargs):
        result = list.append(self, *args, **kwargs)
        self._update(); return result
    def extend(self, *args, **kwargs):
        result = list.extend(self, *args, **kwargs)
        self._update(); return result
    def insert(self, *args, **kwargs):
        result = list.insert(self, *args, **kwargs)
        self._update(); return result
    def pop(self, *args, **kwargs):
        result = list.pop(self, *args, **kwargs)
        self._update(); return result
    def remove(self, *args, **kwargs):
        result = list.remove(self, *args, **kwargs)
        self._update(); return result
    def reverse(self, *args, **kwargs):
        result = list.reverse(self, *args, **kwargs)
        self._update(); return result
    def sort(self, *args, **kwargs):
        result = list.sort(self, *args, **kwargs)
        self._update(); return result
 
 
#===========================================================================
# Wrapper for Python's built-in dict type.
 
class DictList(ListBase, dict):
    """
        Wrapper for Python's built-in dict that supports automatic
        Natlink notification of changes.  The object's keys are used
        as the elements of the Natlink list, while use of the associated
        values is left to the user.
    """
 
    def __init__(self, name, *args, **kwargs):
        ListBase.__init__(self, name)
        dict.__init__(self, *args, **kwargs)
 
    #-----------------------------------------------------------------------
    # Accessor for the grammar to retrieve the list items.
 
    def get_list_items(self):
        return self.keys()
 
    #-----------------------------------------------------------------------
    # Custom methods.
 
    def set(self, other):
        """Set the contents of this dict to the contents of another."""
        self.clear()
        self.update(other)
        self._update()
 
    #-----------------------------------------------------------------------
    # Overridden dict methods.
 
    def __delitem__(self, *args, **kwargs):
        result = dict.__delitem__(self, *args, **kwargs)
        self._update(); return result
    def __reduce__(self, *args, **kwargs):
        result = dict.__reduce__(self, *args, **kwargs)
        self._update(); return result
    def __reduce_ex__(self, *args, **kwargs):
        result = dict.__reduce_ex__(self, *args, **kwargs)
        self._update(); return result
    def __setitem__(self, *args, **kwargs):
        result = dict.__setitem__(self, *args, **kwargs)
        self._update(); return result
    def clear(self, *args, **kwargs):
        result = dict.clear(self, *args, **kwargs)
        self._update(); return result
    def fromkeys(self, *args, **kwargs):
        result = dict.fromkeys(self, *args, **kwargs)
        self._update(); return result
    def pop(self, *args, **kwargs):
        result = dict.pop(self, *args, **kwargs)
        self._update(); return result
    def popitem(self, *args, **kwargs):
        result = dict.popitem(self, *args, **kwargs)
        self._update(); return result
    def setdefault(self, *args, **kwargs):
        result = dict.setdefault(self, *args, **kwargs)
        self._update(); return result
    def update(self, *args, **kwargs):
        result = dict.update(self, *args, **kwargs)
        self._update(); return result