1/*
2 * This file is part of libSavitar.
3 *
4 * Parts of this code have been copied from libArcus
5 * Copyright (C) 2016 Ultimaker b.v. <a.hiemstra@ultimaker.com>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published
9 * by the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20
21/**
22 * Convert a python str object to a std::string.
23 */
24%MappedType std::string
25{
26    %TypeHeaderCode
27    #include <string>
28    %End
29
30    %ConvertFromTypeCode // From C++ to python
31        PyObject* newstring;
32        newstring = PyUnicode_DecodeUTF8(sipCpp->c_str(), sipCpp->length(), NULL);
33        if(newstring == NULL)
34        {
35            PyErr_Clear();
36            newstring = PyBytes_FromString(sipCpp->c_str());
37        }
38        return newstring;
39    %End
40
41    %ConvertToTypeCode // From python to C++
42        // Allow a Python string (or a unicode string) whenever a string is
43        // expected.
44        // If argument is a Unicode string, just decode it to UTF-8
45        // If argument is a Python string, assume it's UTF-8
46        if (sipIsErr == NULL)
47        {
48            return (PyBytes_Check(sipPy) || PyUnicode_Check(sipPy));
49        }
50
51        if (sipPy == Py_None)
52        {
53            *sipCppPtr = new std::string;
54            return 1;
55        }
56
57        if (PyUnicode_Check(sipPy))
58        {
59            PyObject* s = PyUnicode_AsEncodedString(sipPy, "UTF-8", "");
60            *sipCppPtr = new std::string(PyBytes_AS_STRING(s));
61            Py_DECREF(s);
62            return 1;
63        }
64
65        if (PyBytes_Check(sipPy))
66        {
67            *sipCppPtr = new std::string(PyBytes_AS_STRING(sipPy));
68            return 1;
69        }
70        return 0;
71    %End
72};
73
74/**
75 * Convert a list filled with SceneNodes to a vector with SceneNodes.
76 */
77%MappedType std::vector<SceneNode*>
78{
79    %TypeHeaderCode
80    #include <vector>
81    %End
82
83    %ConvertFromTypeCode // From C++ to python
84        PyObject *result_list = PyList_New(sipCpp -> size());
85
86        // Create the Python list of the correct length.
87        if (!result_list)
88        {
89            return NULL;
90        }
91
92        // Go through each element in the C++ instance and convert it to a
93        // wrapped P2d.
94        for (int i = 0; i < (int)sipCpp->size(); ++i)
95        {
96            SceneNode *cpp = new SceneNode(*sipCpp->at(i));
97            PyObject *pobj = sipConvertFromType(cpp, sipType_SceneNode, sipTransferObj);
98
99            // Get the Python wrapper for the Type instance, creating a new
100            // one if necessary, and handle any ownership transfer.
101            if (!pobj)
102            {
103                // There was an error so garbage collect the Python list.
104                Py_DECREF(result_list);
105                return NULL;
106            }
107
108            // Add the wrapper to the list.
109            PyList_SET_ITEM(result_list, i, pobj);
110        }
111
112        // Return the Python list.
113        return result_list;
114    %End
115
116    %ConvertToTypeCode // From python to C++
117        // Check if type is compatible
118        if (!sipIsErr)
119        {
120            // Must be any iterable
121            PyObject *i = PyObject_GetIter(sipPy);
122            bool iterable = (i != NULL);
123            Py_XDECREF(i);
124            return iterable;
125        }
126
127        // Iterate over the object
128        PyObject *iterator = PyObject_GetIter(sipPy);
129        PyObject *item;
130
131        std::vector<SceneNode*> *result_vector = new std::vector<SceneNode*>();
132
133        while ((item = PyIter_Next(iterator)))
134        {
135            if (!sipCanConvertToType(item, sipType_SceneNode, SIP_NOT_NONE))
136            {
137                PyErr_Format(PyExc_TypeError, "object in iterable cannot be converted to SceneNode");
138                *sipIsErr = 1;
139                break;
140            }
141
142            int state;
143            SceneNode* p = reinterpret_cast<SceneNode*>(sipConvertToType(item, sipType_SceneNode, 0, SIP_NOT_NONE, &state, sipIsErr));
144
145            if (!*sipIsErr)
146            {
147                result_vector->push_back(p);
148            }
149
150            sipReleaseType(p, sipType_SceneNode, state);
151            Py_DECREF(item);
152        }
153
154        Py_DECREF(iterator);
155
156        if (*sipIsErr)
157        {
158            delete result_vector;
159            return 0;
160        }
161
162        *sipCppPtr = result_vector;
163        return sipGetState(sipTransferObj);
164    %End
165};
166
167/**
168 * Convert to and from byte arrays.
169 * Uses the internal data vector directly to create/extract a PyBytes* instance.
170 */
171%MappedType bytearray
172{
173    %TypeHeaderCode
174    #include <vector>
175    #include <cstdint>
176    #include "Types.h"
177    %End
178
179    %ConvertFromTypeCode // From C++ to python
180        return PyBytes_FromStringAndSize(reinterpret_cast<const char *>(sipCpp->data()), sipCpp->size());
181    %End
182
183    %ConvertToTypeCode // From python to C++
184        if (sipIsErr == NULL)
185        {
186            return (PyBytes_Check(sipPy));
187        }
188
189        if (sipPy == Py_None)
190        {
191            *sipCppPtr = new bytearray;
192            return 1;
193        }
194
195        if (PyBytes_Check(sipPy))
196        {
197            uint8_t *buffer = reinterpret_cast<uint8_t *>(PyBytes_AS_STRING(sipPy));
198            *sipCppPtr = new bytearray(buffer, buffer + PyBytes_GET_SIZE(sipPy));
199            return 1;
200        }
201        return 0;
202    %End
203};
204
205/**
206 * SIP generic implementation for std::vector<TYPE>
207 */
208template<TYPE>
209%MappedType std::vector<TYPE>
210{
211    %TypeHeaderCode
212    #include <vector>
213    %End
214
215    %ConvertFromTypeCode // From C++ to python
216        PyObject *result_list = PyList_New(sipCpp -> size());
217
218        // Create the Python list of the correct length.
219        if (!result_list)
220        {
221            return NULL;
222        }
223
224        // Go through each element in the C++ instance and convert it to a
225        // wrapped P2d.
226        for (int i = 0; i < (int)sipCpp->size(); ++i)
227        {
228            TYPE *cpp = new TYPE(sipCpp->at(i));
229            PyObject *pobj = sipConvertFromType(cpp, sipClass_TYPE, sipTransferObj);
230
231            // Get the Python wrapper for the Type instance, creating a new
232            // one if necessary, and handle any ownership transfer.
233            if (!pobj)
234            {
235                // There was an error so garbage collect the Python list.
236                Py_DECREF(result_list);
237                return NULL;
238            }
239
240            // Add the wrapper to the list.
241            PyList_SET_ITEM(result_list, i, pobj);
242        }
243
244        // Return the Python list.
245        return result_list;
246    %End
247
248    %ConvertToTypeCode // From python to C++
249        // Check if type is compatible
250        if (!sipIsErr)
251        {
252            // Must be any iterable
253            PyObject *i = PyObject_GetIter(sipPy);
254            bool iterable = (i != NULL);
255            Py_XDECREF(i);
256            return iterable;
257        }
258
259        // Iterate over the object
260        PyObject *iterator = PyObject_GetIter(sipPy);
261        PyObject *item;
262
263        std::vector<TYPE> *result_vector = new std::vector<TYPE>();
264
265        while ((item = PyIter_Next(iterator)))
266        {
267            if (!sipCanConvertToType(item, sipClass_TYPE, SIP_NOT_NONE))
268            {
269                PyErr_Format(PyExc_TypeError, "object in iterable cannot be converted to TYPE");
270                *sipIsErr = 1;
271                break;
272            }
273
274            int state;
275            TYPE* p = reinterpret_cast<TYPE*>(sipConvertToType(item, sipClass_TYPE, 0, SIP_NOT_NONE, &state, sipIsErr));
276
277            if (!*sipIsErr)
278            {
279                result_vector->push_back(*p);
280            }
281
282            sipReleaseType(p, sipClass_TYPE, state);
283            Py_DECREF(item);
284        }
285
286        Py_DECREF(iterator);
287
288        if (*sipIsErr)
289        {
290            delete result_vector;
291            return 0;
292        }
293
294        *sipCppPtr = result_vector;
295        return sipGetState(sipTransferObj);
296    %End
297};
298
299
300/**
301 * Convert a (python) dict with strings as keys and values to a map<string, string>.
302 */
303%MappedType std::map<std::string, std::string>
304{
305    %TypeHeaderCode
306    #include <map>
307    %End
308
309    %ConvertFromTypeCode // From C++ to python
310        // Create the dictionary.
311        PyObject *result_dict = PyDict_New();
312
313        if (!result_dict)
314        {
315            return NULL;
316        }
317
318        // Set the dictionary elements.
319        std::map<std::string, std::string>::const_iterator i = sipCpp->begin();
320
321        while (i != sipCpp->end())
322        {
323            std::string *key = new std::string((*i).first);
324            std::string *value = new std::string((*i).second);
325
326            PyObject *key_object = sipConvertFromNewType(key, sipType_std_string, sipTransferObj);
327            PyObject *value_object = sipConvertFromNewType(value, sipType_std_string, sipTransferObj);
328
329            if (key_object == NULL || value_object == NULL || PyDict_SetItem(result_dict, key_object, value_object) < 0)
330            {
331                Py_DECREF(result_dict);
332
333                if (key_object)
334                {
335                    Py_DECREF(key_object);
336                }
337                else
338                {
339                    delete key;
340                }
341
342                if (value_object)
343                {
344                    Py_DECREF(value_object);
345                }
346                else
347                {
348                    delete value;
349                }
350
351                return nullptr;
352            }
353
354            Py_DECREF(key_object);
355            Py_DECREF(value_object);
356
357            ++i;
358        }
359
360        return result_dict;
361    %End
362
363    %ConvertToTypeCode // From python to C++
364        PyObject *key_object, *value_object;
365        SIP_SSIZE_T i = 0;
366
367        // Check the type if that is all that is required.
368        if (sipIsErr == nullptr)
369        {
370            if (!PyDict_Check(sipPy))
371            {
372                return 0;
373            }
374
375            while (PyDict_Next(sipPy, &i, &key_object, &value_object))
376            {
377                if (!sipCanConvertToType(key_object, sipType_std_string, SIP_NOT_NONE))
378                {
379                    return 0;
380                }
381
382                if (!sipCanConvertToType(value_object, sipType_std_string, SIP_NOT_NONE))
383                {
384                    return 0;
385                }
386            }
387
388            return 1;
389        }
390
391        std::map<std::string, std::string> *std_map = new std::map<std::string, std::string>;
392
393        while (PyDict_Next(sipPy, &i, &key_object, &value_object))
394        {
395            int key_state, value_state;
396
397            std::string *key = reinterpret_cast<std::string *>(sipConvertToType(key_object, sipType_std_string, sipTransferObj, SIP_NOT_NONE, &key_state, sipIsErr));
398            std::string *value = reinterpret_cast<std::string *>(sipConvertToType(value_object, sipType_std_string, sipTransferObj, SIP_NOT_NONE, &value_state, sipIsErr));
399
400            if (*sipIsErr)
401            {
402                sipReleaseType(key, sipType_std_string, key_state);
403                sipReleaseType(value, sipType_std_string, value_state);
404
405                delete std_map;
406                return 0;
407            }
408
409            (*std_map)[*key] = *value;
410
411            sipReleaseType(key, sipType_std_string, key_state);
412            sipReleaseType(value, sipType_std_string, value_state);
413        }
414
415        *sipCppPtr = std_map;
416
417        return sipGetState(sipTransferObj);
418    %End
419};
420