1// This is the SIP interface definition for the majority of the QMap and
2// QMultiMap based mapped types.
3//
4// Copyright (c) 2021 Riverbank Computing Limited <info@riverbankcomputing.com>
5//
6// This file is part of PyQt5.
7//
8// This file may be used under the terms of the GNU General Public License
9// version 3.0 as published by the Free Software Foundation and appearing in
10// the file LICENSE included in the packaging of this file.  Please review the
11// following information to ensure the GNU General Public License version 3.0
12// requirements will be met: http://www.gnu.org/copyleft/gpl.html.
13//
14// If you do not wish to use this file under the terms of the GPL version 3.0
15// then you may purchase a commercial license.  For more information contact
16// info@riverbankcomputing.com.
17//
18// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
19// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20
21
22template<_TYPE1_, _TYPE2_>
23%MappedType QMap<_TYPE1_, _TYPE2_>
24        /TypeHint="Dict[_TYPE1_, _TYPE2_]", TypeHintValue="{}"/
25{
26%TypeHeaderCode
27#include <qmap.h>
28%End
29
30%ConvertFromTypeCode
31    PyObject *d = PyDict_New();
32
33    if (!d)
34        return 0;
35
36    QMap<_TYPE1_, _TYPE2_>::const_iterator it = sipCpp->constBegin();
37    QMap<_TYPE1_, _TYPE2_>::const_iterator end = sipCpp->constEnd();
38
39    while (it != end)
40    {
41        _TYPE1_ *k = new _TYPE1_(it.key());
42        PyObject *kobj = sipConvertFromNewType(k, sipType__TYPE1_,
43                sipTransferObj);
44
45        if (!kobj)
46        {
47            delete k;
48            Py_DECREF(d);
49
50            return 0;
51        }
52
53        _TYPE2_ *v = new _TYPE2_(it.value());
54        PyObject *vobj = sipConvertFromNewType(v, sipType__TYPE2_,
55                sipTransferObj);
56
57        if (!vobj)
58        {
59            delete v;
60            Py_DECREF(kobj);
61            Py_DECREF(d);
62
63            return 0;
64        }
65
66        int rc = PyDict_SetItem(d, kobj, vobj);
67
68        Py_DECREF(vobj);
69        Py_DECREF(kobj);
70
71        if (rc < 0)
72        {
73            Py_DECREF(d);
74
75            return 0;
76        }
77
78        ++it;
79    }
80
81    return d;
82%End
83
84%ConvertToTypeCode
85    if (!sipIsErr)
86        return PyDict_Check(sipPy);
87
88    QMap<_TYPE1_, _TYPE2_> *qm = new QMap<_TYPE1_, _TYPE2_>;
89
90    Py_ssize_t pos = 0;
91    PyObject *kobj, *vobj;
92
93    while (PyDict_Next(sipPy, &pos, &kobj, &vobj))
94    {
95        int kstate;
96        _TYPE1_ *k = reinterpret_cast<_TYPE1_ *>(
97                sipForceConvertToType(kobj, sipType__TYPE1_, sipTransferObj,
98                        SIP_NOT_NONE, &kstate, sipIsErr));
99
100        if (*sipIsErr)
101        {
102            PyErr_Format(PyExc_TypeError,
103                    "a dict key has type '%s' but '_TYPE1_' is expected",
104                    sipPyTypeName(Py_TYPE(kobj)));
105
106            delete qm;
107
108            return 0;
109        }
110
111        int vstate;
112        _TYPE2_ *v = reinterpret_cast<_TYPE2_ *>(
113                sipForceConvertToType(vobj, sipType__TYPE2_, sipTransferObj,
114                        SIP_NOT_NONE, &vstate, sipIsErr));
115
116        if (*sipIsErr)
117        {
118            PyErr_Format(PyExc_TypeError,
119                    "a dict value has type '%s' but '_TYPE2_' is expected",
120                    sipPyTypeName(Py_TYPE(vobj)));
121
122            sipReleaseType(k, sipType__TYPE1_, kstate);
123            delete qm;
124
125            return 0;
126        }
127
128        qm->insert(*k, *v);
129
130        sipReleaseType(v, sipType__TYPE2_, vstate);
131        sipReleaseType(k, sipType__TYPE1_, kstate);
132    }
133
134    *sipCppPtr = qm;
135
136    return sipGetState(sipTransferObj);
137%End
138};
139
140
141template<int, _TYPE_>
142%MappedType QMap<int, _TYPE_>
143        /TypeHint="Dict[int, _TYPE_]", TypeHintValue="{}"/
144{
145%TypeHeaderCode
146#include <qmap.h>
147%End
148
149%ConvertFromTypeCode
150    PyObject *d = PyDict_New();
151
152    if (!d)
153        return 0;
154
155    QMap<int, _TYPE_>::const_iterator it = sipCpp->constBegin();
156    QMap<int, _TYPE_>::const_iterator end = sipCpp->constEnd();
157
158    while (it != end)
159    {
160        PyObject *kobj = SIPLong_FromLong(it.key());
161
162        if (!kobj)
163        {
164            Py_DECREF(d);
165
166            return 0;
167        }
168
169        _TYPE_ *v = new _TYPE_(it.value());
170        PyObject *vobj = sipConvertFromNewType(v, sipType__TYPE_,
171                sipTransferObj);
172
173        if (!vobj)
174        {
175            delete v;
176            Py_DECREF(kobj);
177            Py_DECREF(d);
178
179            return 0;
180        }
181
182        int rc = PyDict_SetItem(d, kobj, vobj);
183
184        Py_DECREF(vobj);
185        Py_DECREF(kobj);
186
187        if (rc < 0)
188        {
189            Py_DECREF(d);
190
191            return 0;
192        }
193
194        ++it;
195    }
196
197    return d;
198%End
199
200%ConvertToTypeCode
201    if (!sipIsErr)
202        return PyDict_Check(sipPy);
203
204    QMap<int, _TYPE_> *qm = new QMap<int, _TYPE_>;
205
206    Py_ssize_t pos = 0;
207    PyObject *kobj, *vobj;
208
209    while (PyDict_Next(sipPy, &pos, &kobj, &vobj))
210    {
211        int k = sipLong_AsInt(kobj);
212
213        if (PyErr_Occurred())
214        {
215            if (PyErr_ExceptionMatches(PyExc_TypeError))
216                PyErr_Format(PyExc_TypeError,
217                        "a dict key has type '%s' but 'int' is expected",
218                        sipPyTypeName(Py_TYPE(kobj)));
219
220            delete qm;
221            *sipIsErr = 1;
222
223            return 0;
224        }
225
226        int vstate;
227        _TYPE_ *v = reinterpret_cast<_TYPE_ *>(
228                sipForceConvertToType(vobj, sipType__TYPE_, sipTransferObj,
229                        SIP_NOT_NONE, &vstate, sipIsErr));
230
231        if (*sipIsErr)
232        {
233            PyErr_Format(PyExc_TypeError,
234                    "a dict value has type '%s' but '_TYPE_' is expected",
235                    sipPyTypeName(Py_TYPE(vobj)));
236
237            delete qm;
238
239            return 0;
240        }
241
242        qm->insert(k, *v);
243
244        sipReleaseType(v, sipType__TYPE_, vstate);
245    }
246
247    *sipCppPtr = qm;
248
249    return sipGetState(sipTransferObj);
250%End
251};
252
253
254template<_TYPE1_, _TYPE2_>
255%MappedType QMultiMap<_TYPE1_, _TYPE2_>
256        /TypeHintOut="Dict[_TYPE1_, List[_TYPE2_]]", TypeHintValue="{}"/
257{
258%TypeHeaderCode
259#include <qmap.h>
260%End
261
262%ConvertFromTypeCode
263    PyObject *d = PyDict_New();
264
265    if (!d)
266        return 0;
267
268    QList<_TYPE1_> keys = sipCpp->keys();
269    QList<_TYPE1_>::const_iterator kit = keys.constBegin();
270    QList<_TYPE1_>::const_iterator kit_end = keys.constEnd();
271
272    while (kit != kit_end)
273    {
274        _TYPE1_ *k = new _TYPE1_(*kit);
275        PyObject *kobj = sipConvertFromNewType(k, sipType__TYPE1_,
276                sipTransferObj);
277
278        if (!kobj)
279        {
280            delete k;
281            Py_DECREF(d);
282
283            return 0;
284        }
285
286        // Create a Python list as the dictionary value.
287        QList<_TYPE2_> values = sipCpp->values(*kit);
288        PyObject *vobj = PyList_New(values.count());
289
290        if (!vobj)
291        {
292            Py_DECREF(kobj);
293            Py_DECREF(d);
294
295            return 0;
296        }
297
298        QList<_TYPE2_>::const_iterator vit = values.constBegin();
299        QList<_TYPE2_>::const_iterator vit_end = values.constEnd();
300
301        for (int i = 0; vit != vit_end; ++i)
302        {
303            _TYPE2_ *sv = new _TYPE2_(*vit);
304            PyObject *svobj = sipConvertFromNewType(sv, sipType__TYPE2_,
305                    sipTransferObj);
306
307            if (!svobj)
308            {
309                delete sv;
310                Py_DECREF(vobj);
311                Py_DECREF(kobj);
312                Py_DECREF(d);
313
314                return 0;
315            }
316
317            PyList_SetItem(vobj, i, svobj);
318
319            ++vit;
320        }
321
322        int rc = PyDict_SetItem(d, kobj, vobj);
323
324        Py_DECREF(vobj);
325        Py_DECREF(kobj);
326
327        if (rc < 0)
328        {
329            Py_DECREF(d);
330
331            return 0;
332        }
333
334        ++kit;
335    }
336
337    return d;
338%End
339
340%ConvertToTypeCode
341    if (!sipIsErr)
342        return PyDict_Check(sipPy);
343
344    // Note that PyQt v5.1 contains an unused implementation that can be
345    // restored if needed (although it will need updating to accept an iterable
346    // rather than just a list of values).
347    PyErr_SetString(PyExc_NotImplementedError,
348            "converting to QMultiMap<_TYPE1_, _TYPE2_> is unsupported");
349
350    return 0;
351%End
352};
353