/** \file gameengine/Expressions/ListValue.cpp * \ingroup expressions */ // ListValue.cpp: implementation of the CListValue class. // ////////////////////////////////////////////////////////////////////// /* * Copyright (c) 1996-2000 Erwin Coumans * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. Erwin Coumans makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * */ #include #include "EXP_ListValue.h" #include "EXP_StringValue.h" #include "EXP_VoidValue.h" #include #include "EXP_BoolValue.h" #include "BLI_sys_types.h" /* for intptr_t support */ ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CListValue::CListValue() : CPropValue() { m_bReleaseContents=true; } CListValue::~CListValue() { if (m_bReleaseContents) { for (unsigned int i=0;iRelease(); } } } static STR_String gstrListRep=STR_String("List"); const STR_String & CListValue::GetText() { gstrListRep = "["; STR_String commastr = ""; for (int i=0;iGetText(); commastr = ","; } gstrListRep += "]"; return gstrListRep; } CValue* CListValue::GetReplica() { CListValue* replica = new CListValue(*this); replica->ProcessReplica(); replica->m_bReleaseContents=true; // for copy, complete array is copied for now... // copy all values int numelements = m_pValueArray.size(); unsigned int i=0; replica->m_pValueArray.resize(numelements); for (i=0;im_pValueArray[i] = m_pValueArray[i]->GetReplica(); return replica; }; void CListValue::SetValue(int i, CValue *val) { assertd(i < m_pValueArray.size()); m_pValueArray[i]=val; } void CListValue::Resize(int num) { m_pValueArray.resize(num); } void CListValue::Remove(int i) { assertd(iRelease(); m_pValueArray.clear();//.Clear(); } CValue* CListValue::FindValue(const STR_String &name) { for (int i=0; i < GetCount(); i++) if (GetValue(i)->GetName() == name) return GetValue(i); return NULL; } CValue* CListValue::FindValue(const char *name) { for (int i=0; i < GetCount(); i++) if (GetValue(i)->GetName() == name) return GetValue(i); return NULL; } bool CListValue::SearchValue(CValue *val) { for (int i=0;i=0;i--) if (val == GetValue(i)) { Remove(i); result=true; } return result; } void CListValue::MergeList(CListValue *otherlist) { int numelements = this->GetCount(); int numotherelements = otherlist->GetCount(); Resize(numelements+numotherelements); for (int i=0;iGetValue(i)->AddRef()); } } bool CListValue::CheckEqual(CValue* first,CValue* second) { bool result = false; CValue* eqval = ((CValue*)first)->Calc(VALUE_EQL_OPERATOR,(CValue*)second); if (eqval==NULL) return false; const STR_String& text = eqval->GetText(); if (&text==&CBoolValue::sTrueString) { result = true; } eqval->Release(); return result; } /* --------------------------------------------------------------------- * Some stuff taken from the header * --------------------------------------------------------------------- */ CValue* CListValue::Calc(VALUE_OPERATOR op,CValue *val) { //assert(false); // todo: implement me! static int error_printed = 0; if (error_printed==0) { fprintf(stderr, "CValueList::Calc not yet implemented\n"); error_printed = 1; } return NULL; } CValue* CListValue::CalcFinal(VALUE_DATA_TYPE dtype, VALUE_OPERATOR op, CValue* val) { //assert(false); // todo: implement me! static int error_printed = 0; if (error_printed==0) { fprintf(stderr, "CValueList::CalcFinal not yet implemented\n"); error_printed = 1; } return NULL; } void CListValue::Add(CValue* value) { m_pValueArray.push_back(value); } double CListValue::GetNumber() { return -1; } int CListValue::GetValueType() { return VALUE_LIST_TYPE; } void CListValue::SetModified(bool bModified) { CValue::SetModified(bModified); int numels = GetCount(); for (int i=0;iSetModified(bModified); } bool CListValue::IsModified() { bool bmod = CValue::IsModified(); //normal own flag int numels = GetCount(); for (int i=0;iIsModified(); return bmod; } #ifdef WITH_PYTHON /* --------------------------------------------------------------------- */ /* Python interface ---------------------------------------------------- */ /* --------------------------------------------------------------------- */ static Py_ssize_t listvalue_bufferlen(PyObject *self) { CListValue *list= static_cast(BGE_PROXY_REF(self)); if (list==NULL) return 0; return (Py_ssize_t)list->GetCount(); } static PyObject *listvalue_buffer_item(PyObject *self, Py_ssize_t index) { CListValue *list= static_cast(BGE_PROXY_REF(self)); CValue *cval; if (list==NULL) { PyErr_SetString(PyExc_SystemError, "val = CList[i], " BGE_PROXY_ERROR_MSG); return NULL; } int count = list->GetCount(); if (index < 0) index = count+index; if (index < 0 || index >= count) { PyErr_SetString(PyExc_IndexError, "CList[i]: Python ListIndex out of range in CValueList"); return NULL; } cval= list->GetValue(index); PyObject *pyobj = cval->ConvertValueToPython(); if (pyobj) return pyobj; else return cval->GetProxy(); } /* just slice it into a python list... */ static PyObject *listvalue_buffer_slice(CListValue *list, Py_ssize_t start, Py_ssize_t stop) { PyObject *newlist; Py_ssize_t i, j; /* caller needs to validate negative index */ #if 0 Py_ssize_t len = list->GetCount(); if (start > len) start = len; if (stop > len) stop = len; #endif newlist = PyList_New(stop - start); if (!newlist) return NULL; for (i = start, j = 0; i < stop; i++, j++) { PyObject *pyobj = list->GetValue(i)->ConvertValueToPython(); if (!pyobj) { pyobj = list->GetValue(i)->GetProxy(); } PyList_SET_ITEM(newlist, j, pyobj); } return newlist; } static PyObject *listvalue_mapping_subscript(PyObject *self, PyObject *key) { CListValue *list= static_cast(BGE_PROXY_REF(self)); if (list==NULL) { PyErr_SetString(PyExc_SystemError, "value = CList[i], " BGE_PROXY_ERROR_MSG); return NULL; } if (PyUnicode_Check(key)) { CValue *item = ((CListValue*) list)->FindValue(_PyUnicode_AsString(key)); if (item) { PyObject *pyobj = item->ConvertValueToPython(); if (pyobj) return pyobj; else return item->GetProxy(); } } else if (PyIndex_Check(key)) { Py_ssize_t index = PyLong_AsSsize_t(key); return listvalue_buffer_item(self, index); /* wont add a ref */ } else if (PySlice_Check(key)) { Py_ssize_t start, stop, step, slicelength; if (PySlice_GetIndicesEx(key, list->GetCount(), &start, &stop, &step, &slicelength) < 0) return NULL; if (slicelength <= 0) { return PyList_New(0); } else if (step == 1) { return listvalue_buffer_slice(list, start, stop); } else { PyErr_SetString(PyExc_TypeError, "CList[slice]: slice steps not supported"); return NULL; } } PyErr_Format(PyExc_KeyError, "CList[key]: '%R' key not in list", key); return NULL; } /* clist + list, return a list that python owns */ static PyObject *listvalue_buffer_concat(PyObject *self, PyObject *other) { CListValue *listval= static_cast(BGE_PROXY_REF(self)); Py_ssize_t i, numitems, numitems_orig; if (listval==NULL) { PyErr_SetString(PyExc_SystemError, "CList+other, " BGE_PROXY_ERROR_MSG); return NULL; } numitems_orig= listval->GetCount(); // for now, we support CListValue concatenated with items // and CListValue concatenated to Python Lists // and CListValue concatenated with another CListValue /* Shallow copy, don't use listval->GetReplica(), it will screw up with KX_GameObjects */ CListValue* listval_new = new CListValue(); if (PyList_Check(other)) { CValue* listitemval; bool error = false; numitems = PyList_GET_SIZE(other); /* copy the first part of the list */ listval_new->Resize(numitems_orig + numitems); for (i=0;iSetValue(i, listval->GetValue(i)->AddRef()); for (i=0;iConvertPythonToValue(PyList_GET_ITEM(other, i), true, "cList + pyList: CListValue, "); if (listitemval) { listval_new->SetValue(i+numitems_orig, listitemval); } else { error= true; break; } } if (error) { listval_new->Resize(numitems_orig+i); /* resize so we don't try release NULL pointers */ listval_new->Release(); return NULL; /* ConvertPythonToValue above sets the error */ } } else if (PyObject_TypeCheck(other, &CListValue::Type)) { // add items from otherlist to this list CListValue* otherval = static_cast(BGE_PROXY_REF(other)); if (otherval==NULL) { listval_new->Release(); PyErr_SetString(PyExc_SystemError, "CList+other, " BGE_PROXY_ERROR_MSG); return NULL; } numitems = otherval->GetCount(); /* copy the first part of the list */ listval_new->Resize(numitems_orig + numitems); /* resize so we don't try release NULL pointers */ for (i=0;iSetValue(i, listval->GetValue(i)->AddRef()); /* now copy the other part of the list */ for (i=0;iSetValue(i+numitems_orig, otherval->GetValue(i)->AddRef()); } return listval_new->NewProxy(true); /* python owns this list */ } static int listvalue_buffer_contains(PyObject *self_v, PyObject *value) { CListValue *self = static_cast(BGE_PROXY_REF(self_v)); if (self == NULL) { PyErr_SetString(PyExc_SystemError, "val in CList, " BGE_PROXY_ERROR_MSG); return -1; } if (PyUnicode_Check(value)) { if (self->FindValue((const char *)_PyUnicode_AsString(value))) { return 1; } } else if (PyObject_TypeCheck(value, &CValue::Type)) { /* not dict like at all but this worked before __contains__ was used */ CValue *item= static_cast(BGE_PROXY_REF(value)); for (int i=0; i < self->GetCount(); i++) if (self->GetValue(i) == item) // Com return 1; } // not using CheckEqual return 0; } static PySequenceMethods listvalue_as_sequence = { listvalue_bufferlen,//(inquiry)buffer_length, /*sq_length*/ listvalue_buffer_concat, /*sq_concat*/ NULL, /*sq_repeat*/ listvalue_buffer_item, /*sq_item*/ // TODO, slicing in py3 NULL, // listvalue_buffer_slice, /*sq_slice*/ NULL, /*sq_ass_item*/ NULL, /*sq_ass_slice*/ (objobjproc)listvalue_buffer_contains, /* sq_contains */ (binaryfunc) NULL, /* sq_inplace_concat */ (ssizeargfunc) NULL, /* sq_inplace_repeat */ }; /* Is this one used ? */ static PyMappingMethods instance_as_mapping = { listvalue_bufferlen, /*mp_length*/ listvalue_mapping_subscript, /*mp_subscript*/ NULL /*mp_ass_subscript*/ }; PyTypeObject CListValue::Type = { PyVarObject_HEAD_INIT(NULL, 0) "CListValue", /*tp_name*/ sizeof(PyObjectPlus_Proxy), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ py_base_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ py_base_repr, /*tp_repr*/ 0, /*tp_as_number*/ &listvalue_as_sequence, /*tp_as_sequence*/ &instance_as_mapping, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call */ 0, NULL, NULL, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, 0,0,0,0,0,0,0, Methods, 0, 0, &CValue::Type, 0,0,0,0,0,0, py_base_new }; PyMethodDef CListValue::Methods[] = { /* List style access */ {"append", (PyCFunction)CListValue::sPyappend,METH_O}, {"reverse", (PyCFunction)CListValue::sPyreverse,METH_NOARGS}, {"index", (PyCFunction)CListValue::sPyindex,METH_O}, {"count", (PyCFunction)CListValue::sPycount,METH_O}, /* Dict style access */ {"get", (PyCFunction)CListValue::sPyget,METH_VARARGS}, /* Own cvalue funcs */ {"from_id", (PyCFunction)CListValue::sPyfrom_id,METH_O}, {NULL,NULL} //Sentinel }; PyAttributeDef CListValue::Attributes[] = { { NULL } //Sentinel }; PyObject *CListValue::Pyappend(PyObject *value) { CValue *objval = ConvertPythonToValue(value, true, "CList.append(i): CValueList, "); if (!objval) /* ConvertPythonToValue sets the error */ return NULL; if (!BGE_PROXY_PYOWNS(m_proxy)) { PyErr_SetString(PyExc_TypeError, "CList.append(i): internal values can't be modified"); return NULL; } Add(objval); Py_RETURN_NONE; } PyObject *CListValue::Pyreverse() { if (!BGE_PROXY_PYOWNS(m_proxy)) { PyErr_SetString(PyExc_TypeError, "CList.reverse(): internal values can't be modified"); return NULL; } std::reverse(m_pValueArray.begin(),m_pValueArray.end()); Py_RETURN_NONE; } PyObject *CListValue::Pyindex(PyObject *value) { PyObject *result = NULL; CValue *checkobj = ConvertPythonToValue(value, true, "val = cList[i]: CValueList, "); if (checkobj==NULL) return NULL; /* ConvertPythonToValue sets the error */ int numelem = GetCount(); for (int i=0;iRelease(); if (result==NULL) { PyErr_SetString(PyExc_ValueError, "CList.index(x): x not in CListValue"); } return result; } PyObject *CListValue::Pycount(PyObject *value) { int numfound = 0; CValue *checkobj = ConvertPythonToValue(value, false, ""); /* error ignored */ if (checkobj==NULL) { /* in this case just return that there are no items in the list */ PyErr_Clear(); return PyLong_FromLong(0); } int numelem = GetCount(); for (int i=0;iRelease(); return PyLong_FromLong(numfound); } /* Matches python dict.get(key, [default]) */ PyObject *CListValue::Pyget(PyObject *args) { char *key; PyObject *def = Py_None; if (!PyArg_ParseTuple(args, "s|O:get", &key, &def)) return NULL; CValue *item = FindValue((const char *)key); if (item) { PyObject *pyobj = item->ConvertValueToPython(); if (pyobj) return pyobj; else return item->GetProxy(); } Py_INCREF(def); return def; } PyObject *CListValue::Pyfrom_id(PyObject *value) { uintptr_t id= (uintptr_t)PyLong_AsVoidPtr(value); if (PyErr_Occurred()) return NULL; int numelem = GetCount(); for (int i=0;i(m_pValueArray[i]->m_proxy) == id) return GetValue(i)->GetProxy(); } PyErr_SetString(PyExc_IndexError, "from_id(#): id not found in CValueList"); return NULL; } #endif // WITH_PYTHON