1 /** \file gameengine/Expressions/ListValue.cpp
4 // ListValue.cpp: implementation of the CListValue class.
6 //////////////////////////////////////////////////////////////////////
8 * Copyright (c) 1996-2000 Erwin Coumans <coockie@acm.org>
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.
22 #include "ListValue.h"
23 #include "StringValue.h"
24 #include "VoidValue.h"
26 #include "BoolValue.h"
28 #include "BLO_sys_types.h" /* for intptr_t support */
31 //////////////////////////////////////////////////////////////////////
32 // Construction/Destruction
33 //////////////////////////////////////////////////////////////////////
35 CListValue::CListValue()
38 m_bReleaseContents=true;
43 CListValue::~CListValue()
46 if (m_bReleaseContents) {
47 for (unsigned int i=0;i<m_pValueArray.size();i++) {
48 m_pValueArray[i]->Release();
54 static STR_String gstrListRep=STR_String("List");
56 const STR_String & CListValue::GetText()
59 STR_String commastr = "";
61 for (int i=0;i<GetCount();i++)
63 gstrListRep += commastr;
64 gstrListRep += GetValue(i)->GetText();
74 CValue* CListValue::GetReplica() {
75 CListValue* replica = new CListValue(*this);
77 replica->ProcessReplica();
79 replica->m_bReleaseContents=true; // for copy, complete array is copied for now...
81 int numelements = m_pValueArray.size();
83 replica->m_pValueArray.resize(numelements);
84 for (i=0;i<m_pValueArray.size();i++)
85 replica->m_pValueArray[i] = m_pValueArray[i]->GetReplica();
93 void CListValue::SetValue(int i, CValue *val)
95 assertd(i < m_pValueArray.size());
101 void CListValue::Resize(int num)
103 m_pValueArray.resize(num);
108 void CListValue::Remove(int i)
110 assertd(i<m_pValueArray.size());
111 m_pValueArray.erase(m_pValueArray.begin()+i);
116 void CListValue::ReleaseAndRemoveAll()
118 for (unsigned int i=0;i<m_pValueArray.size();i++)
119 m_pValueArray[i]->Release();
120 m_pValueArray.clear();//.Clear();
125 CValue* CListValue::FindValue(const STR_String & name)
127 for (int i=0; i < GetCount(); i++)
128 if (GetValue(i)->GetName() == name)
134 CValue* CListValue::FindValue(const char * name)
136 for (int i=0; i < GetCount(); i++)
137 if (GetValue(i)->GetName() == name)
143 bool CListValue::SearchValue(CValue *val)
145 for (int i=0;i<GetCount();i++)
146 if (val == GetValue(i))
153 void CListValue::SetReleaseOnDestruct(bool bReleaseContents)
155 m_bReleaseContents = bReleaseContents;
160 bool CListValue::RemoveValue(CValue *val)
164 for (int i=GetCount()-1;i>=0;i--)
165 if (val == GetValue(i))
175 void CListValue::MergeList(CListValue *otherlist)
178 int numelements = this->GetCount();
179 int numotherelements = otherlist->GetCount();
182 Resize(numelements+numotherelements);
184 for (int i=0;i<numotherelements;i++)
186 SetValue(i+numelements,otherlist->GetValue(i)->AddRef());
190 bool CListValue::CheckEqual(CValue* first,CValue* second)
194 CValue* eqval = ((CValue*)first)->Calc(VALUE_EQL_OPERATOR,(CValue*)second);
198 const STR_String& text = eqval->GetText();
199 if (&text==&CBoolValue::sTrueString)
209 /* ---------------------------------------------------------------------
210 * Some stuff taken from the header
211 * --------------------------------------------------------------------- */
212 CValue* CListValue::Calc(VALUE_OPERATOR op,CValue *val)
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");
223 CValue* CListValue::CalcFinal(VALUE_DATA_TYPE dtype,
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");
238 void CListValue::Add(CValue* value)
240 m_pValueArray.push_back(value);
245 double CListValue::GetNumber()
252 void CListValue::SetModified(bool bModified)
254 CValue::SetModified(bModified);
255 int numels = GetCount();
257 for (int i=0;i<numels;i++)
258 GetValue(i)->SetModified(bModified);
263 bool CListValue::IsModified()
265 bool bmod = CValue::IsModified(); //normal own flag
266 int numels = GetCount();
268 for (int i=0;i<numels;i++)
269 bmod = bmod || GetValue(i)->IsModified();
276 /* --------------------------------------------------------------------- */
277 /* Python interface ---------------------------------------------------- */
278 /* --------------------------------------------------------------------- */
280 Py_ssize_t listvalue_bufferlen(PyObject* self)
282 CListValue *list= static_cast<CListValue *>(BGE_PROXY_REF(self));
286 return (Py_ssize_t)list->GetCount();
289 PyObject* listvalue_buffer_item(PyObject* self, Py_ssize_t index)
291 CListValue *list= static_cast<CListValue *>(BGE_PROXY_REF(self));
295 PyErr_SetString(PyExc_SystemError, "val = CList[i], "BGE_PROXY_ERROR_MSG);
299 int count = list->GetCount();
304 if (index < 0 || index >= count) {
305 PyErr_SetString(PyExc_IndexError, "CList[i]: Python ListIndex out of range in CValueList");
309 cval= list->GetValue(index);
311 PyObject* pyobj = cval->ConvertValueToPython();
315 return cval->GetProxy();
318 PyObject* listvalue_mapping_subscript(PyObject* self, PyObject* pyindex)
320 CListValue *list= static_cast<CListValue *>(BGE_PROXY_REF(self));
322 PyErr_SetString(PyExc_SystemError, "value = CList[i], "BGE_PROXY_ERROR_MSG);
326 if (PyUnicode_Check(pyindex))
328 CValue *item = ((CListValue*) list)->FindValue(_PyUnicode_AsString(pyindex));
330 PyObject* pyobj = item->ConvertValueToPython();
334 return item->GetProxy();
337 else if (PyLong_Check(pyindex))
339 int index = PyLong_AsSsize_t(pyindex);
340 return listvalue_buffer_item(self, index); /* wont add a ref */
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);
350 /* just slice it into a python list... */
351 PyObject* listvalue_buffer_slice(PyObject* self,Py_ssize_t ilow, Py_ssize_t ihigh)
353 CListValue *list= static_cast<CListValue *>(BGE_PROXY_REF(self));
355 PyErr_SetString(PyExc_SystemError, "val = CList[i:j], "BGE_PROXY_ERROR_MSG);
362 if (ilow < 0) ilow = 0;
364 int n = ((CListValue*) list)->GetCount();
371 newlist = PyList_New(ihigh - ilow);
375 for (i = ilow, j = 0; i < ihigh; i++, j++)
377 PyObject* pyobj = list->GetValue(i)->ConvertValueToPython();
379 pyobj = list->GetValue(i)->GetProxy();
380 PyList_SET_ITEM(newlist, i, pyobj);
386 /* clist + list, return a list that python owns */
387 static PyObject *listvalue_buffer_concat(PyObject * self, PyObject * other)
389 CListValue *listval= static_cast<CListValue *>(BGE_PROXY_REF(self));
390 Py_ssize_t i, numitems, numitems_orig;
393 PyErr_SetString(PyExc_SystemError, "CList+other, "BGE_PROXY_ERROR_MSG);
397 numitems_orig= listval->GetCount();
399 // for now, we support CListValue concatenated with items
400 // and CListValue concatenated to Python Lists
401 // and CListValue concatenated with another CListValue
403 /* Shallow copy, dont use listval->GetReplica(), it will screw up with KX_GameObjects */
404 CListValue* listval_new = new CListValue();
406 if (PyList_Check(other))
411 numitems = PyList_GET_SIZE(other);
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());
418 for (i=0;i<numitems;i++)
420 listitemval = listval->ConvertPythonToValue(PyList_GetItem(other,i), "cList + pyList: CListValue, ");
423 listval_new->SetValue(i+numitems_orig, listitemval);
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 */
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));
441 listval_new->Release();
442 PyErr_SetString(PyExc_SystemError, "CList+other, "BGE_PROXY_ERROR_MSG);
446 numitems = otherval->GetCount();
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());
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());
458 return listval_new->NewProxy(true); /* python owns this list */
461 static int listvalue_buffer_contains(PyObject *self_v, PyObject *value)
463 CListValue *self= static_cast<CListValue *>(BGE_PROXY_REF(self_v));
466 PyErr_SetString(PyExc_SystemError, "val in CList, "BGE_PROXY_ERROR_MSG);
470 if (PyUnicode_Check(value)) {
471 if (self->FindValue((const char *)_PyUnicode_AsString(value))) {
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
481 } // not using CheckEqual
487 static PySequenceMethods listvalue_as_sequence = {
488 listvalue_bufferlen,//(inquiry)buffer_length, /*sq_length*/
489 listvalue_buffer_concat, /*sq_concat*/
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 */
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*/
512 PyTypeObject CListValue::Type = {
513 PyVarObject_HEAD_INIT(NULL, 0)
514 "CListValue", /*tp_name*/
515 sizeof(PyObjectPlus_Proxy), /*tp_basicsize*/
518 py_base_dealloc, /*tp_dealloc*/
523 py_base_repr, /*tp_repr*/
525 &listvalue_as_sequence, /*tp_as_sequence*/
526 &instance_as_mapping, /*tp_as_mapping*/
533 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
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},
550 /* Dict style access */
551 {"get", (PyCFunction)CListValue::sPyget,METH_VARARGS},
553 /* Own cvalue funcs */
554 {"from_id", (PyCFunction)CListValue::sPyfrom_id,METH_O},
556 {NULL,NULL} //Sentinel
559 PyAttributeDef CListValue::Attributes[] = {
563 PyObject* CListValue::Pyappend(PyObject* value)
565 CValue* objval = ConvertPythonToValue(value, "CList.append(i): CValueList, ");
567 if (!objval) /* ConvertPythonToValue sets the error */
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");
580 PyObject* CListValue::Pyreverse()
582 std::reverse(m_pValueArray.begin(),m_pValueArray.end());
586 PyObject* CListValue::Pyindex(PyObject *value)
588 PyObject* result = NULL;
590 CValue* checkobj = ConvertPythonToValue(value, "val = cList[i]: CValueList, ");
592 return NULL; /* ConvertPythonToValue sets the error */
594 int numelem = GetCount();
595 for (int i=0;i<numelem;i++)
597 CValue* elem = GetValue(i);
598 if (checkobj==elem || CheckEqual(checkobj,elem))
600 result = PyLong_FromSsize_t(i);
607 PyErr_SetString(PyExc_ValueError, "CList.index(x): x not in CListValue");
615 PyObject* CListValue::Pycount(PyObject* value)
619 CValue* checkobj = ConvertPythonToValue(value, ""); /* error ignored */
621 if (checkobj==NULL) { /* in this case just return that there are no items in the list */
623 return PyLong_FromSsize_t(0);
626 int numelem = GetCount();
627 for (int i=0;i<numelem;i++)
629 CValue* elem = GetValue(i);
630 if (checkobj==elem || CheckEqual(checkobj,elem))
637 return PyLong_FromSsize_t(numfound);
640 /* Matches python dict.get(key, [default]) */
641 PyObject* CListValue::Pyget(PyObject *args)
644 PyObject* def = Py_None;
646 if (!PyArg_ParseTuple(args, "s|O:get", &key, &def))
649 CValue *item = FindValue((const char *)key);
651 PyObject* pyobj = item->ConvertValueToPython();
655 return item->GetProxy();
662 PyObject* CListValue::Pyfrom_id(PyObject* value)
664 uintptr_t id= (uintptr_t)PyLong_AsVoidPtr(value);
666 if (PyErr_Occurred())
669 int numelem = GetCount();
670 for (int i=0;i<numelem;i++)
672 if (reinterpret_cast<uintptr_t>(m_pValueArray[i]->m_proxy) == id)
673 return GetValue(i)->GetProxy();
675 PyErr_SetString(PyExc_IndexError, "from_id(#): id not found in CValueList");
680 #endif // WITH_PYTHON