summaryrefslogtreecommitdiffstats
path: root/src/python/document.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/python/document.cpp')
-rw-r--r--src/python/document.cpp330
1 files changed, 330 insertions, 0 deletions
diff --git a/src/python/document.cpp b/src/python/document.cpp
new file mode 100644
index 0000000..d908668
--- /dev/null
+++ b/src/python/document.cpp
@@ -0,0 +1,330 @@
+/* -*- 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 <ixion/model_context.hpp>
+#include <ixion/named_expressions_iterator.hpp>
+#include <structmember.h>
+#include <object.h>
+#include <sstream>
+
+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<pyobj_document*>(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<PyObject*>(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<PyObject*>(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<spreadsheet::document>&& doc)
+{
+ if (!self)
+ return;
+
+ pyobj_document* pydoc = reinterpret_cast<pyobj_document*>(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<pyobj_document*>(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<char**>(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<char**>(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<spreadsheet::document>&& doc)
+{
+ if (!import_from_stream_object(app, obj_bytes))
+ return nullptr;
+
+ return create_document(std::move(doc));
+}
+
+PyObject* create_document(std::unique_ptr<spreadsheet::document>&& 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: */