ifdef's for future py3 support, after this adding py3 can mostly be done with defines...
[blender.git] / source / gameengine / Expressions / ListValue.cpp
1 // ListValue.cpp: implementation of the CListValue class.
2 //
3 //////////////////////////////////////////////////////////////////////
4 /*
5  * Copyright (c) 1996-2000 Erwin Coumans <coockie@acm.org>
6  *
7  * Permission to use, copy, modify, distribute and sell this software
8  * and its documentation for any purpose is hereby granted without fee,
9  * provided that the above copyright notice appear in all copies and
10  * that both that copyright notice and this permission notice appear
11  * in supporting documentation.  Erwin Coumans makes no
12  * representations about the suitability of this software for any
13  * purpose.  It is provided "as is" without express or implied warranty.
14  *
15  */
16
17 #include "ListValue.h"
18 #include "StringValue.h"
19 #include "VoidValue.h"
20 #include <algorithm>
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #if  ((PY_MAJOR_VERSION == 2) &&(PY_MINOR_VERSION < 5))
27 #define Py_ssize_t int
28 #endif
29
30 Py_ssize_t listvalue_bufferlen(PyObject* self)
31 {
32         CListValue *list= static_cast<CListValue *>(BGE_PROXY_REF(self));
33         if (list==NULL)
34                 return 0;
35         
36         return (Py_ssize_t)list->GetCount();
37 }
38
39 PyObject* listvalue_buffer_item(PyObject* self, Py_ssize_t index)
40 {
41         CListValue *list= static_cast<CListValue *>(BGE_PROXY_REF(self));
42         CValue *cval;
43         
44         if (list==NULL) {
45                 PyErr_SetString(PyExc_SystemError, "val = CList[i], "BGE_PROXY_ERROR_MSG);
46                 return NULL;
47         }
48         
49         int count = list->GetCount();
50         
51         if (index < 0)
52                 index = count+index;
53         
54         if (index < 0 || index >= count) {
55                 PyErr_SetString(PyExc_IndexError, "CList[i]: Python ListIndex out of range in CValueList");
56                 return NULL;
57         }
58         
59         cval= list->GetValue(index);
60         
61         PyObject* pyobj = cval->ConvertValueToPython();
62         if (pyobj)
63                 return pyobj;
64         else
65                 return cval->GetProxy();
66 }
67
68 PyObject* listvalue_mapping_subscript(PyObject* self, PyObject* pyindex)
69 {
70         CListValue *list= static_cast<CListValue *>(BGE_PROXY_REF(self));
71         if (list==NULL) {
72                 PyErr_SetString(PyExc_SystemError, "value = CList[i], "BGE_PROXY_ERROR_MSG);
73                 return NULL;
74         }
75         
76         if (PyString_Check(pyindex))
77         {
78                 STR_String  index(PyString_AsString(pyindex));
79                 CValue *item = ((CListValue*) list)->FindValue(index);
80                 if (item)
81                         return item->GetProxy();
82                         
83         }
84         if (PyInt_Check(pyindex))
85         {
86                 int index = PyInt_AsLong(pyindex);
87                 return listvalue_buffer_item(self, index);
88         }
89         
90         PyObject *pyindex_str = PyObject_Repr(pyindex); /* new ref */
91         PyErr_Format(PyExc_KeyError, "CList[key]: '%s' key not in list", PyString_AsString(pyindex_str));
92         Py_DECREF(pyindex_str);
93         return NULL;
94 }
95
96
97 /* just slice it into a python list... */
98 PyObject* listvalue_buffer_slice(PyObject* self,Py_ssize_t ilow, Py_ssize_t ihigh)
99 {
100         CListValue *list= static_cast<CListValue *>(BGE_PROXY_REF(self));
101         if (list==NULL) {
102                 PyErr_SetString(PyExc_SystemError, "val = CList[i:j], "BGE_PROXY_ERROR_MSG);
103                 return NULL;
104         }
105         
106         int i, j;
107         PyObject *newlist;
108
109         if (ilow < 0) ilow = 0;
110
111         int n = ((CListValue*) list)->GetCount();
112
113         if (ihigh >= n)
114                 ihigh = n;
115     if (ihigh < ilow)
116         ihigh = ilow;
117
118         newlist = PyList_New(ihigh - ilow);
119         if (!newlist)
120                 return NULL;
121
122         for (i = ilow, j = 0; i < ihigh; i++, j++)
123         {
124                 PyObject* pyobj = list->GetValue(i)->ConvertValueToPython();
125                 if (!pyobj)
126                         pyobj = list->GetValue(i)->GetProxy();
127                 PyList_SET_ITEM(newlist, i, pyobj);
128         }       
129         return newlist;
130 }
131
132
133 /* clist + list, return a list that python owns */
134 static PyObject *listvalue_buffer_concat(PyObject * self, PyObject * other)
135 {
136         CListValue *listval= static_cast<CListValue *>(BGE_PROXY_REF(self));
137         int i, numitems, numitems_orig;
138         
139         if (listval==NULL) {
140                 PyErr_SetString(PyExc_SystemError, "CList+other, "BGE_PROXY_ERROR_MSG);
141                 return NULL;
142         }
143         
144         numitems_orig= listval->GetCount();
145         
146         // for now, we support CListValue concatenated with items
147         // and CListValue concatenated to Python Lists
148         // and CListValue concatenated with another CListValue
149         
150         /* Shallow copy, dont use listval->GetReplica(), it will screw up with KX_GameObjects */
151         CListValue* listval_new = new CListValue();
152         
153         if (PyList_Check(other))
154         {
155                 CValue* listitemval;
156                 bool error = false;
157                 
158                 numitems = PyList_Size(other);
159                 
160                 /* copy the first part of the list */
161                 listval_new->Resize(numitems_orig + numitems);
162                 for (i=0;i<numitems_orig;i++)
163                         listval_new->SetValue(i, listval->GetValue(i)->AddRef());
164                 
165                 for (i=0;i<numitems;i++)
166                 {
167                         listitemval = listval->ConvertPythonToValue(PyList_GetItem(other,i), "cList + pyList: CListValue, ");
168                         
169                         if (listitemval) {
170                                 listval_new->SetValue(i+numitems_orig, listitemval);
171                         } else {
172                                 error= true;
173                                 break;
174                         }
175                 }
176                 
177                 if (error) {
178                         listval_new->Resize(numitems_orig+i); /* resize so we dont try release NULL pointers */
179                         listval_new->Release();
180                         return NULL; /* ConvertPythonToValue above sets the error */ 
181                 }
182         
183         }
184         else if (PyObject_TypeCheck(other, &CListValue::Type)) {
185                 // add items from otherlist to this list
186                 CListValue* otherval = static_cast<CListValue *>(BGE_PROXY_REF(other));
187                 if(otherval==NULL) {
188                         listval_new->Release();
189                         PyErr_SetString(PyExc_SystemError, "CList+other, "BGE_PROXY_ERROR_MSG);
190                         return NULL;
191                 }
192                 
193                 numitems = otherval->GetCount();
194                 
195                 /* copy the first part of the list */
196                 listval_new->Resize(numitems_orig + numitems); /* resize so we dont try release NULL pointers */
197                 for (i=0;i<numitems_orig;i++)
198                         listval_new->SetValue(i, listval->GetValue(i)->AddRef());
199                 
200                 /* now copy the other part of the list */
201                 for (i=0;i<numitems;i++)
202                         listval_new->SetValue(i+numitems_orig, otherval->GetValue(i)->AddRef());
203                 
204         }
205         return listval_new->NewProxy(true); /* python owns this list */
206 }
207
208
209
210 static  PySequenceMethods listvalue_as_sequence = {
211         listvalue_bufferlen,//(inquiry)buffer_length, /*sq_length*/
212         listvalue_buffer_concat, /*sq_concat*/
213         NULL, /*sq_repeat*/
214         listvalue_buffer_item, /*sq_item*/
215 #if (PY_VERSION_HEX >= 0x03000000) // TODO, slicing in py3?
216         NULL,
217         NULL,
218         NULL,
219 #else
220         listvalue_buffer_slice, /*sq_slice*/
221         (ssizeobjargproc)NULL, /*sq_ass_item*/
222         NULL, /*sq_ass_slice*/
223 #endif
224 };
225
226
227
228 /* Is this one used ? */
229 static  PyMappingMethods instance_as_mapping = {
230         listvalue_bufferlen, /*mp_length*/
231         listvalue_mapping_subscript, /*mp_subscript*/
232         NULL /*mp_ass_subscript*/
233 };
234
235
236
237 PyTypeObject CListValue::Type = {
238 #if (PY_VERSION_HEX >= 0x02060000)
239         PyVarObject_HEAD_INIT(NULL, 0)
240 #else
241         /* python 2.5 and below */
242         PyObject_HEAD_INIT( NULL )  /* required py macro */
243         0,                              /*ob_size*/
244 #endif
245         "CListValue",                   /*tp_name*/
246         sizeof(PyObjectPlus_Proxy), /*tp_basicsize*/
247         0,                              /*tp_itemsize*/
248         /* methods */
249         py_base_dealloc,                        /*tp_dealloc*/
250         0,                              /*tp_print*/
251         0,                      /*tp_getattr*/
252         0,                      /*tp_setattr*/
253         0,                              /*tp_compare*/
254         py_base_repr,                           /*tp_repr*/
255         0,                              /*tp_as_number*/
256         &listvalue_as_sequence, /*tp_as_sequence*/
257         &instance_as_mapping,           /*tp_as_mapping*/
258         0,                              /*tp_hash*/
259         0,                              /*tp_call */
260         0,
261         py_base_getattro,
262         py_base_setattro,
263         0,0,0,0,0,0,0,0,0,
264         Methods
265 };
266
267
268
269 PyParentObject CListValue::Parents[] = {
270         &CListValue::Type,
271         &CValue::Type,
272                 NULL
273 };
274
275
276
277
278 PyMethodDef CListValue::Methods[] = {
279         {"append", (PyCFunction)CListValue::sPyappend,METH_O},
280         {"reverse", (PyCFunction)CListValue::sPyreverse,METH_NOARGS},
281         {"index", (PyCFunction)CListValue::sPyindex,METH_O},
282         {"count", (PyCFunction)CListValue::sPycount,METH_O},
283         {"from_id", (PyCFunction)CListValue::sPyfrom_id,METH_O},
284         
285         {NULL,NULL} //Sentinel
286 };
287
288 PyAttributeDef CListValue::Attributes[] = {
289         { NULL }        //Sentinel
290 };
291
292 PyObject* CListValue::py_getattro(PyObject* attr) {
293         py_getattro_up(CValue);
294 }
295
296 PyObject* CListValue::py_getattro_dict() {
297         py_getattro_dict_up(CValue);
298 }
299
300
301 //////////////////////////////////////////////////////////////////////
302 // Construction/Destruction
303 //////////////////////////////////////////////////////////////////////
304
305 CListValue::CListValue(PyTypeObject *T ) 
306 : CPropValue(T)
307 {
308         m_bReleaseContents=true;        
309 }
310
311
312
313 CListValue::~CListValue()
314 {
315
316         if (m_bReleaseContents) {
317                 for (unsigned int i=0;i<m_pValueArray.size();i++) {
318                         m_pValueArray[i]->Release();
319                 }
320         }
321 }
322
323
324 static STR_String gstrListRep=STR_String("List");
325
326 const STR_String & CListValue::GetText()
327 {
328         gstrListRep = "[";
329         STR_String commastr = "";
330
331         for (int i=0;i<GetCount();i++)
332         {
333                 gstrListRep += commastr;
334                 gstrListRep += GetValue(i)->GetText();
335                 commastr = ",";
336         }
337         gstrListRep += "]";
338
339         return gstrListRep;
340 }
341
342
343
344 CValue* CListValue::GetReplica() { 
345         CListValue* replica = new CListValue(*this);
346
347         replica->ProcessReplica();
348
349         replica->m_bReleaseContents=true; // for copy, complete array is copied for now...
350         // copy all values
351         int numelements = m_pValueArray.size();
352         unsigned int i=0;
353         replica->m_pValueArray.resize(numelements);
354         for (i=0;i<m_pValueArray.size();i++)
355                 replica->m_pValueArray[i] = m_pValueArray[i]->GetReplica();
356
357
358         return replica;
359 };
360
361
362
363 void CListValue::SetValue(int i, CValue *val)
364 {
365         assertd(i < m_pValueArray.size());
366         m_pValueArray[i]=val;
367 }
368
369
370
371 void CListValue::Resize(int num)
372 {
373         m_pValueArray.resize(num);
374 }
375
376
377
378 void CListValue::Remove(int i)
379 {
380         assertd(i<m_pValueArray.size());
381         m_pValueArray.erase(m_pValueArray.begin()+i);
382 }
383
384
385
386 void CListValue::ReleaseAndRemoveAll()
387 {
388         for (unsigned int i=0;i<m_pValueArray.size();i++)
389                 m_pValueArray[i]->Release();
390         m_pValueArray.clear();//.Clear();
391 }
392
393
394
395 CValue* CListValue::FindValue(const STR_String & name)
396 {
397         CValue* resultval = NULL;
398         int i=0;
399         
400         while (!resultval && i < GetCount())
401         {
402                 CValue* myval = GetValue(i);
403                                 
404                 if (myval->GetName() == name)
405                         resultval = GetValue(i)->AddRef(); // add referencecount
406                 else
407                         i++;
408                 
409         }
410         return resultval;
411 }
412
413
414
415 bool CListValue::SearchValue(CValue *val)
416 {
417         for (int i=0;i<GetCount();i++)
418                 if (val == GetValue(i))
419                         return true;
420         return false;
421 }
422
423
424
425 void CListValue::SetReleaseOnDestruct(bool bReleaseContents)
426 {
427         m_bReleaseContents = bReleaseContents;
428 }
429
430
431
432 bool CListValue::RemoveValue(CValue *val)
433 {
434         bool result=false;
435
436         for (int i=GetCount()-1;i>=0;i--)
437                 if (val == GetValue(i))
438                 {
439                         Remove(i);
440                         result=true;
441                 }
442         return result;
443 }
444
445
446
447 void CListValue::MergeList(CListValue *otherlist)
448 {
449
450         int numelements = this->GetCount();
451         int numotherelements = otherlist->GetCount();
452
453
454         Resize(numelements+numotherelements);
455
456         for (int i=0;i<numotherelements;i++)
457         {
458                 SetValue(i+numelements,otherlist->GetValue(i)->AddRef());
459         }
460 }
461
462
463 PyObject* CListValue::Pyappend(PyObject* value)
464 {
465         CValue* objval = ConvertPythonToValue(value, "CList.append(i): CValueList, ");
466
467         if (!objval) /* ConvertPythonToValue sets the error */
468                 return NULL;
469         
470         if (!BGE_PROXY_PYOWNS(m_proxy)) {
471                 PyErr_SetString(PyExc_TypeError, "CList.append(i): this CValueList is used internally for the game engine and can't be modified");
472                 return NULL;
473         }
474         
475         Add(objval);
476         
477         Py_RETURN_NONE;
478 }
479
480
481
482 PyObject* CListValue::Pyreverse()
483 {
484         std::reverse(m_pValueArray.begin(),m_pValueArray.end());
485         Py_RETURN_NONE;
486 }
487
488
489
490 bool CListValue::CheckEqual(CValue* first,CValue* second)
491 {
492         bool result = false;
493
494         CValue* eqval =  ((CValue*)first)->Calc(VALUE_EQL_OPERATOR,(CValue*)second);
495         
496         if (eqval==NULL)
497                 return false;
498                 
499         STR_String txt = eqval->GetText();
500         eqval->Release();
501         if (txt=="TRUE")
502         {
503                 result = true;
504         }
505         return result;
506
507 }
508
509
510
511 PyObject* CListValue::Pyindex(PyObject *value)
512 {
513         PyObject* result = NULL;
514
515         CValue* checkobj = ConvertPythonToValue(value, "val = cList[i]: CValueList, ");
516         if (checkobj==NULL)
517                 return NULL; /* ConvertPythonToValue sets the error */
518
519         int numelem = GetCount();
520         for (int i=0;i<numelem;i++)
521         {
522                 CValue* elem =                  GetValue(i);
523                 if (CheckEqual(checkobj,elem))
524                 {
525                         result = PyInt_FromLong(i);
526                         break;
527                 }
528         }
529         checkobj->Release();
530
531         if (result==NULL) {
532                 PyErr_SetString(PyExc_ValueError, "CList.index(x): x not in CListValue");
533         }
534         return result;
535         
536 }
537
538
539
540 PyObject* CListValue::Pycount(PyObject* value)
541 {
542         int numfound = 0;
543
544         CValue* checkobj = ConvertPythonToValue(value, ""); /* error ignored */
545         
546         if (checkobj==NULL) { /* in this case just return that there are no items in the list */
547                 PyErr_Clear();
548                 return PyInt_FromLong(0);
549         }
550
551         int numelem = GetCount();
552         for (int i=0;i<numelem;i++)
553         {
554                 CValue* elem =                  GetValue(i);
555                 if (CheckEqual(checkobj,elem))
556                 {
557                         numfound ++;
558                 }
559         }
560         checkobj->Release();
561
562         return PyInt_FromLong(numfound);
563 }
564
565
566
567 PyObject* CListValue::Pyfrom_id(PyObject* value)
568 {
569         uintptr_t id= (uintptr_t)PyLong_AsVoidPtr(value);
570         
571         if (PyErr_Occurred())
572                 return NULL;
573
574         int numelem = GetCount();
575         for (int i=0;i<numelem;i++)
576         {
577                 if (reinterpret_cast<uintptr_t>(m_pValueArray[i]->m_proxy) == id)
578                         return GetValue(i)->GetProxy();
579         }
580         PyErr_SetString(PyExc_IndexError, "from_id(#): id not found in CValueList");
581         return NULL;    
582
583 }
584
585
586 /* --------------------------------------------------------------------- 
587  * Some stuff taken from the header
588  * --------------------------------------------------------------------- */
589 CValue* CListValue::Calc(VALUE_OPERATOR op,CValue *val) 
590 {
591         //assert(false); // todo: implement me!
592         static int error_printed =  0;
593         if (error_printed==0) {
594                 fprintf(stderr, "CValueList::Calc not yet implimented\n");
595                 error_printed = 1;
596         }
597         return NULL;
598 }
599
600 CValue* CListValue::CalcFinal(VALUE_DATA_TYPE dtype,
601                                                           VALUE_OPERATOR op, 
602                                                           CValue* val) 
603 {
604         //assert(false); // todo: implement me!
605         static int error_printed =  0;
606         if (error_printed==0) {
607                 fprintf(stderr, "CValueList::CalcFinal not yet implimented\n");
608                 error_printed = 1;
609         }
610         return NULL;
611 }
612
613
614
615 void CListValue::Add(CValue* value)
616 {
617         m_pValueArray.push_back(value);
618 }
619
620
621
622 double CListValue::GetNumber()
623 {
624         return -1;
625 }
626
627
628
629 void CListValue::SetModified(bool bModified)
630 {       
631         CValue::SetModified(bModified);
632         int numels = GetCount();
633
634         for (int i=0;i<numels;i++)
635                 GetValue(i)->SetModified(bModified);
636 }
637
638
639
640 bool CListValue::IsModified()
641 {
642         bool bmod = CValue::IsModified(); //normal own flag
643         int numels = GetCount();
644
645         for (int i=0;i<numels;i++)
646                 bmod = bmod || GetValue(i)->IsModified();
647
648         return bmod;
649 }