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