Merge from trunk -r 23000:23968.
[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 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30
31
32 //////////////////////////////////////////////////////////////////////
33 // Construction/Destruction
34 //////////////////////////////////////////////////////////////////////
35
36 CListValue::CListValue()
37 : CPropValue()
38 {
39         m_bReleaseContents=true;
40 }
41
42
43
44 CListValue::~CListValue()
45 {
46
47         if (m_bReleaseContents) {
48                 for (unsigned int i=0;i<m_pValueArray.size();i++) {
49                         m_pValueArray[i]->Release();
50                 }
51         }
52 }
53
54
55 static STR_String gstrListRep=STR_String("List");
56
57 const STR_String & CListValue::GetText()
58 {
59         gstrListRep = "[";
60         STR_String commastr = "";
61
62         for (int i=0;i<GetCount();i++)
63         {
64                 gstrListRep += commastr;
65                 gstrListRep += GetValue(i)->GetText();
66                 commastr = ",";
67         }
68         gstrListRep += "]";
69
70         return gstrListRep;
71 }
72
73
74
75 CValue* CListValue::GetReplica() {
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 implimented\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 implimented\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 #ifndef DISABLE_PYTHON
276
277 /* --------------------------------------------------------------------- */
278 /* Python interface ---------------------------------------------------- */
279 /* --------------------------------------------------------------------- */
280
281 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 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 PyObject* listvalue_mapping_subscript(PyObject* self, PyObject* pyindex)
320 {
321         CListValue *list= static_cast<CListValue *>(BGE_PROXY_REF(self));
322         if (list==NULL) {
323                 PyErr_SetString(PyExc_SystemError, "value = CList[i], "BGE_PROXY_ERROR_MSG);
324                 return NULL;
325         }
326         
327         if (PyUnicode_Check(pyindex))
328         {
329                 CValue *item = ((CListValue*) list)->FindValue(_PyUnicode_AsString(pyindex));
330                 if (item) {
331                         PyObject* pyobj = item->ConvertValueToPython();
332                         if(pyobj)
333                                 return pyobj;
334                         else
335                                 return item->GetProxy();
336                 }
337         }
338         else if (PyLong_Check(pyindex))
339         {
340                 int index = PyLong_AsSsize_t(pyindex);
341                 return listvalue_buffer_item(self, index); /* wont add a ref */
342         }
343         
344         PyObject *pyindex_str = PyObject_Repr(pyindex); /* new ref */
345         PyErr_Format(PyExc_KeyError, "CList[key]: '%s' key not in list", _PyUnicode_AsString(pyindex_str));
346         Py_DECREF(pyindex_str);
347         return NULL;
348 }
349
350
351 /* just slice it into a python list... */
352 PyObject* listvalue_buffer_slice(PyObject* self,Py_ssize_t ilow, Py_ssize_t ihigh)
353 {
354         CListValue *list= static_cast<CListValue *>(BGE_PROXY_REF(self));
355         if (list==NULL) {
356                 PyErr_SetString(PyExc_SystemError, "val = CList[i:j], "BGE_PROXY_ERROR_MSG);
357                 return NULL;
358         }
359         
360         int i, j;
361         PyObject *newlist;
362
363         if (ilow < 0) ilow = 0;
364
365         int n = ((CListValue*) list)->GetCount();
366
367         if (ihigh >= n)
368                 ihigh = n;
369     if (ihigh < ilow)
370         ihigh = ilow;
371
372         newlist = PyList_New(ihigh - ilow);
373         if (!newlist)
374                 return NULL;
375
376         for (i = ilow, j = 0; i < ihigh; i++, j++)
377         {
378                 PyObject* pyobj = list->GetValue(i)->ConvertValueToPython();
379                 if (!pyobj)
380                         pyobj = list->GetValue(i)->GetProxy();
381                 PyList_SET_ITEM(newlist, i, pyobj);
382         }       
383         return newlist;
384 }
385
386
387 /* clist + list, return a list that python owns */
388 static PyObject *listvalue_buffer_concat(PyObject * self, PyObject * other)
389 {
390         CListValue *listval= static_cast<CListValue *>(BGE_PROXY_REF(self));
391         int i, numitems, numitems_orig;
392         
393         if (listval==NULL) {
394                 PyErr_SetString(PyExc_SystemError, "CList+other, "BGE_PROXY_ERROR_MSG);
395                 return NULL;
396         }
397         
398         numitems_orig= listval->GetCount();
399         
400         // for now, we support CListValue concatenated with items
401         // and CListValue concatenated to Python Lists
402         // and CListValue concatenated with another CListValue
403         
404         /* Shallow copy, dont use listval->GetReplica(), it will screw up with KX_GameObjects */
405         CListValue* listval_new = new CListValue();
406         
407         if (PyList_Check(other))
408         {
409                 CValue* listitemval;
410                 bool error = false;
411                 
412                 numitems = PyList_Size(other);
413                 
414                 /* copy the first part of the list */
415                 listval_new->Resize(numitems_orig + numitems);
416                 for (i=0;i<numitems_orig;i++)
417                         listval_new->SetValue(i, listval->GetValue(i)->AddRef());
418                 
419                 for (i=0;i<numitems;i++)
420                 {
421                         listitemval = listval->ConvertPythonToValue(PyList_GetItem(other,i), "cList + pyList: CListValue, ");
422                         
423                         if (listitemval) {
424                                 listval_new->SetValue(i+numitems_orig, listitemval);
425                         } else {
426                                 error= true;
427                                 break;
428                         }
429                 }
430                 
431                 if (error) {
432                         listval_new->Resize(numitems_orig+i); /* resize so we dont try release NULL pointers */
433                         listval_new->Release();
434                         return NULL; /* ConvertPythonToValue above sets the error */ 
435                 }
436         
437         }
438         else if (PyObject_TypeCheck(other, &CListValue::Type)) {
439                 // add items from otherlist to this list
440                 CListValue* otherval = static_cast<CListValue *>(BGE_PROXY_REF(other));
441                 if(otherval==NULL) {
442                         listval_new->Release();
443                         PyErr_SetString(PyExc_SystemError, "CList+other, "BGE_PROXY_ERROR_MSG);
444                         return NULL;
445                 }
446                 
447                 numitems = otherval->GetCount();
448                 
449                 /* copy the first part of the list */
450                 listval_new->Resize(numitems_orig + numitems); /* resize so we dont try release NULL pointers */
451                 for (i=0;i<numitems_orig;i++)
452                         listval_new->SetValue(i, listval->GetValue(i)->AddRef());
453                 
454                 /* now copy the other part of the list */
455                 for (i=0;i<numitems;i++)
456                         listval_new->SetValue(i+numitems_orig, otherval->GetValue(i)->AddRef());
457                 
458         }
459         return listval_new->NewProxy(true); /* python owns this list */
460 }
461
462 static int listvalue_buffer_contains(PyObject *self_v, PyObject *value)
463 {
464         CListValue *self= static_cast<CListValue *>(BGE_PROXY_REF(self_v));
465         
466         if (self==NULL) {
467                 PyErr_SetString(PyExc_SystemError, "val in CList, "BGE_PROXY_ERROR_MSG);
468                 return -1;
469         }
470         
471         if (PyUnicode_Check(value)) {
472                 if (self->FindValue((const char *)_PyUnicode_AsString(value))) {
473                         return 1;
474                 }
475         }
476         else if (PyObject_TypeCheck(value, &CValue::Type)) { /* not dict like at all but this worked before __contains__ was used */
477                 CValue *item= static_cast<CValue *>(BGE_PROXY_REF(value));
478                 for (int i=0; i < self->GetCount(); i++)
479                         if (self->GetValue(i) == item) // Com
480                                 return 1;
481                 
482         } // not using CheckEqual
483         
484         return 0;
485 }
486
487
488 static  PySequenceMethods listvalue_as_sequence = {
489         listvalue_bufferlen,//(inquiry)buffer_length, /*sq_length*/
490         listvalue_buffer_concat, /*sq_concat*/
491         NULL, /*sq_repeat*/
492         listvalue_buffer_item, /*sq_item*/
493 // TODO, slicing in py3
494         NULL, // listvalue_buffer_slice, /*sq_slice*/
495         NULL, /*sq_ass_item*/
496         NULL, /*sq_ass_slice*/
497         (objobjproc)listvalue_buffer_contains,  /* sq_contains */
498 };
499
500
501
502 /* Is this one used ? */
503 static  PyMappingMethods instance_as_mapping = {
504         listvalue_bufferlen, /*mp_length*/
505         listvalue_mapping_subscript, /*mp_subscript*/
506         NULL /*mp_ass_subscript*/
507 };
508
509
510
511 PyTypeObject CListValue::Type = {
512         PyVarObject_HEAD_INIT(NULL, 0)
513         "CListValue",                   /*tp_name*/
514         sizeof(PyObjectPlus_Proxy), /*tp_basicsize*/
515         0,                              /*tp_itemsize*/
516         /* methods */
517         py_base_dealloc,                        /*tp_dealloc*/
518         0,                              /*tp_print*/
519         0,                      /*tp_getattr*/
520         0,                      /*tp_setattr*/
521         0,                              /*tp_compare*/
522         py_base_repr,                           /*tp_repr*/
523         0,                              /*tp_as_number*/
524         &listvalue_as_sequence, /*tp_as_sequence*/
525         &instance_as_mapping,           /*tp_as_mapping*/
526         0,                              /*tp_hash*/
527         0,                              /*tp_call */
528         0,
529         NULL,
530         NULL,
531         0,
532         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
533         0,0,0,0,0,0,0,
534         Methods,
535         0,
536         0,
537         &CValue::Type,
538         0,0,0,0,0,0,
539         py_base_new
540 };
541
542 PyMethodDef CListValue::Methods[] = {
543         /* List style access */
544         {"append", (PyCFunction)CListValue::sPyappend,METH_O},
545         {"reverse", (PyCFunction)CListValue::sPyreverse,METH_NOARGS},
546         {"index", (PyCFunction)CListValue::sPyindex,METH_O},
547         {"count", (PyCFunction)CListValue::sPycount,METH_O},
548
549         /* Dict style access */
550         {"get", (PyCFunction)CListValue::sPyget,METH_VARARGS},
551
552         /* Own cvalue funcs */
553         {"from_id", (PyCFunction)CListValue::sPyfrom_id,METH_O},
554
555         {NULL,NULL} //Sentinel
556 };
557
558 PyAttributeDef CListValue::Attributes[] = {
559         { NULL }        //Sentinel
560 };
561
562 PyObject* CListValue::Pyappend(PyObject* value)
563 {
564         CValue* objval = ConvertPythonToValue(value, "CList.append(i): CValueList, ");
565
566         if (!objval) /* ConvertPythonToValue sets the error */
567                 return NULL;
568
569         if (!BGE_PROXY_PYOWNS(m_proxy)) {
570                 PyErr_SetString(PyExc_TypeError, "CList.append(i): this CValueList is used internally for the game engine and can't be modified");
571                 return NULL;
572         }
573
574         Add(objval);
575
576         Py_RETURN_NONE;
577 }
578
579 PyObject* CListValue::Pyreverse()
580 {
581         std::reverse(m_pValueArray.begin(),m_pValueArray.end());
582         Py_RETURN_NONE;
583 }
584
585 PyObject* CListValue::Pyindex(PyObject *value)
586 {
587         PyObject* result = NULL;
588
589         CValue* checkobj = ConvertPythonToValue(value, "val = cList[i]: CValueList, ");
590         if (checkobj==NULL)
591                 return NULL; /* ConvertPythonToValue sets the error */
592
593         int numelem = GetCount();
594         for (int i=0;i<numelem;i++)
595         {
596                 CValue* elem =                  GetValue(i);
597                 if (checkobj==elem || CheckEqual(checkobj,elem))
598                 {
599                         result = PyLong_FromSsize_t(i);
600                         break;
601                 }
602         }
603         checkobj->Release();
604
605         if (result==NULL) {
606                 PyErr_SetString(PyExc_ValueError, "CList.index(x): x not in CListValue");
607         }
608         return result;
609
610 }
611
612
613
614 PyObject* CListValue::Pycount(PyObject* value)
615 {
616         int numfound = 0;
617
618         CValue* checkobj = ConvertPythonToValue(value, ""); /* error ignored */
619
620         if (checkobj==NULL) { /* in this case just return that there are no items in the list */
621                 PyErr_Clear();
622                 return PyLong_FromSsize_t(0);
623         }
624
625         int numelem = GetCount();
626         for (int i=0;i<numelem;i++)
627         {
628                 CValue* elem =                  GetValue(i);
629                 if (checkobj==elem || CheckEqual(checkobj,elem))
630                 {
631                         numfound ++;
632                 }
633         }
634         checkobj->Release();
635
636         return PyLong_FromSsize_t(numfound);
637 }
638
639 /* Matches python dict.get(key, [default]) */
640 PyObject* CListValue::Pyget(PyObject *args)
641 {
642         char *key;
643         PyObject* def = Py_None;
644
645         if (!PyArg_ParseTuple(args, "s|O:get", &key, &def))
646                 return NULL;
647
648         CValue *item = FindValue((const char *)key);
649         if (item) {
650                 PyObject* pyobj = item->ConvertValueToPython();
651                 if (pyobj)
652                         return pyobj;
653                 else
654                         return item->GetProxy();
655         }
656         Py_INCREF(def);
657         return def;
658 }
659
660
661 PyObject* CListValue::Pyfrom_id(PyObject* value)
662 {
663         uintptr_t id= (uintptr_t)PyLong_AsVoidPtr(value);
664
665         if (PyErr_Occurred())
666                 return NULL;
667
668         int numelem = GetCount();
669         for (int i=0;i<numelem;i++)
670         {
671                 if (reinterpret_cast<uintptr_t>(m_pValueArray[i]->m_proxy) == id)
672                         return GetValue(i)->GetProxy();
673         }
674         PyErr_SetString(PyExc_IndexError, "from_id(#): id not found in CValueList");
675         return NULL;
676
677 }
678
679 #endif // DISABLE_PYTHON