remove config.h references, was added for automake build system rev around 124-126...
[blender-staging.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 };
495
496
497
498 /* Is this one used ? */
499 static  PyMappingMethods instance_as_mapping = {
500         listvalue_bufferlen, /*mp_length*/
501         listvalue_mapping_subscript, /*mp_subscript*/
502         NULL /*mp_ass_subscript*/
503 };
504
505
506
507 PyTypeObject CListValue::Type = {
508         PyVarObject_HEAD_INIT(NULL, 0)
509         "CListValue",                   /*tp_name*/
510         sizeof(PyObjectPlus_Proxy), /*tp_basicsize*/
511         0,                              /*tp_itemsize*/
512         /* methods */
513         py_base_dealloc,                        /*tp_dealloc*/
514         0,                              /*tp_print*/
515         0,                      /*tp_getattr*/
516         0,                      /*tp_setattr*/
517         0,                              /*tp_compare*/
518         py_base_repr,                           /*tp_repr*/
519         0,                              /*tp_as_number*/
520         &listvalue_as_sequence, /*tp_as_sequence*/
521         &instance_as_mapping,           /*tp_as_mapping*/
522         0,                              /*tp_hash*/
523         0,                              /*tp_call */
524         0,
525         NULL,
526         NULL,
527         0,
528         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
529         0,0,0,0,0,0,0,
530         Methods,
531         0,
532         0,
533         &CValue::Type,
534         0,0,0,0,0,0,
535         py_base_new
536 };
537
538 PyMethodDef CListValue::Methods[] = {
539         /* List style access */
540         {"append", (PyCFunction)CListValue::sPyappend,METH_O},
541         {"reverse", (PyCFunction)CListValue::sPyreverse,METH_NOARGS},
542         {"index", (PyCFunction)CListValue::sPyindex,METH_O},
543         {"count", (PyCFunction)CListValue::sPycount,METH_O},
544
545         /* Dict style access */
546         {"get", (PyCFunction)CListValue::sPyget,METH_VARARGS},
547
548         /* Own cvalue funcs */
549         {"from_id", (PyCFunction)CListValue::sPyfrom_id,METH_O},
550
551         {NULL,NULL} //Sentinel
552 };
553
554 PyAttributeDef CListValue::Attributes[] = {
555         { NULL }        //Sentinel
556 };
557
558 PyObject* CListValue::Pyappend(PyObject* value)
559 {
560         CValue* objval = ConvertPythonToValue(value, "CList.append(i): CValueList, ");
561
562         if (!objval) /* ConvertPythonToValue sets the error */
563                 return NULL;
564
565         if (!BGE_PROXY_PYOWNS(m_proxy)) {
566                 PyErr_SetString(PyExc_TypeError, "CList.append(i): this CValueList is used internally for the game engine and can't be modified");
567                 return NULL;
568         }
569
570         Add(objval);
571
572         Py_RETURN_NONE;
573 }
574
575 PyObject* CListValue::Pyreverse()
576 {
577         std::reverse(m_pValueArray.begin(),m_pValueArray.end());
578         Py_RETURN_NONE;
579 }
580
581 PyObject* CListValue::Pyindex(PyObject *value)
582 {
583         PyObject* result = NULL;
584
585         CValue* checkobj = ConvertPythonToValue(value, "val = cList[i]: CValueList, ");
586         if (checkobj==NULL)
587                 return NULL; /* ConvertPythonToValue sets the error */
588
589         int numelem = GetCount();
590         for (int i=0;i<numelem;i++)
591         {
592                 CValue* elem =                  GetValue(i);
593                 if (checkobj==elem || CheckEqual(checkobj,elem))
594                 {
595                         result = PyLong_FromSsize_t(i);
596                         break;
597                 }
598         }
599         checkobj->Release();
600
601         if (result==NULL) {
602                 PyErr_SetString(PyExc_ValueError, "CList.index(x): x not in CListValue");
603         }
604         return result;
605
606 }
607
608
609
610 PyObject* CListValue::Pycount(PyObject* value)
611 {
612         int numfound = 0;
613
614         CValue* checkobj = ConvertPythonToValue(value, ""); /* error ignored */
615
616         if (checkobj==NULL) { /* in this case just return that there are no items in the list */
617                 PyErr_Clear();
618                 return PyLong_FromSsize_t(0);
619         }
620
621         int numelem = GetCount();
622         for (int i=0;i<numelem;i++)
623         {
624                 CValue* elem =                  GetValue(i);
625                 if (checkobj==elem || CheckEqual(checkobj,elem))
626                 {
627                         numfound ++;
628                 }
629         }
630         checkobj->Release();
631
632         return PyLong_FromSsize_t(numfound);
633 }
634
635 /* Matches python dict.get(key, [default]) */
636 PyObject* CListValue::Pyget(PyObject *args)
637 {
638         char *key;
639         PyObject* def = Py_None;
640
641         if (!PyArg_ParseTuple(args, "s|O:get", &key, &def))
642                 return NULL;
643
644         CValue *item = FindValue((const char *)key);
645         if (item) {
646                 PyObject* pyobj = item->ConvertValueToPython();
647                 if (pyobj)
648                         return pyobj;
649                 else
650                         return item->GetProxy();
651         }
652         Py_INCREF(def);
653         return def;
654 }
655
656
657 PyObject* CListValue::Pyfrom_id(PyObject* value)
658 {
659         uintptr_t id= (uintptr_t)PyLong_AsVoidPtr(value);
660
661         if (PyErr_Occurred())
662                 return NULL;
663
664         int numelem = GetCount();
665         for (int i=0;i<numelem;i++)
666         {
667                 if (reinterpret_cast<uintptr_t>(m_pValueArray[i]->m_proxy) == id)
668                         return GetValue(i)->GetProxy();
669         }
670         PyErr_SetString(PyExc_IndexError, "from_id(#): id not found in CValueList");
671         return NULL;
672
673 }
674
675 #endif // DISABLE_PYTHON