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