1# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
2# All rights reserved.
3#
4# This software is provided without warranty under the terms of the BSD
5# license included in LICENSE.txt and may be redistributed only under
6# the conditions described in the aforementioned license. The license
7# is also available online at http://www.enthought.com/licenses/BSD.txt
8#
9# Thanks for using Enthought open source!
10
11from codecs import decode, encode
12from functools import partial
13from typing import Any as TAny, Callable as TCallable, NamedTuple
14
15from traits.api import Any, HasStrictTraits, Interface
16
17
18class DataFormat(NamedTuple):
19    """ Information about a mimetype and serializers.
20
21    Simple namedtuple-based class that stores the mimetype, serializer and
22    deserializer together.
23    """
24
25    #: The mimetype of the data.
26    mimetype: str
27
28    #: A callable that serializes this format.  It should take an python
29    #: object of a supported type, and return a bytestring.
30    serialize: TCallable[[TAny], bytes]
31
32    #: A callable that deserializes this format.  It should take a
33    #: bytestring and return the extracted object.
34    deserialize: TCallable[[bytes], TAny]
35
36
37def text_format(encoding='utf-8', mimetype='text/plain'):
38    """ DataFormat factory for text mimetypes.
39    """
40    return DataFormat(
41        mimetype=mimetype,
42        serialize=partial(encode, encoding=encoding),
43        deserialize=partial(decode, encoding=encoding),
44    )
45
46
47class IDataWrapper(Interface):
48    """ Wrapper around polymorphic toolkit data object containing mimedata.
49
50    To support clipboard and drag and drop operations, toolkits need a way
51    of generically representing data in multiple formats.  This is a wrapper
52    class that provides a toolkit independent intreface to these classes
53    which allow the exchange of data, with types specified by MIME types.
54    """
55
56    #: The toolkit data object.
57    toolkit_data = Any()
58
59    def mimetypes(self):
60        """ Return a set of mimetypes holding data.
61
62        Returns
63        -------
64        mimetypes : set of str
65            The set of mimetypes currently storing data in the toolkit data
66            object.
67        """
68        pass
69
70    def has_format(self, format):
71        """ Whether or not a particular format has available data.
72
73        Parameters
74        ----------
75        format : DataFormat
76            A data format object.
77
78        Returns
79        -------
80        has_format : bool
81            Whether or not there is data associated with that format in the
82            underlying toolkit object.
83        """
84        raise NotImplementedError()
85
86    def get_format(self, format):
87        """ The decoded data associted with the format.
88
89        Parameters
90        ----------
91        format : DataFormat
92            A data format object.
93
94        Returns
95        -------
96        data : any
97            The data decoded for the given format.
98        """
99        raise NotImplementedError()
100
101    def set_format(self, format, data):
102        """ Encode and set data for the format.
103
104        Parameters
105        ----------
106        format : DataFormat
107            A data format object.
108        data : any
109            The data to be encoded and stored.
110        """
111        raise NotImplementedError()
112
113    def get_mimedata(self, mimetype):
114        """ Get raw data for the given media type.
115
116        Parameters
117        ----------
118        mimetype : str
119            The mime media type to be extracted.
120
121        Returns
122        -------
123        mimedata : bytes
124            The mime media data as bytes.
125        """
126        raise NotImplementedError()
127
128    def set_mimedata(self, mimetype, mimedata):
129        """ Set raw data for the given media type.
130
131        Parameters
132        ----------
133        mimetype : str
134            The mime media type to be extracted.
135        mimedata : bytes
136            The mime media data encoded as bytes..
137        """
138        raise NotImplementedError()
139
140
141class MDataWrapper(HasStrictTraits):
142    """ Mixin class for DataWrappers.
143
144    This provides standard methods for using DataFormat objects, but not the
145    low-level communication with the underlying toolkit.
146    """
147
148    def has_format(self, format):
149        """ Whether or not a particular format has available data.
150
151        Parameters
152        ----------
153        format : DataFormat
154            A data format object.
155
156        Returns
157        -------
158        has_format : bool
159            Whether or not there is data associated with that format in the
160            underlying toolkit object.
161        """
162        return format.mimetype in self.mimetypes()
163
164    def get_format(self, format):
165        """ The decoded data associted with the format.
166
167        Parameters
168        ----------
169        format : DataFormat
170            A data format object.
171
172        Returns
173        -------
174        data : any
175            The data decoded for the given format.
176        """
177        return format.deserialize(self.get_mimedata(format.mimetype))
178
179    def set_format(self, format, data):
180        """ Encode and set data for the format.
181
182        Parameters
183        ----------
184        format : DataFormat
185            A data format object.
186        data : any
187            The data to be encoded and stored.
188        """
189        self.set_mimedata(format.mimetype, format.serialize(data))
190