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