/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "document.hpp" #include "sheet.hpp" #include "global.hpp" #include "named_expression.hpp" #include "named_expressions.hpp" #include #include #include #include #include using namespace std; namespace ss = orcus::spreadsheet; namespace orcus { namespace python { document_data::~document_data() { } namespace { /** * Python object for orcus.Document. */ struct pyobj_document { PyObject_HEAD PyObject* sheets; // tuple of sheet objects. document_data* data; }; inline pyobj_document* t(PyObject* self) { return reinterpret_cast(self); } void tp_dealloc(pyobj_document* self) { delete self->data; // Destroy all sheet objects. Py_ssize_t n = PyTuple_Size(self->sheets); for (Py_ssize_t i = 0; i < n; ++i) { PyObject* o = PyTuple_GetItem(self->sheets, i); Py_CLEAR(o); } Py_CLEAR(self->sheets); // and the tuple containing the sheets. Py_TYPE(self)->tp_free(reinterpret_cast(self)); } PyObject* tp_new(PyTypeObject* type, PyObject* /*args*/, PyObject* /*kwargs*/) { pyobj_document* self = t(type->tp_alloc(type, 0)); self->data = new document_data; return reinterpret_cast(self); } int tp_init(pyobj_document* /*self*/, PyObject* /*args*/, PyObject* /*kwargs*/) { return 0; } PyObject* doc_get_named_expressions(PyObject* self, PyObject* /*args*/, PyObject* /*kwargs*/) { const ss::document& doc = *t(self)->data->m_doc; const ixion::model_context& cxt = doc.get_model_context(); return create_named_expressions_object(-1, doc, cxt.get_named_expressions_iterator()); } PyMethodDef tp_methods[] = { { "get_named_expressions", (PyCFunction)doc_get_named_expressions, METH_NOARGS, "Get a named expressions iterator." }, { nullptr } }; PyMemberDef tp_members[] = { { (char*)"sheets", T_OBJECT_EX, offsetof(pyobj_document, sheets), READONLY, (char*)"sheet objects" }, { nullptr } }; PyTypeObject document_type = { PyVarObject_HEAD_INIT(nullptr, 0) "orcus.Document", // tp_name sizeof(pyobj_document), // tp_basicsize 0, // tp_itemsize (destructor)tp_dealloc, // tp_dealloc 0, // tp_print 0, // tp_getattr 0, // tp_setattr 0, // tp_compare 0, // tp_repr 0, // tp_as_number 0, // tp_as_sequence 0, // tp_as_mapping 0, // tp_hash 0, // tp_call 0, // tp_str 0, // tp_getattro 0, // tp_setattro 0, // tp_as_buffer Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags "orcus document object", // tp_doc 0, // tp_traverse 0, // tp_clear 0, // tp_richcompare 0, // tp_weaklistoffset 0, // tp_iter 0, // tp_iternext tp_methods, // tp_methods tp_members, // tp_members 0, // tp_getset 0, // tp_base 0, // tp_dict 0, // tp_descr_get 0, // tp_descr_set 0, // tp_dictoffset (initproc)tp_init, // tp_init 0, // tp_alloc tp_new, // tp_new }; bool import_from_stream_object(iface::import_filter& app, PyObject* obj_bytes) { const char* p = PyBytes_AsString(obj_bytes); if (!p) return false; size_t n = PyBytes_Size(obj_bytes); app.read_stream({p, n}); return true; } PyObject* create_document_object() { PyTypeObject* type = get_document_type(); PyObject* obj_doc = create_object_from_type(type); if (!obj_doc) return nullptr; type->tp_init(obj_doc, nullptr, nullptr); return obj_doc; } void store_document(PyObject* self, std::unique_ptr&& doc) { if (!self) return; pyobj_document* pydoc = reinterpret_cast(self); document_data* pydoc_data = pydoc->data; pydoc_data->m_doc = std::move(doc); PyTypeObject* sheet_type = get_sheet_type(); if (!sheet_type) return; // Create a tuple of sheet objects and store it with the pydoc instance. size_t sheet_size = pydoc_data->m_doc->get_sheet_count(); pydoc->sheets = PyTuple_New(sheet_size); for (size_t i = 0; i < sheet_size; ++i) { spreadsheet::sheet* sheet = pydoc_data->m_doc->get_sheet(i); if (!sheet) continue; PyObject* pysheet = sheet_type->tp_new(sheet_type, nullptr, nullptr); if (!pysheet) continue; sheet_type->tp_init(pysheet, nullptr, nullptr); Py_INCREF(pysheet); PyTuple_SetItem(pydoc->sheets, i, pysheet); store_sheet(pysheet, pydoc_data->m_doc.get(), sheet); } } } // anonoymous namespace PyTypeObject* get_document_type() { return &document_type; } document_data* get_document_data(PyObject* self) { return reinterpret_cast(self)->data; } stream_with_formulas read_stream_and_formula_params_from_args(PyObject* args, PyObject* kwargs) { static const char* kwlist[] = { "stream", "recalc", "error_policy", nullptr }; stream_with_formulas ret; PyObject* file = nullptr; int recalc_formula_cells = 0; const char* error_policy_s = nullptr; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|ps", const_cast(kwlist), &file, &recalc_formula_cells, &error_policy_s)) return ret; if (!file) { PyErr_SetString(PyExc_RuntimeError, "Invalid file object has been passed."); return ret; } PyObject* obj_bytes = nullptr; if (PyObject_HasAttrString(file, "read")) { PyObject* func_read = PyObject_GetAttrString(file, "read"); // new reference obj_bytes = PyObject_CallFunction(func_read, nullptr); Py_XDECREF(func_read); } if (!obj_bytes) { if (PyObject_TypeCheck(file, &PyBytes_Type)) obj_bytes = PyBytes_FromObject(file); } if (!obj_bytes) { PyErr_SetString(PyExc_RuntimeError, "failed to extract bytes from this object."); return ret; } if (error_policy_s) { ss::formula_error_policy_t error_policy = ss::to_formula_error_policy(error_policy_s); if (error_policy == ss::formula_error_policy_t::unknown) { std::ostringstream os; os << "invalid error policy value: '" << error_policy_s << "'. The value must be either 'fail' or 'skip'."; PyErr_SetString(PyExc_RuntimeError, os.str().data()); return ret; } ret.error_policy = error_policy; } ret.stream.reset(obj_bytes); ret.recalc_formula_cells = recalc_formula_cells != 0; return ret; } py_unique_ptr read_stream_from_args(PyObject* args, PyObject* kwargs) { static const char* kwlist[] = { "stream", nullptr }; py_unique_ptr obj_bytes; PyObject* file = nullptr; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", const_cast(kwlist), &file)) return obj_bytes; if (!file) { PyErr_SetString(PyExc_RuntimeError, "Invalid file object has been passed."); return obj_bytes; } if (PyObject_HasAttrString(file, "read")) { PyObject* func_read = PyObject_GetAttrString(file, "read"); // new reference obj_bytes.reset(PyObject_CallFunction(func_read, nullptr)); Py_XDECREF(func_read); } if (!obj_bytes) { if (PyObject_TypeCheck(file, &PyBytes_Type)) obj_bytes.reset(PyBytes_FromObject(file)); } if (!obj_bytes) { PyErr_SetString(PyExc_RuntimeError, "failed to extract bytes from this object."); return obj_bytes; } return obj_bytes; } PyObject* import_from_stream_into_document( PyObject* obj_bytes, iface::import_filter& app, std::unique_ptr&& doc) { if (!import_from_stream_object(app, obj_bytes)) return nullptr; return create_document(std::move(doc)); } PyObject* create_document(std::unique_ptr&& doc) { PyObject* obj_doc = create_document_object(); if (!obj_doc) return nullptr; store_document(obj_doc, std::move(doc)); return obj_doc; } }} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */