# Copyright (c) 2004-2005 DoCoMo Euro-Labs GmbH (Munich, Germany).
# Copyright (c) 2001-2005 LOGILAB S.A. (Paris, FRANCE).
#
# http://www.docomolab-euro.com/ -- mailto:tarlano@docomolab-euro.com
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This library 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 2.1 of the License, or (at your option) any later version.
#
# This library 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 this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
"""Tags module.

Handles relationships between elements.

Steps and transitions can tag elements to mark that they used them. Later,
steps and transitions that have use='yes' in their prototype will have
elements tagged by them filtered out automatically.


:version: $Revision:$  
:author: Logilab  

:copyright:
  2001-2005 LOGILAB S.A. (Paris, FRANCE)
  
  2004-2005 DoCoMo Euro-Labs GmbH (Munich, Germany)

:contact:
  http://www.logilab.fr/ -- mailto:contact@logilab.fr
  
  http://www.docomolab-euro.com/ -- mailto:tarlano@docomolab-euro.com


:var _TAGSDB: the tags database instance
:type _TAGSDB: `TagsDB`
"""

__revision__ = '$Id: tags.py,v 1.4 2001/12/21 13:52:51 syt Exp $'
__docformat__ = "restructuredtext en"

from threading import Lock

from narval.utils import Singleton

class TagsDB(Singleton):
    """the tags database"""
    
    def __init__(self):
        super(TagsDB, self).__init__()
        self._lock = Lock()
        self._db = {}
        
    def is_tagged(self, element, node):
        """return true if the element as been tagged by node

        :type element: `narval.public.ALElement`
        :param element: the element we are informing about tag

        :type node:
          `narval.plan.PlanStep` or `narval.plan.PlanTransition`
        :param node: the tagging step or transition

        :rtype: bool
        :return: true if the element is tagged by the node
        """
        self._lock.acquire()
        try:
            try:
                return self._db[_get_id(element)][node.tagger_id()]
            except KeyError:
                return False
        finally:
            self._lock.release()

    def tag_element(self, element, node):
        """mark element as tagged by node

        :type element: `narval.public.ALElement`
        :param element: the element to tag

        :type node:
          `narval.plan.PlanStep` or `narval.plan.PlanTransition`
        :param node: the tagging step or transition
        """
        self._lock.acquire()
        self._db.setdefault(_get_id(element), {})[node.tagger_id()] = True
        self._lock.release()
        
    def clear_tags(self, element):
        """clear element tags

        :type element: `narval.public.ALElement`
        :param element: the element about which to clear tag information
        """
        self._lock.acquire()
        try:
            del self._db[_get_id(element)]
        except KeyError:
            pass
        self._lock.release()

_TAGSDB = TagsDB()

def _get_id(element):
    """get an identifier for an element

    :type element:
    :param element: the element to identify

    :rtype: int or str
    :return: a unique identifier for the element
    """
    assert isinstance(element.eid, int)
    return element.eid

def is_tagged(element, node):
    """return true if the element as been tagged by node

    :type element: `narval.public.ALElement`
    :param element: the element we are informing about tag

    :type node:
      `narval.plan.PlanStep` or `narval.plan.PlanTransition`
    :param node: the tagging step or transition

    :rtype: bool
    :return: true if the element is tagged by the node
    """
    return _TAGSDB.is_tagged(element, node)

def tag_element(element, node):
    """mark element as tagged by node

    :type element: `narval.public.ALElement`
    :param element: the element to tag

    :type node:
      `narval.plan.PlanStep` or `narval.plan.PlanTransition`
    :param node: the tagging step or transition
    """
    _TAGSDB.tag_element(element, node)

def clear_tags(element):
    """clear element tags

    :type element: `narval.public.ALElement`
    :param element: the element about which to clear tag information
    """
    _TAGSDB.clear_tags(element)

__all__ = ('is_tagged', 'tag_element', 'clear_tags')
