/******************************************************************************** * * * This file is part of IfcOpenShell. * * * * IfcOpenShell is free software: you can redistribute it and/or modify * * it under the terms of the Lesser GNU General Public License as published by * * the Free Software Foundation, either version 3.0 of the License, or * * (at your option) any later version. * * * * IfcOpenShell is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * Lesser GNU General Public License for more details. * * * * You should have received a copy of the Lesser GNU General Public License * * along with this program. If not, see . * * * ********************************************************************************/ %ignore IfcParse::IfcFile::register_inverse; %ignore IfcParse::IfcFile::unregister_inverse; %ignore IfcParse::IfcFile::schema; %ignore IfcParse::IfcFile::begin; %ignore IfcParse::IfcFile::end; %ignore IfcParse::IfcFile::types_begin; %ignore IfcParse::IfcFile::types_end; %ignore IfcParse::IfcFile::internal_guid_map; %ignore IfcParse::IfcFile::storage_; %ignore IfcParse::IfcFile::byguid_; %ignore IfcParse::IfcFile::byid_; %ignore IfcParse::IfcFile::byref_excl_; %ignore IfcParse::IfcFile::types_to_bypass_loading_; %ignore IfcParse::InstanceStreamer::InstanceStreamer(const IfcParse::schema_definition* schema, IfcParse::IfcSpfLexer* lexer); %ignore IfcParse::InstanceStreamer::readInstance; %ignore IfcParse::InstanceStreamer::stealInstances; %ignore express::Entity; %ignore express::Select; %ignore express::DeclaredType; %ignore in_memory_file_storage; %ignore rocks_db_file_storage; // Available as get_inverse(). %ignore IfcParse::IfcFile::instances_by_reference; %ignore IfcParse::parse_context; %ignore operator<<; %ignore IfcParse::FileDescription::FileDescription; %ignore IfcParse::FileName::FileName; %ignore IfcParse::FileSchema::FileSchema; %ignore IfcParse::IfcFile::tokens; %ignore IfcParse::IfcSpfHeader::IfcSpfHeader(IfcSpfLexer*); %ignore IfcParse::IfcSpfHeader::lexer; %ignore IfcParse::IfcSpfHeader::stream; %ignore IfcParse::IfcSpfHeader::file_description; %ignore IfcParse::IfcSpfHeader::file_name; %ignore IfcParse::IfcSpfHeader::file_schema; %ignore IfcParse::HeaderEntity::is; %ignore IfcParse::IfcFile::type_iterator; %ignore express::Base::is; %rename("by_id") instance_by_id; %rename("by_guid") instance_by_guid; %rename("_by_type") instances_by_type; %rename("_by_type_excl_subtypes") instances_by_type_excl_subtypes; %rename("get_inverses_by_declaration") getInverse; %rename("get_total_inverses_by_id") getTotalInverses; %rename("entity_instance") express::Base; %rename("file") IfcFile; // _add() because mixin defined add which adds transaction logic %rename("_add") addEntity; %rename("remove") removeEntity; %rename("_traverse") traverse; %rename("_traverse_breadth_first") traverse_breadth_first; class attribute_value_derived {}; %{ class attribute_value_derived {}; %} %extend attribute_value_derived { %pythoncode %{ def __bool__(self): return False def __repr__(self): return '*' %} } %inline %{ static bool feature_use_attribute_value_derived = false; void set_feature(const std::string& x, PyObject* v) { if (PyBool_Check(v) && x == "use_attribute_value_derived") { feature_use_attribute_value_derived = v == Py_True; } else { throw std::runtime_error("Invalid feature specification"); } } PyObject* get_feature(const std::string& x) { if (x == "use_attribute_value_derived") { return PyBool_FromLong(feature_use_attribute_value_derived); } else { throw std::runtime_error("Invalid feature specification"); } } %} %{ static const std::string& helper_fn_declaration_get_name(const IfcParse::declaration* decl) { return decl->name(); } static IfcUtil::ArgumentType helper_fn_attribute_type(const express::Base* instp, unsigned i) { const auto& inst = *instp; const IfcParse::parameter_type* pt = 0; if (inst.declaration().as_entity()) { pt = inst.declaration().as_entity()->attribute_by_index(i)->type_of_attribute(); if (inst.declaration().as_entity()->derived()[i]) { return IfcUtil::Argument_DERIVED; } } else if (inst.declaration().as_type_declaration() && i == 0) { pt = inst.declaration().as_type_declaration()->declared_type(); } else if (inst.declaration().as_enumeration_type() && i == 0) { // Enumeration is always from string in Python return IfcUtil::Argument_STRING; } if (pt == 0) { return IfcUtil::Argument_UNKNOWN; } else { return IfcUtil::from_parameter_type(pt); } } %} %inline %{ #include #include #ifdef IFOPSH_WITH_ROCKSDB #include #include class RocksDBPrefixIterator { public: RocksDBPrefixIterator(const IfcParse::impl::rocks_db_file_storage* storage, const std::string& prefix) : it_(storage->db->NewIterator(storage->ropts)), prefix_(prefix) { it_->Seek(prefix_); } bool valid() const { if (!it_ || !it_->Valid()) return false; const rocksdb::Slice k = it_->key(); const rocksdb::Slice p(prefix_); return k.starts_with(p); } void next() { if (it_) it_->Next(); } PyObject* key() const { if (!valid()) { Py_RETURN_NONE; } const rocksdb::Slice k = it_->key(); return PyBytes_FromStringAndSize(k.data(), static_cast(k.size())); } PyObject* value() const { if (!valid()) { Py_RETURN_NONE; } const rocksdb::Slice v = it_->value(); return PyBytes_FromStringAndSize(v.data(), static_cast(v.size())); } private: std::unique_ptr it_; std::string prefix_; }; #endif %} %newobject IfcParse::IfcFile::key_value_store_iter; %extend IfcParse::IfcFile { /* // Use to correlate to entity_instance.file_pointer, so that we // can trace file ownership of instances on the python side. size_t file_pointer() const { return reinterpret_cast($self); } */ IfcFile(const std::string& schema = "IFC4") { auto resolved_schema = schema; if (resolved_schema == "IFC4X3") { resolved_schema = "IFC4X3_ADD2"; } return new IfcParse::IfcFile(IfcParse::schema_by_name(resolved_schema)); } IfcFile(const std::vector& schema_version) { static const char* prefixes[] = { "IFC", "X", "_ADD", "_TC" }; std::string resolved_schema; for (size_t i = 0; i < schema_version.size() && i < 4; ++i) { if (schema_version[i] != 0) { resolved_schema += prefixes[i]; resolved_schema += std::to_string(schema_version[i]); } } return new IfcParse::IfcFile(IfcParse::schema_by_name(resolved_schema)); } std::vector get_inverse(const express::Base& e) { if (auto e_ = e.as()) { return cast_vector($self->getInverse(e_.id(), 0, -1)); } throw IfcParse::IfcException("Only entities with ids are supported for get_inverse. Provided entity: '" + e.declaration().name() + "'."); } std::vector get_inverse_indices(const express::Base& e) { if (auto e_ = e.as()) { return $self->get_inverse_indices(e_.id()); } throw IfcParse::IfcException("Only entities with ids are supported for get_inverse_indices. Provided entity: '" + e.declaration().name() + "'."); } int get_total_inverses(const express::Base& e) { if (auto e_ = e.as()) { return $self->getTotalInverses(e_.id()); } throw IfcParse::IfcException("Only entities with ids are supported for get_total_inverses. Provided entity: '" + e.declaration().name() + "'."); } void write(const std::string& fn) { std::ofstream f(IfcUtil::path::from_utf8(fn).c_str()); if (!f.good()) { throw std::runtime_error("Failed to write to path: '" + fn + "', check folder and file permissions."); } f << (*$self); } std::string to_string() { std::stringstream s; s << (*$self); return s.str(); } express::Base create(const std::string& entity_name) { const IfcParse::declaration* decl = $self->schema()->declaration_by_name(entity_name); if (!decl || !(decl->as_entity() || decl->as_type_declaration())) { throw IfcParse::IfcException("No such entity or type declaration: '" + entity_name + "' in schema '" + $self->schema()->name()); } return $self->create(decl); } std::vector entity_names() const { std::vector keys; keys.reserve(std::distance($self->begin(), $self->end())); for (auto it = $self->begin(); it != $self->end(); ++ it) { keys.push_back(it->first); } return keys; } std::vector types() const { const size_t n = std::distance($self->types_begin(), $self->types_end()); std::vector ts; ts.reserve(n); std::transform($self->types_begin(), $self->types_end(), std::back_inserter(ts), helper_fn_declaration_get_name); return ts; } /* std::vector types_with_super() const { const size_t n = std::distance($self->types_incl_super_begin(), $self->types_incl_super_end()); std::vector ts; ts.reserve(n); std::transform($self->types_incl_super_begin(), $self->types_incl_super_end(), std::back_inserter(ts), helper_fn_declaration_get_name); return ts; } */ std::string schema_name() const { if ($self->schema() == 0) return ""; return $self->schema()->name(); } int storage_mode() const { return std::visit([](auto& m) -> int { if constexpr (std::is_same_v, IfcParse::impl::in_memory_file_storage>) { return 0; } else if constexpr (std::is_same_v, IfcParse::impl::rocks_db_file_storage>) { return 1; } return -1; }, $self->storage_); } #ifdef IFOPSH_WITH_ROCKSDB RocksDBPrefixIterator* key_value_store_iter(const std::string& prefix) const { auto* storage = std::visit([](auto& m) -> IfcParse::impl::rocks_db_file_storage const * { if constexpr (std::is_same_v, IfcParse::impl::rocks_db_file_storage>) { return &m; } return nullptr; }, $self->storage_); if (!storage) { nullptr; } return new RocksDBPrefixIterator(storage, prefix); } #endif PyObject* key_value_store_query(const std::string& key) const { auto* storage = std::visit([](auto& m) -> IfcParse::impl::rocks_db_file_storage const * { if constexpr (std::is_same_v, IfcParse::impl::rocks_db_file_storage>) { return &m; } return nullptr; }, $self->storage_); if (!storage) { Py_RETURN_NONE; } #ifdef IFOPSH_WITH_ROCKSDB std::string value; if (storage->db->Get(storage->ropts, key, &value) != rocksdb::Status::OK()) { Py_RETURN_NONE; } return PyBytes_FromStringAndSize(value.data(), value.size()); #else Py_RETURN_NONE; #endif } %pythoncode %{ schema = property(schema_name) header = property(header) _registry = {} _old_init = __init__ def __init__(self, schema=None, schema_version=None): self._old_init(*filter(None, (schema, schema_version))) def __setattr__(self, k, v): object.__setattr__(self, k, v) if k == 'this': # only now we know the identity of the object and we set our python-side attributes based on a python-side map self.post_init(int(v)) %} } %extend express::Base { // 0 = not found // 1 = regular forward attribute // 2 = inverse attribute // 3 = derived attribute (redeclared in subtype as derived) int get_attribute_category(const std::string& name) const { if (!$self->declaration().as_entity()) { return name == "wrappedValue" ? 1 : 0; } { const std::vector attrs = $self->declaration().as_entity()->all_attributes(); std::vector::const_iterator it = attrs.begin(); for (; it != attrs.end(); ++it) { if ((*it)->name() == name) { if ($self->declaration().as_entity()->derived()[std::distance(attrs.begin(), it)]) { return 3; } else { return 1; } } } } { const std::vector attrs = $self->declaration().as_entity()->all_inverse_attributes(); std::vector::const_iterator it = attrs.begin(); for (; it != attrs.end(); ++it) { if ((*it)->name() == name) { return 2; } } } return 0; } /* @todo determine if we want to reinstante id() availability only on Entity instances. // id() is defined on IfcBaseEntity and not on IfcBaseClass, in order // to expose it to the Python wrapper it is simply duplicated here. // Same applies to the two methods reimplemented below. int id() const { return $self->as() != nullptr ? $self->as()->id() : 0; } */ int __len__() const { if ($self->declaration().as_entity()) { return $self->declaration().as_entity()->attribute_count(); } else { return 1; } } std::vector get_attribute_names() const { if (!$self->declaration().as_entity()) { return std::vector(1, "wrappedValue"); } const std::vector attrs = $self->declaration().as_entity()->all_attributes(); std::vector attr_names; attr_names.reserve(attrs.size()); std::vector::const_iterator it = attrs.begin(); for (; it != attrs.end(); ++it) { attr_names.push_back((*it)->name()); } return attr_names; } std::vector get_inverse_attribute_names() const { if (!$self->declaration().as_entity()) { return std::vector(0); } const std::vector attrs = $self->declaration().as_entity()->all_inverse_attributes(); std::vector attr_names; attr_names.reserve(attrs.size()); std::vector::const_iterator it = attrs.begin(); for (; it != attrs.end(); ++it) { attr_names.push_back((*it)->name()); } return attr_names; } bool is_a(const std::string& s) { return self->declaration().is(s); } std::string is_a(bool with_schema=false) const { auto t = self->declaration().name(); if (with_schema) { t = self->declaration().schema()->name() + "." + t; } return t; } AttributeValue get_argument(unsigned i) { return $self->get_attribute_value(i); } AttributeValue get_argument(const std::string& a) { auto i = $self->declaration().as_entity()->attribute_index(a); if (i == -1) { throw std::runtime_error("Attribute '" + a + "' not found on entity named " + $self->declaration().name()); } return $self->get_attribute_value((unsigned)i); } bool __eq__(const express::Base& other) const { return $self->identity() == other.identity(); } size_t __hash__() const { if (!self->declaration().as_entity()) { return boost::hash>{}({self->identity(), self->file()}); } else { return self->get_attribute_value(0).apply_visitor([&](const auto& val){ using U = std::decay_t; if constexpr (std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v) { // @todo return boost::hash>{}({self->declaration().index_in_schema(), 0, self->file()}); } else { return boost::hash>{}({self->declaration().index_in_schema(), val, self->file()}); } }); } } std::string __repr__() const { std::ostringstream oss; $self->toString(oss); return oss.str(); } std::string to_string(bool valid_spf = true) const { std::ostringstream oss; $self->toString(oss, valid_spf); return oss.str(); } /* // Just something to have a somewhat sensible value to hash size_t file_pointer() const { return reinterpret_cast($self->file_); } */ unsigned get_argument_index(const std::string& a) const { if ($self->declaration().as_entity()) { return $self->declaration().as_entity()->attribute_index(a); } else if (a == "wrappedValue") { return 0; } else { throw IfcParse::IfcException(a + " not found on " + $self->declaration().name()); } } std::vector get_inverse(const std::string& a) { if ($self->declaration().as_entity()) { return cast_vector($self->as().get_inverse(a)); } else { throw IfcParse::IfcException(a + " not found on " + $self->declaration().name()); } } const char* const attribute_type(unsigned int i) const { return IfcUtil::ArgumentTypeToString(helper_fn_attribute_type($self, i)); } const char* const attribute_type(const std::string& name) const { return IfcUtil::ArgumentTypeToString(helper_fn_attribute_type($self, express_Base_get_argument_index($self, name))); } const std::string& attribute_name(unsigned int i) const { if ($self->declaration().as_entity()) { return $self->declaration().as_entity()->attribute_by_index(i)->name(); } else if (i == 0) { static std::string WRAPPED = "wrappedValue"; return WRAPPED; } else { throw IfcParse::IfcException(boost::lexical_cast(i) + " out of bounds on " + $self->declaration().name()); } } void set_attribute_value_py(unsigned int i, PyObject* value) { if (value == Py_None) { // @nb we don't check anymore if the attribute is optional here, because it should be // possible to go back to the state at construction time. // bool is_optional = $self->declaration().as_entity()->attribute_by_index(i)->optional(); self->set_attribute_value(i, Blank{}); return; } auto to_index_long = [&](PyObject* o) -> long { PyObject* idx = PyNumber_Index(o); // accepts numpy ints, bools, etc. if (!idx) { PyErr_Clear(); throw IfcParse::IfcException("Attribute not set"); } long v = PyLong_AsLong(idx); Py_DECREF(idx); if (PyErr_Occurred()) { PyErr_Clear(); throw IfcParse::IfcException("Attribute not set"); } return v; }; auto to_double = [&](PyObject* o) -> double { double v = PyFloat_AsDouble(o); // accepts ints and float-like objects if (PyErr_Occurred()) { PyErr_Clear(); throw IfcParse::IfcException("Attribute not set"); } return v; }; auto to_string = [&](PyObject* o) -> std::string { if (PyUnicode_Check(o)) { Py_ssize_t n = 0; const char* s = PyUnicode_AsUTF8AndSize(o, &n); if (!s) { PyErr_Clear(); throw IfcParse::IfcException("Attribute not set"); } return std::string(s, static_cast(n)); } if (PyBytes_Check(o)) { char* s = nullptr; Py_ssize_t n = 0; if (PyBytes_AsStringAndSize(o, &s, &n) == -1) { PyErr_Clear(); throw IfcParse::IfcException("Attribute not set"); } return std::string(s, static_cast(n)); } throw IfcParse::IfcException("Attribute not set"); }; auto seq_fast = [&](PyObject* o) -> PyObject* { PyObject* fast = PySequence_Fast(o, "expected a sequence"); if (!fast) { PyErr_Clear(); throw IfcParse::IfcException("Attribute not set"); } return fast; // new ref }; auto to_vec_int = [&](PyObject* o) -> std::vector { PyObject* fast = seq_fast(o); Py_ssize_t n = PySequence_Fast_GET_SIZE(fast); PyObject** items = PySequence_Fast_ITEMS(fast); std::vector out; out.reserve(static_cast(n)); for (Py_ssize_t k = 0; k < n; ++k) { out.push_back(static_cast(to_index_long(items[k]))); } Py_DECREF(fast); return out; }; auto to_vec_double = [&](PyObject* o) -> std::vector { PyObject* fast = seq_fast(o); Py_ssize_t n = PySequence_Fast_GET_SIZE(fast); PyObject** items = PySequence_Fast_ITEMS(fast); std::vector out; out.reserve(static_cast(n)); for (Py_ssize_t k = 0; k < n; ++k) { out.push_back(to_double(items[k])); } Py_DECREF(fast); return out; }; auto to_vec_string = [&](PyObject* o) -> std::vector { PyObject* fast = seq_fast(o); Py_ssize_t n = PySequence_Fast_GET_SIZE(fast); PyObject** items = PySequence_Fast_ITEMS(fast); std::vector out; out.reserve(static_cast(n)); for (Py_ssize_t k = 0; k < n; ++k) { out.push_back(to_string(items[k])); } Py_DECREF(fast); return out; }; auto to_base = [&](PyObject* o) -> express::Base { void* vp = nullptr; // Try non-const pointer first if (swig_type_info* ti = SWIG_TypeQuery("express::Base *")) { int res = SWIG_ConvertPtr(o, &vp, ti, 0); if (res >= 0 && vp) { return *static_cast(vp); } } // Then try const pointer vp = nullptr; if (swig_type_info* ti = SWIG_TypeQuery("express::Base const *")) { int res = SWIG_ConvertPtr(o, &vp, ti, 0); if (res >= 0 && vp) { return *static_cast(vp); } } throw IfcParse::IfcException("Attribute not set"); }; auto to_vec_base = [&](PyObject* o) -> std::vector { PyObject* fast = seq_fast(o); Py_ssize_t n = PySequence_Fast_GET_SIZE(fast); PyObject** items = PySequence_Fast_ITEMS(fast); std::vector out; out.reserve(static_cast(n)); for (Py_ssize_t k = 0; k < n; ++k) { out.push_back(to_base(items[k])); } Py_DECREF(fast); return out; }; auto to_vec_vec_int = [&](PyObject* o) -> std::vector> { PyObject* fast = seq_fast(o); Py_ssize_t n = PySequence_Fast_GET_SIZE(fast); PyObject** items = PySequence_Fast_ITEMS(fast); std::vector> out; out.reserve(static_cast(n)); for (Py_ssize_t k = 0; k < n; ++k) { out.push_back(to_vec_int(items[k])); } Py_DECREF(fast); return out; }; auto to_vec_vec_double = [&](PyObject* o) -> std::vector> { PyObject* fast = seq_fast(o); Py_ssize_t n = PySequence_Fast_GET_SIZE(fast); PyObject** items = PySequence_Fast_ITEMS(fast); std::vector> out; out.reserve(static_cast(n)); for (Py_ssize_t k = 0; k < n; ++k) { out.push_back(to_vec_double(items[k])); } Py_DECREF(fast); return out; }; auto to_vec_vec_base = [&](PyObject* o) -> std::vector> { PyObject* fast = seq_fast(o); Py_ssize_t n = PySequence_Fast_GET_SIZE(fast); PyObject** items = PySequence_Fast_ITEMS(fast); std::vector> out; out.reserve(static_cast(n)); for (Py_ssize_t k = 0; k < n; ++k) { out.push_back(to_vec_base(items[k])); } Py_DECREF(fast); return out; }; // Dispatch based on the IFC argument type (same decision Python was making before) IfcUtil::ArgumentType arg_type = helper_fn_attribute_type($self, i); switch (arg_type) { case IfcUtil::Argument_INT: { self->set_attribute_value(i, static_cast(to_index_long(value))); return; } case IfcUtil::Argument_BOOL: { if (PyBool_Check(value)) { self->set_attribute_value(i, value == Py_True); } return; } case IfcUtil::Argument_LOGICAL: { boost::logic::tribool t(boost::logic::indeterminate); if (PyBool_Check(value)) { t = (value == Py_True); } else { long v = to_index_long(value); if (v == 0) t = false; else if (v == 1) t = true; else if (v == -1 || v == 2) t = boost::logic::tribool(boost::logic::indeterminate); else throw IfcParse::IfcException("Attribute not set"); } self->set_attribute_value(i, t); return; } case IfcUtil::Argument_DOUBLE: { self->set_attribute_value(i, to_double(value)); return; } case IfcUtil::Argument_STRING: self->set_attribute_value(i, to_string(value)); return; case IfcUtil::Argument_ENUMERATION: { const IfcParse::enumeration_type* enum_type = $self->declaration().schema()->declaration_by_name($self->declaration().type())->as_entity()-> attribute_by_index(i)->type_of_attribute()->as_named_type()->declared_type()->as_enumeration_type(); self->set_attribute_value(i, EnumerationReference(enum_type, enum_type->lookup_enum_offset(to_string(value)))); return; } case IfcUtil::Argument_BINARY: { std::string s = to_string(value); if (IfcUtil::valid_binary_string(s)) { boost::dynamic_bitset<> bits(s); self->set_attribute_value(i, bits); } return; } case IfcUtil::Argument_AGGREGATE_OF_INT: { self->set_attribute_value(i, to_vec_int(value)); return; } case IfcUtil::Argument_AGGREGATE_OF_DOUBLE: { self->set_attribute_value(i, to_vec_double(value)); return; } case IfcUtil::Argument_AGGREGATE_OF_STRING: self->set_attribute_value(i, to_vec_string(value)); return; case IfcUtil::Argument_AGGREGATE_OF_BINARY: { auto vs = to_vec_string(value); std::vector< boost::dynamic_bitset<> > bits; bits.reserve(vs.size()); for (auto& v : vs) { if (IfcUtil::valid_binary_string(v)) { bits.push_back(boost::dynamic_bitset<>(v)); } else { throw IfcParse::IfcException("String not a valid binary representation"); } } self->set_attribute_value(i, bits); return; } case IfcUtil::Argument_ENTITY_INSTANCE: { self->set_attribute_value(i, to_base(value)); return; } case IfcUtil::Argument_AGGREGATE_OF_ENTITY_INSTANCE: { self->set_attribute_value(i, to_vec_base(value)); return; } case IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_INT: { self->set_attribute_value(i, to_vec_vec_int(value)); return; } case IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_DOUBLE: { self->set_attribute_value(i, to_vec_vec_double(value)); return; } case IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE: { self->set_attribute_value(i, to_vec_vec_base(value)); return; } default: throw IfcParse::IfcException("Attribute not set"); } } IfcParse::IfcFile* file_py() const { return $self->file(); } %pythoncode %{ file = property(file_py) %} } %extend IfcParse::IfcSpfHeader { // Upcast to header instances for SWIG, because // it has no idea about the schema definitions. express::Base file_description_py() { return $self->file_description(); } express::Base file_name_py() { return $self->file_name(); } express::Base file_schema_py() { return $self->file_schema(); } %pythoncode %{ file_description = property(file_description_py) file_name = property(file_name_py) file_schema = property(file_schema_py) %} }; %include "../ifcparse/ifc_parse_api.h" %include "../ifcparse/IfcSpfHeader.h" %pythoncode %{ ### hack hack hack ### we trick swig into inheriting from our own extension class ### that way we do not constantly need to decorate/undecorate # @todo is there no official way to do this? _old_object = object from .file import file_mixin as custom_base object = custom_base %} %include "../ifcparse/IfcFile.h" %pythoncode %{ ### hack hack hack ### restore object = _old_object %} %include "../ifcparse/file_open_status.h" %pythoncode %{ ### hack hack hack ### we trick swig into inheriting from our own extension class ### that way we do not constantly need to decorate/undecorate # @todo is there no official way to do this? _old_object = object from .entity_instance import entity_instance_mixin as custom_base object = custom_base %} %include "../ifcparse/express.h" %pythoncode %{ ### hack hack hack ### restore object = _old_object %} %include "../ifcparse/IfcSchema.h" %include "../serializers/RocksDbSerializer.h" // The IfcFile* returned by open() is to be freed by SWIG/Python %newobject open; %newobject read; %newobject parse_ifcxml; %newobject stream_from_string; %inline %{ IfcParse::IfcFile* open(const std::string& fn, bool readonly=false) { IfcParse::IfcFile* f; Py_BEGIN_ALLOW_THREADS; f = new IfcParse::IfcFile(fn, IfcParse::FT_AUTODETECT, readonly); Py_END_ALLOW_THREADS; return f; } IfcParse::IfcFile* read(const std::string& data) { char* copiedData = new char[data.length()]; memcpy(copiedData, data.c_str(), data.length()); IfcParse::IfcFile* f; Py_BEGIN_ALLOW_THREADS; f = new IfcParse::IfcFile((void *)copiedData, data.length()); Py_END_ALLOW_THREADS; return f; } IfcParse::InstanceStreamer* stream_from_string(const std::string& data) { char* copiedData = new char[data.length()]; memcpy(copiedData, data.c_str(), data.length()); return new IfcParse::InstanceStreamer((void *)copiedData, data.length()); } const char* version() { return IFCOPENSHELL_VERSION; } express::Base new_IfcBaseClass(IfcParse::IfcFile* file, const std::string& name) { return file->create(file->schema()->declaration_by_name(name)); } %} %extend IfcParse::named_type { %pythoncode %{ def __repr__(self): return repr(self.declared_type()) %} } %extend IfcParse::simple_type { %pythoncode %{ def __repr__(self): return "<%s>" % self.declared_type() %} } %extend IfcParse::aggregation_type { std::string type_of_aggregation_string() const { static const char* const aggr_strings[] = {"array", "bag", "list", "set"}; return aggr_strings[(int) $self->type_of_aggregation()]; } %pythoncode %{ def __repr__(self): format_bound = lambda i: "?" if i == -1 else str(i) return "<%s [%s:%s] of %r>" % ( self.type_of_aggregation_string(), format_bound(self.bound1()), format_bound(self.bound2()), self.type_of_element() ) %} } %extend IfcParse::type_declaration { %pythoncode %{ def __repr__(self): return "" % (self.name(), self.declared_type()) %} std::vector argument_types() { std::vector r; auto at = IfcUtil::Argument_UNKNOWN; auto pt = $self->declared_type(); if (pt) { at = IfcUtil::from_parameter_type(pt); } r.push_back(IfcUtil::ArgumentTypeToString(at)); return r; } } %extend IfcParse::select_type { %pythoncode %{ def __repr__(self): return "