# Source code for taurus.core.util.eventfilters

    #!/usr/bin/env python

    # ###########################################################################
    #
    # This file is part of Taurus
    #
    # http://taurus-scada.org
    #
    # Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
    #
    # Taurus 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.
    #
    # Taurus 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 Taurus.  If not, see <http://www.gnu.org/licenses/>.
    #
    # ###########################################################################

    """event filters library to be used with
    :meth:`taurus.qt.qtgui.base.TaurusBaseComponent.setFilters`
    """

    __all__ = [
        "IGNORE_ALL",
        "ONLY_CHANGE",
        "IGNORE_CHANGE",
        "ONLY_CHANGE_AND_PERIODIC",
        "IGNORE_CHANGE_AND_PERIODIC",
        "ONLY_CONFIG",
        "IGNORE_CONFIG",
        "IGNORE_FAKE",
        "ONLY_VALID",
        "EventValueMap",
        "RepeatedEventFilter",
        "filterEvent",
    ]

    def IGNORE_ALL(s, t, v):
        """Will discard all events"""
        return None

    def ONLY_CHANGE(s, t, v):
        """Only change events pass"""
        from taurus.core import TaurusEventType

        if t == TaurusEventType.Change:
            return s, t, v
        else:
            return None

    def IGNORE_CHANGE(s, t, v):
        """Config events are discarded"""
        from taurus.core import TaurusEventType

        if t != TaurusEventType.Change:
            return s, t, v
        else:
            return None

    def ONLY_CHANGE_AND_PERIODIC(s, t, v):
        """Only change events pass"""
        from taurus.core import TaurusEventType

        if t in [TaurusEventType.Change, TaurusEventType.Periodic]:
            return s, t, v
        else:
            return None

    def IGNORE_CHANGE_AND_PERIODIC(s, t, v):
        """Config events are discarded"""
        from taurus.core import TaurusEventType

        if t not in [TaurusEventType.Change, TaurusEventType.Periodic]:
            return s, t, v
        else:
            return None

    def ONLY_CONFIG(s, t, v):
        """Only config events pass"""
        from taurus.core import TaurusEventType

        if t == TaurusEventType.Config:
            return s, t, v
        else:
            return None

    def IGNORE_CONFIG(s, t, v):
        """Config events are discarded"""
        from taurus.core import TaurusEventType

        if t != TaurusEventType.Config:
            return s, t, v
        else:
            return None

    def IGNORE_FAKE(s, t, v):
        """Only events with actual value (!=None) pass"""
        if v is not None:
            return s, t, v
        else:
            return None

    def ONLY_VALID(s, t, v):
        """Only events whose quality is VALID pass"""
        from taurus.core import AttrQuality

        if t == AttrQuality.ATTR_VALID:
            return s, t, v
        else:
            return None

    class EventValueMap(dict):
        """A filter destined to change the original value into another one
        according to a given map. Example:

            filter = EventValueMap({1:"OPEN", 2:"CHANGING", 3:"CLOSED"})

        this will create a filter that changes the integer value of the event
        into a string. The event type is changed according to the python type in
        the map value.

        For now it only supports simple types: str, int, long, float, bool
        """

        def __call__(self, s, t, v):
            import copy
            from taurus.core import TaurusEventType, DataType

            if t not in (TaurusEventType.Change, TaurusEventType.Periodic):
                return s, t, v
            if v is None:
                return s, t, v

            # make a copy
            v = copy.copy(v)

            v.value = self.get(v.rvalue, v.rvalue)

            v.type = DataType.from_python_type(type(v.rvalue), v.type)
            return s, t, v

    class RepeatedEventFilter(object):
        """
        The instances of this class will be callables that can be used as filters
        of repeated-value events.
        If the event type is Change or Periodic, it will only pass when its
        evt_value.value is different from that of the last event received from the
        same source and type. If evt_value.value is not available in the current
        event, the whole evt_value is used for comparison and as a future
        reference.

        This is useful to avoid processing repetitive events.

        Note that you cannot use this class as a filter: you need to use an
        instance of it.

        Note 2: Use a different instance each time you insert this filter
        into a different widget unless you *really* know what you are doing.

        Example of usage::

            filters = [RepeatedEventFilter(), IGNORE_CONFIG]
            filterEvent(s, t, v, filters)
        """

        def __init__(self):
            self._lastValues = {}

        def __call__(self, s, t, v):
            # restrict this  filter only to change and periodic events.
            if ONLY_CHANGE_AND_PERIODIC(s, t, v) is None:
                return s, t, v
            # block event if we recorded one before with same src, type and v.value
            new_value = getattr(v, "value", v)
            try:
                if self._lastValues[(s, t)] == new_value:
                    return None
            except KeyError:
                pass
            # if it was't blocked, store src, type and v.value for future checks
            self._lastValues[(s, t)] = new_value
            return s, t, v

    def filterEvent(evt_src=-1, evt_type=-1, evt_value=-1, filters=()):
        """The event is processed by each and all filters in strict order
        unless one of them returns None (in which case the event is discarded)

        :param evt_src: object that triggered the event
        :type evt_src: object
        :param evt_type: type of event
        :type evt_type: TaurusEventType
        :param evt_value: event value
        :type evt_value: object
        :param filters: a sequence of callables, each returning either None (to
            discard the event) or the tuple (with possibly transformed values) of
            (evt_src, evt_type, evt_value)
        :type filters: sequence<callable>
        :return: The result of piping the event through the given filters.
        :rtype: None or tuple
        """
        evt = evt_src, evt_type, evt_value

        for f in filters:
            evt = f(*evt)
            if evt is None:
                return None
        return evt