1# -*- coding: utf-8 -*-
2from icalendar.compat import iteritems
3from icalendar.parser_tools import to_unicode
4
5from collections import OrderedDict
6
7
8def canonsort_keys(keys, canonical_order=None):
9    """Sorts leading keys according to canonical_order.  Keys not specified in
10    canonical_order will appear alphabetically at the end.
11    """
12    canonical_map = {k: i for i, k in enumerate(canonical_order or [])}
13    head = [k for k in keys if k in canonical_map]
14    tail = [k for k in keys if k not in canonical_map]
15    return sorted(head, key=lambda k: canonical_map[k]) + sorted(tail)
16
17
18def canonsort_items(dict1, canonical_order=None):
19    """Returns a list of items from dict1, sorted by canonical_order.
20    """
21    return [(k, dict1[k]) for k
22            in canonsort_keys(dict1.keys(), canonical_order)]
23
24
25class CaselessDict(OrderedDict):
26    """A dictionary that isn't case sensitive, and only uses strings as keys.
27    Values retain their case.
28    """
29
30    def __init__(self, *args, **kwargs):
31        """Set keys to upper for initial dict.
32        """
33        super(CaselessDict, self).__init__(*args, **kwargs)
34        for key, value in self.items():
35            key_upper = to_unicode(key).upper()
36            if key != key_upper:
37                super(CaselessDict, self).__delitem__(key)
38                self[key_upper] = value
39
40    def __getitem__(self, key):
41        key = to_unicode(key)
42        return super(CaselessDict, self).__getitem__(key.upper())
43
44    def __setitem__(self, key, value):
45        key = to_unicode(key)
46        super(CaselessDict, self).__setitem__(key.upper(), value)
47
48    def __delitem__(self, key):
49        key = to_unicode(key)
50        super(CaselessDict, self).__delitem__(key.upper())
51
52    def __contains__(self, key):
53        key = to_unicode(key)
54        return super(CaselessDict, self).__contains__(key.upper())
55
56    def get(self, key, default=None):
57        key = to_unicode(key)
58        return super(CaselessDict, self).get(key.upper(), default)
59
60    def setdefault(self, key, value=None):
61        key = to_unicode(key)
62        return super(CaselessDict, self).setdefault(key.upper(), value)
63
64    def pop(self, key, default=None):
65        key = to_unicode(key)
66        return super(CaselessDict, self).pop(key.upper(), default)
67
68    def popitem(self):
69        return super(CaselessDict, self).popitem()
70
71    def has_key(self, key):
72        key = to_unicode(key)
73        return super(CaselessDict, self).__contains__(key.upper())
74
75    def update(self, *args, **kwargs):
76        # Multiple keys where key1.upper() == key2.upper() will be lost.
77        mappings = list(args) + [kwargs]
78        for mapping in mappings:
79            if hasattr(mapping, 'items'):
80                mapping = iteritems(mapping)
81            for key, value in mapping:
82                self[key] = value
83
84    def copy(self):
85        return type(self)(super(CaselessDict, self).copy())
86
87    def __repr__(self):
88        return '%s(%s)' % (type(self).__name__, dict(self))
89
90    def __eq__(self, other):
91        return self is other or dict(self.items()) == dict(other.items())
92
93    # A list of keys that must appear first in sorted_keys and sorted_items;
94    # must be uppercase.
95    canonical_order = None
96
97    def sorted_keys(self):
98        """Sorts keys according to the canonical_order for the derived class.
99        Keys not specified in canonical_order will appear at the end.
100        """
101        return canonsort_keys(self.keys(), self.canonical_order)
102
103    def sorted_items(self):
104        """Sorts items according to the canonical_order for the derived class.
105        Items not specified in canonical_order will appear at the end.
106        """
107        return canonsort_items(self, self.canonical_order)
108