Files

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

497 lines
19 KiB
C++
Raw Permalink Normal View History

/********************************************************************************
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#ifndef IFCFILE_H
#define IFCFILE_H
2023-09-17 12:30:17 +02:00
#include "ifc_parse_api.h"
#include "IfcParse.h"
#include "IfcSchema.h"
#include "IfcSpfHeader.h"
#include "storage.h"
#include "file_open_status.h"
2021-09-11 12:59:48 +02:00
2026-06-11 21:04:44 +02:00
#include <functional>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/random_access_index.hpp>
2023-09-17 12:30:17 +02:00
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/circular_buffer.hpp>
2023-09-17 12:30:17 +02:00
#include <iterator>
#include <map>
#include <cstdint>
#ifdef IFOPSH_WITH_ROCKSDB
#include <rocksdb/merge_operator.h>
2025-02-25 21:29:54 +01:00
namespace {
// @todo move to a proper place
class ConcatenateIdMergeOperator : public rocksdb::AssociativeMergeOperator {
public:
virtual bool FullMergeV2(const MergeOperator::MergeOperationInput& merge_in,
MergeOperator::MergeOperationOutput* merge_out) const {
// Log(InfoLogLevel::INFO_LEVEL, merge_in.logger, "FullMergeV2 new_value size:%ld", merge_out->new_value.size());
2025-08-31 21:26:54 +02:00
merge_out->new_value.clear();
if (merge_in.existing_value) {
merge_out->new_value.append(merge_in.existing_value->data(), merge_in.existing_value->size());
}
for (auto& operand : merge_in.operand_list) {
merge_out->new_value.append(operand.data(), operand.size());
}
return true;
}
2025-02-27 22:07:31 +01:00
virtual bool Merge(const rocksdb::Slice&,
2025-03-18 10:41:03 +01:00
const rocksdb::Slice*,
const rocksdb::Slice&,
std::string*,
2025-02-27 22:07:31 +01:00
rocksdb::Logger*) const override
{
return false;
2025-02-25 21:29:54 +01:00
}
virtual const char* Name() const override {
return "ConcatenateIdMergeOperator";
}
};
}
#endif
2025-02-25 21:29:54 +01:00
namespace IfcParse {
enum filetype {
FT_IFCSPF,
FT_IFCXML,
FT_IFCZIP,
FT_ROCKSDB,
FT_UNKNOWN,
FT_AUTODETECT
};
2025-09-26 14:24:49 +02:00
IFC_PARSE_API filetype guess_file_type(const std::string& fn);
2024-08-23 20:29:07 +02:00
2025-09-26 14:24:49 +02:00
class IFC_PARSE_API InstanceStreamer {
private:
2025-10-13 20:47:43 +02:00
FileReader* stream_;
IfcSpfLexer* lexer_;
IfcSpfHeader* header_;
boost::circular_buffer<Token> token_stream_;
const IfcParse::schema_definition* schema_;
IfcParse::impl::in_memory_file_storage storage_;
IfcParse::file_open_status good_ = IfcParse::file_open_status::SUCCESS;
2026-06-11 21:04:44 +02:00
std::reference_wrapper<Logger> logger_;
int progress_;
IfcParse::unresolved_references references_to_resolve_;
int yielded_header_instances_ = 0;
std::vector<const declaration*> types_to_bypass_;
std::vector<unsigned> bypassed_instances_;
2025-03-14 10:20:22 +01:00
public:
bool coerce_attribute_count = true;
operator bool() const {
2025-10-13 20:47:43 +02:00
return good_ && !lexer_->stream->eof();
}
IfcParse::file_open_status status() const {
return good_;
}
2023-09-17 12:30:17 +02:00
const IfcParse::unresolved_references& references() const {
return references_to_resolve_;
}
2023-09-17 12:30:17 +02:00
IfcParse::unresolved_references& references() {
return references_to_resolve_;
}
2023-09-17 12:30:17 +02:00
const std::vector<unsigned>& bypassed_instances() {
std::sort(bypassed_instances_.begin(), bypassed_instances_.end());
return bypassed_instances_;
}
const IfcParse::impl::in_memory_file_storage::entities_by_ref_t& inverses() const {
return storage_.byref_excl_;
}
2023-09-17 12:30:17 +02:00
IfcParse::impl::in_memory_file_storage::entities_by_ref_t& inverses() {
return storage_.byref_excl_;
}
2023-09-17 12:30:17 +02:00
2025-10-22 21:42:15 +02:00
std::vector<std::unique_ptr<IfcUtil::IfcBaseClass>> stealInstances() {
2025-10-08 14:17:47 +02:00
return storage_.steal_instances();
}
2025-10-22 21:42:15 +02:00
bool hasSemicolon() const;
2025-10-13 20:47:43 +02:00
size_t semicolonCount() const;
void pushPage(const std::string& page);
2025-10-13 20:47:43 +02:00
2026-06-11 21:04:44 +02:00
InstanceStreamer(Logger& logger = Logger::Root());
2025-10-13 20:47:43 +02:00
2026-06-11 21:04:44 +02:00
InstanceStreamer(const std::string& fn, bool mmap=false, Logger& logger = Logger::Root());
2026-06-11 21:04:44 +02:00
InstanceStreamer(void* data, int length, Logger& logger = Logger::Root());
2026-06-11 21:04:44 +02:00
InstanceStreamer(const IfcParse::schema_definition* schema, IfcParse::IfcSpfLexer* lexer, Logger& logger = Logger::Root());
void bypassTypes(const std::set<std::string>& type_names);
~InstanceStreamer() {
delete stream_;
if (stream_) {
delete lexer_;
}
delete header_;
}
2025-10-22 21:42:15 +02:00
std::optional<std::tuple<size_t, const IfcParse::declaration*, IfcEntityInstanceData>> readInstance();
};
2023-09-17 12:30:17 +02:00
class uninitialized_tag {};
/// This class provides access to the entity instances in an IFC file
/// The file takes ownership of instances added to this file and deletes them when the file is deleted.
class IFC_PARSE_API IfcFile {
private:
2023-09-17 12:30:17 +02:00
typedef std::map<uint32_t, IfcUtil::IfcBaseClass*> entity_entity_map_t;
// @todo determine the constness of things (probably needs to be all const, we don't want to overwrite)
// @todo we have variant_iterator and MapVariant, we probably need to retain only one?
public:
using const_iterator = variant_iterator<impl::in_memory_file_storage::iterator, impl::rocks_db_file_storage::const_iterator>;
using type_iterator = variant_iterator<impl::in_memory_file_storage::type_iterator, impl::rocks_db_file_storage::rocksdb_types_iterator>;
using storage_t = std::variant<std::monostate, impl::in_memory_file_storage, impl::rocks_db_file_storage>;
2025-03-14 18:56:27 +01:00
2025-08-27 11:33:01 +02:00
typedef VariantMap<impl::in_memory_file_storage::entity_instance_by_guid_t, impl::rocks_db_file_storage::entity_instance_by_guid_t> entity_instance_by_guid_t;
entity_instance_by_guid_t byguid_;
typedef VariantMap<impl::in_memory_file_storage::entity_instance_by_name_t, impl::rocks_db_file_storage::entity_instance_by_name_t> entity_by_id_t;
entity_by_id_t byid_;
typedef VariantMap<impl::in_memory_file_storage::entities_by_ref_t, impl::rocks_db_file_storage::entities_by_ref_t> entities_by_ref_t;
entities_by_ref_t byref_excl_;
2025-03-14 18:56:27 +01:00
bool check_existance_before_adding = true;
bool calculate_unit_factors = true;
2025-03-18 10:41:03 +01:00
bool instantiate_typed_instances = true;
2025-03-14 18:56:27 +01:00
// @todo temporarily public for header
storage_t storage_;
std::set<std::string> types_to_bypass_loading_;
private:
2023-09-17 12:30:17 +02:00
file_open_status good_ = file_open_status::SUCCESS;
2026-06-11 21:04:44 +02:00
std::reference_wrapper<Logger> logger_;
2023-09-17 12:30:17 +02:00
const IfcParse::schema_definition* schema_;
const IfcParse::declaration* ifcroot_type_;
entity_entity_map_t entity_file_map_;
2023-09-17 12:30:17 +02:00
unsigned int max_id_;
2023-09-17 12:30:17 +02:00
IfcSpfHeader _header;
void setDefaultHeaderValues();
typedef boost::multi_index_container<
int,
boost::multi_index::indexed_by<
boost::multi_index::sequenced<>,
boost::multi_index::ordered_unique<
boost::multi_index::identity<int>>>>
batch_deletion_ids_t;
batch_deletion_ids_t batch_deletion_ids_;
bool batch_mode_ = false;
2025-08-31 15:52:46 +02:00
void process_deletion_(IfcUtil::IfcBaseClass* entity);
2023-09-17 12:30:17 +02:00
public:
2017-12-12 10:33:24 +01:00
#ifdef USE_MMAP
2025-10-13 20:47:43 +02:00
/// <summary>
/// Constructs an IfcFile object from a file path, optionally using memory-mapped I/O, only supports IFC-SPF files.
/// </summary>
/// <param name="path">UTF-8 file path to an IFC-SPF file</param>
/// <param name="mmap">Whether to use memory-mapped I/O</param>
2026-06-11 21:04:44 +02:00
IfcFile(const std::string& path, bool mmap, Logger& logger = Logger::Root());
2017-12-12 10:33:24 +01:00
#endif
2025-10-13 20:47:43 +02:00
/// <summary>
/// Constructs an IfcFile object from a file path, supports IFC-SPF and the IfcOpenShell-specific RocksDB format.
/// </summary>
/// <param name="path">UTF-8 file path to an IFC-SPF file or RocksDB database directory</param>
/// <param name="ty">File type of the path</param>
/// <param name="readonly">Whether to open in read-only mode, only supported on RocksDB databases</param>
2026-06-11 21:04:44 +02:00
IfcFile(const std::string& path, filetype ty=FT_AUTODETECT, bool readonly=false, Logger& logger = Logger::Root());
2025-10-13 20:47:43 +02:00
/// <summary>
/// Constructs an IfcFile object from a stream containing IFC-SPF data.
/// </summary>
2026-06-11 21:04:44 +02:00
IfcFile(std::istream& stream, int length, Logger& logger = Logger::Root());
2025-10-13 20:47:43 +02:00
/// <summary>
/// Constructs an IfcFile object from a memory buffer containing IFC-SPF data.
/// </summary>
2026-06-11 21:04:44 +02:00
IfcFile(void* data, int length, Logger& logger = Logger::Root());
2025-10-13 20:47:43 +02:00
/// <summary>
/// Constructs an IfcFile object from a given IFC SPF stream.
/// </summary>
/// <param name="stream">A pointer to an IfcParse::FileReader object representing the input IFC SPF data stream.</param>
2026-06-11 21:04:44 +02:00
IfcFile(IfcParse::FileReader* stream, Logger& logger = Logger::Root());
2025-10-13 20:47:43 +02:00
/// <summary>
/// Constructs an IfcFile object with the specified schema, file type, and file path.
/// @nb path is only used in rocksdb mode, for spf file is in-memory only until write() is called
/// </summary>
/// <param name="schema">Pointer to the schema definition to use. Defaults to the IFC4 schema if not specified.</param>
/// <param name="ty">The file type to use for the file. Defaults to FT_AUTODETECT.</param>
/// <param name="path">The file system path to the IFC file. Defaults to an empty string.</param>
2026-06-11 21:04:44 +02:00
IfcFile(const IfcParse::schema_definition* schema = IfcParse::schema_by_name("IFC4"), filetype ty = FT_AUTODETECT, const std::string& path = "", Logger& logger = Logger::Root());
2023-09-17 12:30:17 +02:00
/// <summary>
/// Constructs an unitialized IfcFile object. Call initialize() later on. Allows to specify which types to bypass during load.
/// </summary>
2026-06-11 21:04:44 +02:00
IfcFile(const uninitialized_tag&, Logger& logger = Logger::Root());
bool initialize(const std::string& path, filetype ty = FT_AUTODETECT, bool readonly = false);
#ifdef USE_MMAP
bool initialize(const std::string& path, bool mmap);
#endif
/// @brief Bypass loading of all instances of the specified type name. Only applies to parsed IFC-SPF files.
/// @param type_name case insensitive name of the type to bypass
void bypass_type(const std::string& type_name);
~IfcFile();
2023-09-17 12:30:17 +02:00
IfcParse::file_open_status good() const { return good_; }
2026-06-11 21:04:44 +02:00
Logger& logger() const { return logger_.get(); }
2023-09-17 12:30:17 +02:00
/// Returns the first entity in the range of instances contained in the model,
/// in arbitrary order
2025-08-27 11:33:01 +02:00
entity_by_id_t::iterator begin() const {
return byid_.begin();
}
/// Returns the first entity in the range of instances contained in the model,
/// in arbitrary order
2025-08-27 11:33:01 +02:00
entity_by_id_t::iterator end() const {
return byid_.end();
}
2023-09-17 12:30:17 +02:00
type_iterator types_begin() const;
type_iterator types_end() const;
/// Returns all entities in the file that match the template argument.
/// NOTE: This also returns subtypes of the requested type, for example:
/// IfcWall will also return IfcWallStandardCase entities
template <class T>
typename T::list::ptr instances_by_type() {
aggregate_of_instance::ptr untyped_list = instances_by_type(&T::Class());
if (untyped_list) {
return untyped_list->as<T>();
}
return typename T::list::ptr(new typename T::list);
2023-09-17 12:30:17 +02:00
}
template <class T>
typename T::list::ptr instances_by_type_excl_subtypes() {
aggregate_of_instance::ptr untyped_list = instances_by_type_excl_subtypes(&T::Class());
if (untyped_list) {
return untyped_list->as<T>();
}
return typename T::list::ptr(new typename T::list);
2023-09-17 12:30:17 +02:00
}
/// Returns all entities in the file that match the positional argument.
/// NOTE: This also returns subtypes of the requested type, for example:
/// IfcWall will also return IfcWallStandardCase entities
aggregate_of_instance::ptr instances_by_type(const IfcParse::declaration*);
/// Returns all entities in the file that match the positional argument.
aggregate_of_instance::ptr instances_by_type_excl_subtypes(const IfcParse::declaration*);
/// Returns all entities in the file that match the positional argument.
/// NOTE: This also returns subtypes of the requested type, for example:
/// IfcWall will also return IfcWallStandardCase entities
aggregate_of_instance::ptr instances_by_type(const std::string& type);
2023-09-17 12:30:17 +02:00
/// Returns all entities in the file that match the positional argument.
aggregate_of_instance::ptr instances_by_type_excl_subtypes(const std::string& type);
2023-09-17 12:30:17 +02:00
/// Returns all entities in the file that reference the id
aggregate_of_instance::ptr instances_by_reference(int id);
2018-08-29 12:54:25 +02:00
2023-09-17 12:30:17 +02:00
/// Returns the entity with the specified id
IfcUtil::IfcBaseClass* instance_by_id(int id);
2018-08-29 12:54:25 +02:00
2023-09-17 12:30:17 +02:00
/// Returns the entity with the specified GlobalId
IfcUtil::IfcBaseClass* instance_by_guid(const std::string& guid);
2023-09-17 12:30:17 +02:00
/// Performs a depth-first traversal, returning all entity instance
/// attributes as a flat list. NB: includes the root instance specified
/// in the first function argument.
2024-08-23 20:29:07 +02:00
static aggregate_of_instance::ptr traverse(IfcUtil::IfcBaseClass* instance, int max_level = -1);
2023-09-17 12:30:17 +02:00
/// Same as traverse() but maintains topological order by using a
/// breadth-first search
2024-08-23 20:29:07 +02:00
static aggregate_of_instance::ptr traverse_breadth_first(IfcUtil::IfcBaseClass* instance, int max_level = -1);
2023-09-17 12:30:17 +02:00
/// Get the attribute indices corresponding to the list of entity instances
/// returned by getInverse().
std::vector<int> get_inverse_indices(int instance_id);
2023-06-01 22:03:31 +02:00
template <typename T>
2023-06-01 21:39:08 +02:00
typename T::list::ptr getInverse(int instance_id, int attribute_index) {
2023-06-01 22:03:31 +02:00
return getInverse(instance_id, &T::Class(), attribute_index)->template as<T>();
2023-06-01 21:39:08 +02:00
}
2023-09-17 12:30:17 +02:00
aggregate_of_instance::ptr getInverse(int instance_id, const IfcParse::declaration* type, int attribute_index);
2024-08-23 20:29:07 +02:00
size_t getTotalInverses(int instance_id);
2023-09-17 12:30:17 +02:00
unsigned int FreshId() { return ++max_id_; }
2023-09-17 12:30:17 +02:00
unsigned int getMaxId() const { return max_id_; }
2023-09-17 12:30:17 +02:00
const IfcParse::declaration* ifcroot_type() const { return ifcroot_type_; }
void recalculate_id_counter();
IfcUtil::IfcBaseClass* addEntity(IfcUtil::IfcBaseClass* entity, int id = -1);
void addEntities(aggregate_of_instance::ptr entities);
2023-09-17 12:30:17 +02:00
/// Removes entity instance from file and unsets references.
///
/// Attention when running removeEntity inside a loop over a list of entities to be removed.
/// This invalidates the iterator. A workaround is to reverse the loop:
/// boost::shared_ptr<aggregate_of_instance> entities = ...;
/// for (auto it = entities->end() - 1; it >= entities->begin(); --it) {
/// IfcUtil::IfcBaseClass *const inst = *it;
/// model->removeEntity(inst);
/// }
void removeEntity(IfcUtil::IfcBaseClass* entity);
const IfcSpfHeader& header() const { return _header; }
IfcSpfHeader& header() { return _header; }
2025-09-19 13:23:04 +02:00
static std::string createTimestamp();
2023-09-17 12:30:17 +02:00
2025-09-19 13:23:04 +02:00
const IfcParse::schema_definition* schema() const;
2023-09-17 12:30:17 +02:00
std::pair<IfcUtil::IfcBaseClass*, double> getUnit(const std::string& unit_type);
void build_inverses();
void register_inverse(unsigned, const IfcParse::entity* from_entity, int inst_id, int attribute_index);
void unregister_inverse(unsigned, const IfcParse::entity* from_entity, IfcUtil::IfcBaseClass*, int attribute_index);
entity_instance_by_guid_t internal_guid_map() { return byguid_; };
void add_type_ref(IfcUtil::IfcBaseClass* new_entity);
void remove_type_ref(IfcUtil::IfcBaseClass* new_entity);
void process_deletion_inverse(IfcUtil::IfcBaseClass* inst);
void build_inverses_(IfcUtil::IfcBaseClass*);
2025-02-25 21:29:54 +01:00
template <typename T>
T* create() {
return std::visit([](auto& m) -> T* {
if constexpr (std::is_same_v<std::decay_t<decltype(m)>, impl::in_memory_file_storage> ||
std::is_same_v<std::decay_t<decltype(m)>, impl::rocks_db_file_storage>)
{
return m.template create<T>();
2025-02-25 21:29:54 +01:00
} else {
return nullptr;
}
}, storage_);
}
IfcUtil::IfcBaseClass* create(const IfcParse::declaration* decl) {
return std::visit([decl](auto& m) -> IfcUtil::IfcBaseClass* {
if constexpr (std::is_same_v<std::decay_t<decltype(m)>, impl::in_memory_file_storage> ||
std::is_same_v<std::decay_t<decltype(m)>, impl::rocks_db_file_storage>)
{
return m.create(decl);
} else {
return nullptr;
}
}, storage_);
}
2025-08-31 15:52:46 +02:00
void batch() {
batch_mode_ = true;
}
void unbatch();
void reset_identity_cache();
};
2018-08-29 12:54:25 +02:00
#ifdef WITH_IFCXML
2026-06-11 21:04:44 +02:00
IFC_PARSE_API IfcFile* parse_ifcxml(const std::string& filename, Logger& logger = Logger::Root());
2018-08-29 12:54:25 +02:00
#endif
namespace impl {
// Trick to have a dependent static assertion
template <class> inline constexpr bool dependent_false_v = false;
}
2023-09-17 12:30:17 +02:00
} // namespace IfcParse
2025-02-25 21:29:54 +01:00
template <typename T>
T* IfcParse::impl::in_memory_file_storage::create() {
2025-02-27 22:07:31 +01:00
IfcUtil::IfcBaseClass* inst = nullptr;
if constexpr (std::is_same_v<std::decay_t<std::invoke_result_t<typename T::Class>>, IfcParse::entity>) {
2025-02-27 22:07:31 +01:00
inst = new T(in_memory_attribute_storage(T::Class().attribute_count()));
} else if constexpr (std::is_same_v<std::decay_t<std::invoke_result_t<typename T::Class>>, IfcParse::type_declaration>) {
2025-02-27 22:07:31 +01:00
inst = new T(in_memory_attribute_storage(1));
2025-02-25 21:29:54 +01:00
} else {
static_assert(dependent_false_v<T>, "Requires and entity or type declaration");
2025-02-25 21:29:54 +01:00
}
2025-02-27 22:07:31 +01:00
inst->file_ = file;
return file->addEntity(inst)->as<T>();
2025-02-25 21:29:54 +01:00
}
template <typename T>
T* IfcParse::impl::rocks_db_file_storage::create() {
if constexpr (std::is_same_v<std::decay_t<std::invoke_result_t<typename T::Class>>, IfcParse::entity> || std::is_same_v<std::decay_t<std::invoke_result_t<typename T::Class>>, IfcParse::type_declaration>) {
2025-02-27 22:07:31 +01:00
auto* inst = new T(rocks_db_attribute_storage{});
inst->file_ = file;
return file->addEntity(inst)->template as<T>();
2025-02-25 21:29:54 +01:00
} else {
static_assert(dependent_false_v<T>, "Requires and entity or type declaration");
2025-02-25 21:29:54 +01:00
}
}
2018-12-14 12:14:12 +01:00
namespace std {
2023-09-17 12:30:17 +02:00
template <>
struct iterator_traits<IfcParse::IfcFile::type_iterator> {
typedef ptrdiff_t difference_type;
typedef const IfcParse::declaration* value_type;
typedef const IfcParse::declaration*& reference;
typedef const IfcParse::declaration** pointer;
typedef std::forward_iterator_tag iterator_category;
};
} // namespace std
#endif