--- /dev/null
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+@author: Frank Brehm
+@contact: frank@brehm-online.com
+@copyright: © 2020 by Frank Brehm, Berlin
+@summary: A module for providing a mutable set with case insensitive sting items.
+"""
+from __future__ import absolute_import
+
+# Standard module
+import logging
+import copy
+
+try:
+ from collections.abc import MutableSet
+except ImportError:
+ from collections import MutableSet
+
+# Third party modules
+
+# Own modules
+from fb_tools.common import pp
+from fb_tools.errors import FbError
+from fb_tools.obj import FbBaseObject
+
+__version__ = '0.1.0'
+
+LOG = logging.getLogger(__name__)
+
+# =============================================================================
+class WrongItemTypeError(TypeError, FbError):
+
+ # -------------------------------------------------------------------------
+ def __init__(self, item):
+
+ self.item = item
+
+ # -------------------------------------------------------------------------
+ def __str__(self):
+
+ msg = "Key {item!r} must be of type 'str', but is of type {cls!r} instead."
+ return msg.format(item=self.item, cls=self.item.__class__.__name__)
+
+
+# =============================================================================
+class WrongCompareSetClassError(TypeError, FbError):
+
+ # -------------------------------------------------------------------------
+ def __init__(self, other):
+
+ self.other_class = other.__class__.__name__
+
+ # -------------------------------------------------------------------------
+ def __str__(self):
+
+ msg = "Object {!r} is not a CaseInsensitiveStringSet object."
+ return msg.format(self.other_class)
+
+
+# =============================================================================
+class CaseInsensitiveStringSet(MutableSet):
+ """
+ A mutable set, where the strings are insensitive strings.
+ The items MUST be of type string!
+ It works like a set.
+ """
+
+ wrong_type_msg = "Item {item!r} must be of type 'str', but is of type {cls!r} instead."
+
+ # -------------------------------------------------------------------------
+ def __init__(self, iterable):
+
+ self._items = []
+
+ for item in iterable:
+
+ if not isinstance(item, str):
+ raise WrongItemTypeError(item)
+
+ if item not in self:
+ self._items.append(item)
+
+ # -------------------------------------------------------------------------
+ # Mandatory methods (ABC methods)
+
+ # -------------------------------------------------------------------------
+ def __iter__(self):
+
+ return iter(self._items)
+
+ # -------------------------------------------------------------------------
+ def __contains__(self, value):
+
+ if not isinstance(value, str):
+ raise WrongItemTypeError(value)
+
+ for item in self._items:
+ if item.lower() == value.lower():
+ return True
+
+ return value in self.elements
+
+ # -------------------------------------------------------------------------
+ def __len__(self):
+ return len(self._items)
+
+ # -------------------------------------------------------------------------
+ def add(self, value):
+
+ if not isinstance(value, str):
+ raise WrongItemTypeError(value)
+
+ if value in self:
+
+ index = 0
+ for item in self._items:
+ if item.lower() == value.lower():
+ if item != value:
+ self._items[index] = value
+ break
+ index += 1
+
+ else:
+ self._items.append(value)
+
+ # -------------------------------------------------------------------------
+ def discard(self, value):
+
+ index = 0
+ for item in self._items:
+ if item.lower() == value.lower():
+ del self._items[index]
+ break
+ index += 1
+
+ # -------------------------------------------------------------------------
+ # Nice to have methods
+
+ # -------------------------------------------------------------------------
+ def isdisjoint(self, other):
+
+ if not isinstance(other, CaseInsensitiveStringSet):
+ raise WrongCompareSetClassError(other)
+
+ for item in self._items:
+ if item in other:
+ return False
+
+ return True
+
+ # -------------------------------------------------------------------------
+ def issubset(self, other):
+
+ if not isinstance(other, CaseInsensitiveStringSet):
+ raise WrongCompareSetClassError(other)
+
+ for item in self._items:
+ if item not in other:
+ return False
+
+ return True
+
+ # -------------------------------------------------------------------------
+ def __le__(self, other):
+
+ if not isinstance(other, CaseInsensitiveStringSet):
+ raise WrongCompareSetClassError(other)
+
+ return self.issubset(other)
+
+ # -------------------------------------------------------------------------
+ def __eq__(self, other):
+
+ if not isinstance(other, CaseInsensitiveStringSet):
+ raise WrongCompareSetClassError(other)
+
+ if len(self) != len(other):
+ return False
+
+ for item in self._items:
+ if item not in other:
+ return False
+
+ return True
+
+ # -------------------------------------------------------------------------
+ def __ne__(self, other):
+
+ if not isinstance(other, CaseInsensitiveStringSet):
+ raise WrongCompareSetClassError(other)
+
+ if self == other:
+ return False
+ return True
+
+ # -------------------------------------------------------------------------
+ def __lt__(self, other):
+
+ if not isinstance(other, CaseInsensitiveStringSet):
+ raise WrongCompareSetClassError(other)
+
+ ret = True
+ for item in self._items:
+ if item not in other:
+ ret = False
+ if ret:
+ if len(self) != len(other):
+ return True
+ return False
+
+
+# =============================================================================
+if __name__ == "__main__":
+
+ pass
+
+# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 list