Files
IfcOpenShell/src/ifcparse/IfcParse.cpp
T

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

2988 lines
119 KiB
C++
Raw Normal View History

/********************************************************************************
2023-09-18 16:13:44 +02:00
* *
* 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/>. *
* *
********************************************************************************/
2011-05-08 11:04:32 +00:00
2023-09-17 12:30:17 +02:00
#include "IfcParse.h"
#include "IfcBaseClass.h"
#include "IfcCharacterDecoder.h"
#include "IfcException.h"
#include "IfcFile.h"
#include "IfcLogger.h"
2023-09-17 12:30:17 +02:00
#include "IfcSchema.h"
#include "IfcSIPrefix.h"
2025-10-13 20:47:43 +02:00
#include "FileReader.h"
2023-09-17 12:30:17 +02:00
#include "utils.h"
2011-05-08 11:04:32 +00:00
2023-09-17 12:30:17 +02:00
#include <algorithm>
#include <boost/algorithm/string.hpp>
2024-08-23 20:29:07 +02:00
#include <boost/variant.hpp>
2023-09-17 12:30:17 +02:00
#include <boost/math/special_functions/fpclassify.hpp>
#include <ctime>
2023-09-17 12:30:17 +02:00
#include <set>
#include <stdio.h>
#include <stdlib.h>
2023-09-17 12:30:17 +02:00
#include <string>
2024-08-23 20:29:07 +02:00
#include <iomanip>
#include <charconv>
2023-09-18 16:13:44 +02:00
#ifdef USE_MMAP
#include <boost/filesystem/path.hpp>
#endif
2016-06-12 11:22:08 +02:00
#define PERMISSIVE_FLOAT
2011-05-08 11:04:32 +00:00
using namespace IfcParse;
2023-09-17 12:30:17 +02:00
// A static locale for the real number parser. strtod() is locale-dependent, causing issues
// in locales that have ',' as a decimal separator. Therefore the non standard _strtod_l() /
// strtod_l() is used and a reference to the "C" locale is obtained here. The alternative is
// to use std::istringstream::imbue(std::locale::classic()), but there are subtleties in
// parsing in MSVC2010 and it appears to be much slower.
2016-06-05 14:03:03 +02:00
#if defined(_MSC_VER)
2023-09-17 12:30:17 +02:00
static _locale_t locale = (_locale_t)0;
void init_locale() {
2023-09-17 12:30:17 +02:00
if (locale == (_locale_t)0) {
locale = _create_locale(LC_NUMERIC, "C");
}
}
2016-06-05 14:03:03 +02:00
#else
2016-06-05 14:03:03 +02:00
#if defined(__MINGW64__) || defined(__MINGW32__)
#include <locale>
#include <sstream>
typedef void* locale_t;
2016-06-05 14:03:03 +02:00
static locale_t locale = (locale_t)0;
void init_locale() {}
double strtod_l(const char* start, char** end, locale_t loc) {
2023-09-17 12:30:17 +02:00
double d;
std::stringstream ss;
ss.imbue(std::locale::classic());
ss << start;
ss >> d;
size_t nread = ss.tellg();
*end = const_cast<char*>(start) + nread;
return d;
2016-06-05 14:03:03 +02:00
}
#else
#ifdef __APPLE__
#include <xlocale.h>
#endif
#include <locale.h>
static locale_t locale = (locale_t)0;
void init_locale() {
2023-09-17 12:30:17 +02:00
if (locale == (locale_t)0) {
locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0);
}
}
2016-06-05 14:03:03 +02:00
#endif
2016-06-05 14:03:03 +02:00
#endif
2026-06-11 21:04:44 +02:00
IfcSpfLexer::IfcSpfLexer(IfcParse::FileReader* stream_, Logger& logger)
: logger_(logger)
{
stream = stream_;
2026-06-11 21:04:44 +02:00
decoder_ = new IfcCharacterDecoder(stream_, logger_);
}
IfcSpfLexer::~IfcSpfLexer() {
delete decoder_;
}
2025-10-13 20:47:43 +02:00
size_t IfcSpfLexer::skipWhitespace() const {
size_t index = 0;
while (!stream->eof()) {
char character = stream->peek();
if ((character == ' ' || character == '\r' || character == '\n' || character == '\t')) {
2025-10-13 20:47:43 +02:00
stream->increment();
++index;
2023-09-17 12:30:17 +02:00
} else {
break;
}
}
return index;
2014-04-02 15:04:09 +00:00
}
2025-10-13 20:47:43 +02:00
size_t IfcSpfLexer::skipComment() const {
if (stream->eof()) {
return 0;
}
2025-10-13 20:47:43 +02:00
char character = stream->peek();
if (character != '/') {
2023-09-17 12:30:17 +02:00
return 0;
}
2025-10-13 20:47:43 +02:00
stream->increment();
character = stream->peek();
if (character != '*') {
2025-10-13 20:47:43 +02:00
stream->seek(stream->tell() - 1);
2023-09-17 12:30:17 +02:00
return 0;
}
2025-10-13 20:47:43 +02:00
size_t index = 2;
char intermediate = 0;
2025-10-13 20:47:43 +02:00
while (!stream->eof()) {
character = stream->peek();
stream->increment();
++index;
if (character == '/' && intermediate == '*') {
2023-09-17 12:30:17 +02:00
break;
}
intermediate = character;
2023-09-17 12:30:17 +02:00
}
return index;
2014-04-02 15:04:09 +00:00
}
//
// Returns the offset of the current Token and moves cursor to next
//
Token IfcSpfLexer::Next() {
2025-10-13 20:47:43 +02:00
if (stream->eof()) {
return Token{};
2023-09-17 12:30:17 +02:00
}
while ((skipWhitespace() != 0U) || (skipComment() != 0U)) {
2023-09-17 12:30:17 +02:00
}
2025-10-13 20:47:43 +02:00
if (stream->eof()) {
return Token{};
2023-09-17 12:30:17 +02:00
}
2025-10-13 20:47:43 +02:00
auto& str = GetTempString();
auto pos = stream->tell();
char character = stream->read();
2023-09-17 12:30:17 +02:00
// If the cursor is at [()=,;$*] we know token consists of single char
if (character == '(' ||
character == ')' ||
character == '=' ||
character == ',' ||
character == ';' ||
character == '$' ||
2025-10-13 20:47:43 +02:00
character == '*')
{
return OperatorTokenPtr(this, pos, character);
2023-09-17 12:30:17 +02:00
}
2025-10-13 20:47:43 +02:00
if (character == '\'') {
2023-09-17 12:30:17 +02:00
// If a string is encountered defer processing to the IfcCharacterDecoder
2025-10-13 20:47:43 +02:00
str = *decoder_;
2024-08-23 20:29:07 +02:00
} else {
2025-10-13 20:47:43 +02:00
str.assign(&character, 1);
while (!stream->eof()) {
// Read character and increment pointer if not starting a new token
character = stream->peek();
if (character == '(' ||
character == ')' ||
character == '=' ||
character == ',' ||
character == ';' ||
character == '/') {
break;
}
if (!(character == ' ' || character == '\r' || character == '\n' || character == '\t')) {
str.push_back(character);
}
2025-10-13 20:47:43 +02:00
stream->increment();
}
2023-09-17 12:30:17 +02:00
}
2025-10-13 20:47:43 +02:00
return GeneralTokenPtr(this, pos, str);
}
//
// Reads a std::string from the file at specified offset
// Omits whitespace and comments
//
2025-10-13 20:47:43 +02:00
void IfcSpfLexer::TokenString(size_t offset, std::string& buffer) {
2023-09-17 12:30:17 +02:00
buffer.clear();
2025-10-13 20:47:43 +02:00
auto local_stream = *this->stream;
local_stream.seek(offset);
while (!local_stream.eof()) {
char character = local_stream.peek();
if (!buffer.empty() && (character == '(' ||
character == ')' ||
character == '=' ||
character == ',' ||
character == ';' ||
character == '/')) {
2023-09-17 12:30:17 +02:00
break;
}
2025-10-13 20:47:43 +02:00
local_stream.increment();
if (character == ' ' ||
character == '\r' ||
character == '\n' ||
character == '\t') {
2023-09-17 12:30:17 +02:00
continue;
}
if (character == '\'') {
2023-09-17 12:30:17 +02:00
// todo, make decoder use local offset ptr
2025-10-13 20:47:43 +02:00
auto offset = local_stream.tell();
buffer = decoder_->get(offset);
2023-09-17 12:30:17 +02:00
break;
}
buffer.push_back(character);
2023-09-17 12:30:17 +02:00
}
}
//Note: according to STEP standard, there may be newlines in tokens
2025-10-13 20:47:43 +02:00
/*
inline void RemoveTokenSeparators(FileReader* stream, size_t start, size_t end, std::string& oDestination) {
2023-09-17 12:30:17 +02:00
oDestination.clear();
for (unsigned i = start; i < end; i++) {
2025-10-13 20:47:43 +02:00
char character = stream->get(i);
if (character == ' ' ||
character == '\r' ||
character == '\n' ||
character == '\t') {
2023-09-17 12:30:17 +02:00
continue;
}
oDestination += character;
2023-09-17 12:30:17 +02:00
}
}
2025-10-13 20:47:43 +02:00
*/
2023-09-17 12:30:17 +02:00
bool ParseInt(const char* pStart, int& val) {
char* pEnd;
long result = strtol(pStart, &pEnd, 10);
if (*pEnd != 0) {
return false;
}
val = (int)result;
return true;
}
2023-09-17 12:30:17 +02:00
bool ParseFloat(const char* pStart, double& val) {
char* pEnd;
#ifdef _MSC_VER
2023-09-17 12:30:17 +02:00
double result = _strtod_l(pStart, &pEnd, locale);
#else
2023-09-17 12:30:17 +02:00
double result = strtod_l(pStart, &pEnd, locale);
#endif
2023-09-17 12:30:17 +02:00
if (*pEnd != 0) {
return false;
}
val = result;
return true;
}
2021-07-29 14:54:51 +02:00
2023-09-17 12:30:17 +02:00
bool ParseBool(const char* pStart, int& val) {
if (strlen(pStart) != 3 || pStart[0] != '.' || pStart[2] != '.') {
return false;
}
char mid = pStart[1];
if (mid == 'T') {
val = 1;
} else if (mid == 'F') {
val = 0;
} else if (mid == 'U') {
val = 2;
} else {
return false;
}
return true;
}
2025-10-13 20:47:43 +02:00
Token IfcParse::OperatorTokenPtr(IfcSpfLexer* lexer, size_t start, char data) {
Token token(lexer, start, Token_OPERATOR);
token.value_char = data;
2023-09-17 12:30:17 +02:00
return token;
}
2025-10-13 20:47:43 +02:00
Token IfcParse::GeneralTokenPtr(IfcSpfLexer* lexer, size_t start, const std::string& tokenStr) {
Token token(lexer, start, Token_NONE);
2023-09-17 12:30:17 +02:00
//determine type of the token
2025-10-13 20:47:43 +02:00
const char& first = tokenStr.front();
2023-09-17 12:30:17 +02:00
if (first == '#') {
token.type = Token_IDENTIFIER;
if (!ParseInt(tokenStr.c_str() + 1, token.value_int)) {
2026-06-11 21:04:44 +02:00
lexer->logger().Message(Logger::LOG_ERROR, "SYN", 11, "Token '" + tokenStr + "' at offset " + std::to_string(token.startPos) + " is not valid");
token.type = Token_OPERATOR;
token.value_char = '$';
2023-09-17 12:30:17 +02:00
}
} else if (first == '\'') {
token.type = Token_STRING;
} else if (first == '.') {
token.type = Token_ENUMERATION;
if (ParseBool(tokenStr.c_str(), token.value_int)) { //bool is also enumeration
token.type = Token_BOOL;
}
} else if (first == '"') {
token.type = Token_BINARY;
} else if (ParseInt(tokenStr.c_str(), token.value_int)) {
token.type = Token_INT;
} else if (ParseFloat(tokenStr.c_str(), token.value_double)) {
token.type = Token_FLOAT;
} else {
token.type = Token_KEYWORD;
}
2023-09-17 12:30:17 +02:00
return token;
}
bool TokenFunc::isOperator(const Token& token) {
return token.type == Token_OPERATOR;
2011-05-08 11:04:32 +00:00
}
bool TokenFunc::isOperator(const Token& token, char character) {
return token.type == Token_OPERATOR && token.value_char == character;
2011-06-13 07:23:09 +00:00
}
bool TokenFunc::isIdentifier(const Token& token) {
return token.type == Token_IDENTIFIER;
2011-05-08 11:04:32 +00:00
}
bool TokenFunc::isString(const Token& token) {
return token.type == Token_STRING;
2011-05-08 11:04:32 +00:00
}
bool TokenFunc::isEnumeration(const Token& token) {
return token.type == Token_ENUMERATION || token.type == Token_BOOL;
2011-05-08 11:04:32 +00:00
}
bool TokenFunc::isBinary(const Token& token) {
return token.type == Token_BINARY;
}
bool TokenFunc::isKeyword(const Token& token) {
return token.type == Token_KEYWORD;
2011-05-08 11:04:32 +00:00
}
bool TokenFunc::isInt(const Token& token) {
return token.type == Token_INT;
}
bool TokenFunc::isBool(const Token& token) {
2023-09-17 12:30:17 +02:00
// Bool and logical share the same storage type, just logical unknown is stored as 2.
return token.type == Token_BOOL && token.value_int != 2;
2021-07-29 14:54:51 +02:00
}
bool TokenFunc::isLogical(const Token& token) {
return token.type == Token_BOOL;
}
bool TokenFunc::isFloat(const Token& token) {
2016-06-12 11:22:08 +02:00
#ifdef PERMISSIVE_FLOAT
2023-09-17 12:30:17 +02:00
/// NB: We are being more permissive here then allowed by the standard
return token.type == Token_FLOAT || token.type == Token_INT;
2016-06-12 11:22:08 +02:00
#else
return token.type == Token_FLOAT;
2016-06-12 11:22:08 +02:00
#endif
}
int TokenFunc::asInt(const Token& token) {
if (token.type != Token_INT) {
throw IfcInvalidTokenException(token.startPos, toString(token), "integer");
2023-09-17 12:30:17 +02:00
}
return token.value_int;
}
int TokenFunc::asIdentifier(const Token& token) {
if (token.type != Token_IDENTIFIER) {
throw IfcInvalidTokenException(token.startPos, toString(token), "instance name");
2023-09-17 12:30:17 +02:00
}
return token.value_int;
2011-06-13 07:23:09 +00:00
}
bool TokenFunc::asBool(const Token& token) {
if (token.type != Token_BOOL) {
throw IfcInvalidTokenException(token.startPos, toString(token), "boolean");
2023-09-17 12:30:17 +02:00
}
return token.value_int == 1;
2021-07-29 14:54:51 +02:00
}
boost::logic::tribool TokenFunc::asLogical(const Token& token) {
if (token.type != Token_BOOL) {
throw IfcInvalidTokenException(token.startPos, toString(token), "boolean");
2023-09-17 12:30:17 +02:00
}
if (token.value_int == 0) {
2023-09-17 12:30:17 +02:00
return false;
}
if (token.value_int == 1) {
2023-09-17 12:30:17 +02:00
return true;
}
return boost::logic::indeterminate;
2011-05-08 11:04:32 +00:00
}
double TokenFunc::asFloat(const Token& token) {
2016-06-12 11:22:08 +02:00
#ifdef PERMISSIVE_FLOAT
if (token.type == Token_INT) {
2023-09-17 12:30:17 +02:00
/// NB: We are being more permissive here then allowed by the standard
return token.value_int;
} // ----> continues beyond preprocessor directive
2016-06-12 11:22:08 +02:00
#endif
if (token.type == Token_FLOAT) {
return token.value_double;
2023-09-17 12:30:17 +02:00
}
throw IfcInvalidTokenException(token.startPos, toString(token), "real");
}
const std::string& TokenFunc::asStringRef(const Token& token) {
if (token.type == Token_NONE) {
throw IfcParse::IfcException("Null token encountered, premature end of file?");
}
std::string& str = token.lexer->GetTempString();
token.lexer->TokenString(token.startPos, str);
// A well-formed string/enumeration/binary token has both delimiters (e.g.
// '...', .XXX., "...."), so at least two characters. Malformed input from a
// fuzzer can produce a single-character token (e.g. a bare '.' left by
// ".)" instead of ".PHYSICAL."); stripping both ends would then erase past
// the end of an already-empty string, which is undefined behaviour and
// aborts under hardened standard libraries (_GLIBCXX_ASSERTIONS). Require
// two characters before stripping. See #5683.
if ((isString(token) || isEnumeration(token) || isBinary(token)) && str.size() >= 2) {
2023-09-17 12:30:17 +02:00
//remove start+end characters in-place
str.erase(str.end() - 1);
str.erase(str.begin());
}
return str;
}
std::string TokenFunc::asString(const Token& token) {
if (isString(token) || isEnumeration(token) || isBinary(token)) {
return asStringRef(token);
2023-09-17 12:30:17 +02:00
}
throw IfcInvalidTokenException(token.startPos, toString(token), "string");
}
boost::dynamic_bitset<> TokenFunc::asBinary(const Token& token) {
const std::string& str = asStringRef(token);
2024-08-23 20:29:07 +02:00
if (str.empty()) {
2023-09-17 12:30:17 +02:00
throw IfcException("Token is not a valid binary sequence");
}
2023-09-17 12:30:17 +02:00
std::string::const_iterator it = str.begin();
int n = *it - '0';
if ((n < 0 || n > 3) || (str.size() == 1 && n != 0)) {
throw IfcException("Token is not a valid binary sequence");
}
++it;
unsigned i = ((unsigned)str.size() - 1) * 4 - n;
boost::dynamic_bitset<> bitset(i);
for (; it != str.end(); ++it) {
const std::string::value_type& c = *it;
int value = (c < 'A') ? (c - '0') : (c - 'A' + 10);
for (unsigned j = 0; j < 4; ++j) {
if (i-- == 0) {
break;
}
if ((value & (1 << (3 - j))) != 0) {
2023-09-17 12:30:17 +02:00
bitset.set(i);
}
}
}
2023-09-17 12:30:17 +02:00
return bitset;
2011-05-08 11:04:32 +00:00
}
std::string TokenFunc::toString(const Token& token) {
2023-09-17 12:30:17 +02:00
std::string result;
2025-10-13 20:47:43 +02:00
if (token.type == Token_OPERATOR) {
result.push_back(token.value_char);
} else if (token.type == Token_INT) {
result = std::to_string(token.value_int);
} else if (token.type == Token_BOOL) {
if (token.value_int == 1) {
result = ".T.";
} else if (token.value_int == 0) {
result = ".F.";
} else {
result = ".U.";
}
} else if (token.type == Token_FLOAT) {
std::ostringstream oss;
oss << std::setprecision(15) << token.value_double;
result = oss.str();
} else {
token.lexer->TokenString(token.startPos, result);
}
2023-09-17 12:30:17 +02:00
return result;
}
2023-09-17 12:30:17 +02:00
//
// Reads the arguments from a list of token
2017-06-05 17:26:58 +02:00
// Aditionally, registers the ids (i.e. #[\d]+) in the inverse map
//
2025-10-13 20:47:43 +02:00
void IfcParse::impl::in_memory_file_storage::load(boost::optional<size_t> entity_instance_name, const IfcParse::entity* entity, parse_context& context, int attribute_index) {
2023-09-17 12:30:17 +02:00
Token next = tokens->Next();
2024-08-23 20:29:07 +02:00
/*
if (TokenFunc::isOperator(next, '(')) {
next = tokens->Next();
2023-09-17 12:30:17 +02:00
}
2024-08-23 20:29:07 +02:00
*/
2024-08-23 20:29:07 +02:00
size_t attribute_index_within_data = 0;
2023-09-17 12:30:17 +02:00
size_t return_value = 0;
while ((next.startPos != 0U) || (next.lexer != nullptr)) {
2023-09-17 12:30:17 +02:00
if (TokenFunc::isOperator(next, ',')) {
2024-08-23 20:29:07 +02:00
if (attribute_index == -1) {
attribute_index_within_data += 1;
}
2023-09-17 12:30:17 +02:00
} else if (TokenFunc::isOperator(next, ')')) {
break;
} else if (TokenFunc::isOperator(next, '(')) {
return_value++;
2024-08-23 20:29:07 +02:00
load(entity_instance_name, entity, context.push(), attribute_index == -1 ? (int) attribute_index_within_data : attribute_index);
2023-09-17 12:30:17 +02:00
} else {
return_value++;
2025-10-13 20:47:43 +02:00
if (TokenFunc::isIdentifier(next) && entity && entity_instance_name) {
register_inverse(*entity_instance_name, entity, next.value_int, attribute_index == -1 ? (int) attribute_index_within_data : attribute_index);
2023-09-17 12:30:17 +02:00
}
if (TokenFunc::isKeyword(next)) {
try {
const auto* decl = (schema ? schema : file->schema())->declaration_by_name(TokenFunc::asStringRef(next));
2024-08-23 20:29:07 +02:00
parse_context ps;
tokens->Next();
// The only case we know where a defined type contains entity
// instance references is IfcPropertySetDefinitionSet. For
// that purpose we propagate the entity_instance_name to
// register inverses to the host entity (and not the defined
// type) and to be able to actually register the references in
// the 2nd pass.
load(entity_instance_name, entity, ps, attribute_index == -1 ? (int)attribute_index_within_data : attribute_index);
2026-06-11 21:04:44 +02:00
auto* simple_type_instance = (schema ? schema : file->schema())->instantiate(decl, ps.construct(entity_instance_name, *references_to_resolve, decl, boost::none, attribute_index == -1 ? (int)attribute_index_within_data : attribute_index, logger()));
2025-10-08 14:17:47 +02:00
read_simple_type_instances.emplace_back(simple_type_instance);
2024-08-23 20:29:07 +02:00
//@todo decide addEntity(((IfcUtil::IfcBaseClass*)*entity));
context.push(simple_type_instance);
simple_type_instance->file_ = file;
2023-09-17 12:30:17 +02:00
} catch (IfcException& e) {
2026-06-11 21:04:44 +02:00
logger().Message(Logger::LOG_ERROR, "SYN", 12, std::string(e.what()) + " at offset " + std::to_string(next.startPos));
// #4070 We didn't actually capture an aggregate entry, undo length increment.
return_value--;
2023-09-17 12:30:17 +02:00
}
} else {
2024-08-23 20:29:07 +02:00
context.push(next);
2023-09-17 12:30:17 +02:00
}
}
next = tokens->Next();
}
}
//
// Reads an Entity from the list of Tokens at the specified offset in the file
//
IfcEntityInstanceData IfcParse::impl::in_memory_file_storage::read(unsigned int i) {
Token datatype = tokens->Next();
2023-09-17 12:30:17 +02:00
if (!TokenFunc::isKeyword(datatype)) {
throw IfcException("Unexpected token while parsing entity");
}
const IfcParse::declaration* ty = file->schema()->declaration_by_name(TokenFunc::asStringRef(datatype));
2024-08-23 20:29:07 +02:00
parse_context pc;
tokens->Next();
load(i, ty->as_entity(), pc, -1);
2026-06-11 21:04:44 +02:00
return IfcEntityInstanceData(pc.construct(i, *references_to_resolve, ty, boost::none, -1, logger()));
2011-05-08 11:04:32 +00:00
}
void IfcParse::impl::in_memory_file_storage::try_read_semicolon() const {
2025-10-13 20:47:43 +02:00
auto old_offset = tokens->stream->tell();
2023-09-17 12:30:17 +02:00
Token semilocon = tokens->Next();
if (!TokenFunc::isOperator(semilocon, ';')) {
2025-10-13 20:47:43 +02:00
tokens->stream->seek(old_offset);
2023-09-17 12:30:17 +02:00
}
2011-05-08 11:04:32 +00:00
}
void IfcParse::impl::in_memory_file_storage::register_inverse(unsigned id_from, const IfcParse::entity* from_entity, int inst_id, int attribute_index) {
2023-09-17 12:30:17 +02:00
// Assume a check on token type has already been performed
byref_excl_[{inst_id, from_entity->index_in_schema(), attribute_index}].push_back(id_from);
2011-05-08 11:04:32 +00:00
}
void IfcParse::impl::in_memory_file_storage::unregister_inverse(unsigned id_from, const IfcParse::entity* from_entity, IfcUtil::IfcBaseClass* inst, int attribute_index) {
auto& ids = byref_excl_[{inst->id(), from_entity->index_in_schema(), attribute_index}];
auto iter = std::find(ids.begin(), ids.end(), id_from);
if (iter == ids.end()) {
2023-09-17 12:30:17 +02:00
// @todo inverses also need to be populated when multiple instances are added to a new file.
// throw IfcParse::IfcException("Instance not found among inverses");
} else {
ids.erase(iter);
2023-09-17 12:30:17 +02:00
}
2017-07-03 14:18:56 +02:00
}
namespace {
template <typename T>
2025-10-13 20:47:43 +02:00
std::string to_string_fixed_width(const T& t, size_t) {
// @todo currently inactive
std::ostringstream oss;
oss << /*std::setfill('0') << std::setw(w) <<*/ t;
return oss.str();
}
}
void IfcParse::impl::rocks_db_file_storage::register_inverse(unsigned id_from, const IfcParse::entity* from_entity, int inst_id, int attribute_index) {
#ifdef IFOPSH_WITH_ROCKSDB
static std::string s;
uint32_t v = id_from;
s.resize(sizeof(uint32_t));
memcpy(s.data(), &v, sizeof(uint32_t));
2025-02-27 22:07:31 +01:00
auto key = "v|" + to_string_fixed_width(inst_id, 10) + "|" + to_string_fixed_width(from_entity->index_in_schema(), 4) + "|" + to_string_fixed_width(attribute_index, 2);
db->Merge(wopts, key, s);
2025-02-27 22:07:31 +01:00
/*
// Python client does not support merges
// @todo turn this into a setting
2025-02-27 22:07:31 +01:00
{
std::string current;
db->Get(rocksdb::ReadOptions{}, key, &current);
auto new_val = current + s;
db->Put(wopts, key, new_val);
}*/
#endif
}
void IfcParse::impl::rocks_db_file_storage::unregister_inverse(unsigned id_from, const IfcParse::entity* from_entity, IfcUtil::IfcBaseClass* inst, int attribute_index) {
#ifdef IFOPSH_WITH_ROCKSDB
static std::string s;
auto inst_id = inst->id();
auto key = "v|" + to_string_fixed_width(inst_id, 10) + "|" + to_string_fixed_width(from_entity->index_in_schema(), 4) + "|" + to_string_fixed_width(attribute_index, 2);
if (db->Get(rocksdb::ReadOptions{}, key, &s).ok()) {
std::vector<uint32_t> vals(s.size() / sizeof(uint32_t));
memcpy(vals.data(), s.data(), s.size());
auto it = std::find(vals.begin(), vals.end(), (uint32_t)id_from);
if (it != vals.end()) {
vals.erase(it);
} else {
2026-06-11 21:04:44 +02:00
file->logger().Error("VAL", 17, "Unregistering non-existant inverse #" + std::to_string(id_from) + " on instance #" + std::to_string(inst_id) + " at attribute " + std::to_string(attribute_index));
}
s.resize(vals.size() * sizeof(uint32_t));
memcpy(s.data(), vals.data(), s.size());
db->Put(wopts, key, s);
}
#endif
}
2025-02-24 20:46:29 +01:00
void IfcParse::impl::rocks_db_file_storage::add_type_ref(IfcUtil::IfcBaseClass* new_entity)
{
#ifdef IFOPSH_WITH_ROCKSDB
size_t v;
2025-02-24 20:46:29 +01:00
std::string s(sizeof(size_t), ' ');
2025-02-27 22:07:31 +01:00
if (new_entity->declaration().as_entity()) {
v = new_entity->id();
memcpy(s.data(), &v, sizeof(size_t));
// no merges yet, because the python client doesn't support them
2025-09-10 09:10:32 +02:00
db->Merge(wopts, "t|" + std::to_string(new_entity->declaration().index_in_schema()), s);
/*{
std::string current;
// @todo this uses the same key-namespace as typedecl instances, not a direct conflict, but also not very clear
auto key = "t|" + std::to_string(new_entity->declaration().index_in_schema());
db->Get(rocksdb::ReadOptions{}, key, &current);
auto new_val = current + s;
db->Put(wopts, key, new_val);
}*/
2025-02-27 22:07:31 +01:00
}
// not only mapping also register type
v = new_entity->declaration().index_in_schema();
memcpy(s.data(), &v, sizeof(size_t));
db->Put(wopts, (new_entity->declaration().as_entity() ? "i|" : "t|") + std::to_string(new_entity->id() ? new_entity->id() : new_entity->identity()) + "|_", s);
#endif
2025-02-24 20:46:29 +01:00
}
void IfcParse::impl::rocks_db_file_storage::remove_type_ref(IfcUtil::IfcBaseClass* new_entity)
{
#ifdef IFOPSH_WITH_ROCKSDB
if (new_entity->declaration().as_entity()) {
std::string s;
auto key = "t|" + std::to_string(new_entity->declaration().index_in_schema());
if (db->Get(rocksdb::ReadOptions{}, key, &s).ok()) {
std::vector<size_t> vals(s.size() / sizeof(size_t));
memcpy(vals.data(), s.data(), s.size());
vals.erase(std::find(vals.begin(), vals.end(), (size_t)new_entity->id()));
s.resize(vals.size() * sizeof(size_t));
memcpy(s.data(), vals.data(), s.size());
db->Put(wopts, key, s);
}
2025-02-24 20:46:29 +01:00
}
2025-02-27 22:07:31 +01:00
db->Delete(wopts, (new_entity->declaration().as_entity() ? "i|" : "t|") + std::to_string(new_entity->id() ? new_entity->id() : new_entity->identity()) + "|_");
#endif
2025-02-24 20:46:29 +01:00
}
2024-08-23 20:29:07 +02:00
namespace {
class StringBuilderVisitor : public boost::static_visitor<void> {
private:
StringBuilderVisitor(const StringBuilderVisitor&); //N/A
StringBuilderVisitor& operator=(const StringBuilderVisitor&); //N/A
std::ostream& data_;
template <typename T>
void serialize(const std::vector<T>& i) {
data_ << "(";
for (typename std::vector<T>::const_iterator it = i.begin(); it != i.end(); ++it) {
if (it != i.begin()) {
data_ << ",";
}
data_ << *it;
}
data_ << ")";
}
// The REAL token definition from the IFC SPF standard does not necessarily match
// the output of the C++ ostream formatting operation.
// REAL = [ SIGN ] DIGIT { DIGIT } "." { DIGIT } [ "E" [ SIGN ] DIGIT { DIGIT } ] .
static std::string format_double(const double& d) {
// Use the shortest representation that round-trips exactly (like
// Python's repr) instead of max_digits10. max_digits10 padded clean
// values with noise digits (0.0174532925199433 -> 0.017453292519943299),
// which rewrote every REAL and produced huge diffs when a file was
// re-saved. See #7696.
// std::to_chars is locale-independent, so no ostringstream/imbue is
// needed here.
char buf[64];
const auto res = std::to_chars(buf, buf + sizeof(buf), d);
const std::string str(buf, res.ptr);
2024-08-23 20:29:07 +02:00
std::string::size_type e = str.find('e');
if (e == std::string::npos) {
e = str.find('E');
}
std::string result = str.substr(0, e);
if (result.find('.') == std::string::npos) {
result += '.';
2024-08-23 20:29:07 +02:00
}
if (e != std::string::npos) {
result += 'E';
result += str.substr(e + 1);
2024-08-23 20:29:07 +02:00
}
return result;
2024-08-23 20:29:07 +02:00
}
static std::string format_binary(const boost::dynamic_bitset<>& b) {
std::ostringstream oss;
oss.imbue(std::locale::classic());
oss.put('"');
oss << std::uppercase << std::hex << std::setw(1);
unsigned c = (unsigned)b.size();
unsigned n = (4 - (c % 4)) & 3;
oss << n;
for (unsigned i = 0; i < c + n;) {
unsigned accum = 0;
for (int j = 0; j < 4; ++j, ++i) {
unsigned bit = i < n ? 0 : b.test(c - i + n - 1) ? 1
: 0;
accum |= bit << (3 - j);
}
oss << accum;
}
oss.put('"');
return oss.str();
}
bool upper_;
public:
StringBuilderVisitor(std::ostream& stream, bool upper = false)
: data_(stream),
upper_(upper) {}
void operator()(const Blank& /*i*/) { data_ << "$"; }
void operator()(const Derived& /*i*/) { data_ << "*"; }
void operator()(const int& i) { data_ << i; }
void operator()(const bool& i) { data_ << (i ? ".T." : ".F."); }
void operator()(const boost::logic::tribool& i) { data_ << (i ? ".T." : (boost::logic::indeterminate(i) ? ".U." : ".F.")); }
void operator()(const double& i) { data_ << format_double(i); }
void operator()(const boost::dynamic_bitset<>& i) { data_ << format_binary(i); }
void operator()(const std::string& i) {
std::string s = i;
if (upper_) {
data_ << static_cast<std::string>(IfcCharacterEncoder(s));
} else {
data_ << '\'' << s << '\'';
}
}
void operator()(const std::vector<int>& i);
void operator()(const std::vector<double>& i);
void operator()(const std::vector<std::string>& i);
void operator()(const std::vector<boost::dynamic_bitset<>>& i);
void operator()(const EnumerationReference& i) {
data_ << "." << i.value() << ".";
}
void operator()(const IfcUtil::IfcBaseClass* const& i) {
2025-08-28 11:15:18 +02:00
if (i->declaration().as_entity() == nullptr || i->declaration().schema() == &Header_section_schema::get_schema()) {
2024-08-23 20:29:07 +02:00
i->toString(data_, upper_);
} else {
data_ << "#" << i->id();
}
}
void operator()(const aggregate_of_instance::ptr& i) {
data_ << "(";
for (aggregate_of_instance::it it = i->begin(); it != i->end(); ++it) {
if (it != i->begin()) {
data_ << ",";
}
(*this)(*it);
}
data_ << ")";
}
void operator()(const std::vector<std::vector<int>>& i);
void operator()(const std::vector<std::vector<double>>& i);
void operator()(const aggregate_of_aggregate_of_instance::ptr& i) {
data_ << "(";
for (aggregate_of_aggregate_of_instance::outer_it outer_it = i->begin(); outer_it != i->end(); ++outer_it) {
if (outer_it != i->begin()) {
data_ << ",";
}
data_ << "(";
for (aggregate_of_aggregate_of_instance::inner_it inner_it = outer_it->begin(); inner_it != outer_it->end(); ++inner_it) {
if (inner_it != outer_it->begin()) {
data_ << ",";
}
(*this)(*inner_it);
}
data_ << ")";
}
data_ << ")";
}
void operator()(const empty_aggregate_t& /*unused*/) const { data_ << "()"; }
void operator()(const empty_aggregate_of_aggregate_t& /*unused*/) const { data_ << "()"; }
};
template <>
void StringBuilderVisitor::serialize(const std::vector<std::string>& i) {
data_ << "(";
for (std::vector<std::string>::const_iterator it = i.begin(); it != i.end(); ++it) {
if (it != i.begin()) {
data_ << ",";
}
std::string encoder = IfcCharacterEncoder(*it);
data_ << encoder;
}
data_ << ")";
2023-09-17 12:30:17 +02:00
}
2015-01-25 10:50:12 +00:00
2024-08-23 20:29:07 +02:00
template <>
void StringBuilderVisitor::serialize(const std::vector<double>& i) {
data_ << "(";
for (std::vector<double>::const_iterator it = i.begin(); it != i.end(); ++it) {
if (it != i.begin()) {
data_ << ",";
}
data_ << format_double(*it);
}
data_ << ")";
}
2015-01-25 10:50:12 +00:00
2024-08-23 20:29:07 +02:00
template <>
void StringBuilderVisitor::serialize(const std::vector<boost::dynamic_bitset<>>& i) {
data_ << "(";
for (std::vector<boost::dynamic_bitset<>>::const_iterator it = i.begin(); it != i.end(); ++it) {
if (it != i.begin()) {
data_ << ",";
}
data_ << format_binary(*it);
2023-09-17 12:30:17 +02:00
}
2024-08-23 20:29:07 +02:00
data_ << ")";
}
2023-09-17 12:30:17 +02:00
2024-08-23 20:29:07 +02:00
void StringBuilderVisitor::operator()(const std::vector<int>& i) { serialize(i); }
void StringBuilderVisitor::operator()(const std::vector<double>& i) { serialize(i); }
void StringBuilderVisitor::operator()(const std::vector<std::string>& i) { serialize(i); }
void StringBuilderVisitor::operator()(const std::vector<boost::dynamic_bitset<>>& i) { serialize(i); }
void StringBuilderVisitor::operator()(const std::vector<std::vector<int>>& i) {
data_ << "(";
for (std::vector<std::vector<int>>::const_iterator it = i.begin(); it != i.end(); ++it) {
if (it != i.begin()) {
data_ << ",";
}
serialize(*it);
}
data_ << ")";
}
void StringBuilderVisitor::operator()(const std::vector<std::vector<double>>& i) {
data_ << "(";
for (std::vector<std::vector<double>>::const_iterator it = i.begin(); it != i.end(); ++it) {
if (it != i.begin()) {
data_ << ",";
}
serialize(*it);
2023-09-17 12:30:17 +02:00
}
2024-08-23 20:29:07 +02:00
data_ << ")";
2023-09-17 12:30:17 +02:00
}
2024-08-23 20:29:07 +02:00
}
2023-09-17 12:30:17 +02:00
2024-08-23 20:29:07 +02:00
//
// Returns a string representation of the entity
// Note that this initializes the entity if it is not initialized
//
2025-02-27 22:07:31 +01:00
void IfcEntityInstanceData::toString(void* storage, const IfcParse::declaration* decl, std::size_t identity, std::ostream& ss, bool upper) const {
2024-08-23 20:29:07 +02:00
ss.imbue(std::locale::classic());
2023-09-17 12:30:17 +02:00
2024-08-23 20:29:07 +02:00
ss << "(";
StringBuilderVisitor vis(ss, upper);
2025-08-31 15:52:46 +02:00
// In almost all cases, storage is initialized with the size of the schema declaration,
// apparently except in case of header entities and invalid in-line type declarations.
auto size = (decl && decl->as_entity() ? decl->as_entity()->attribute_count() : 1);
if (storage_) {
size = (std::min)(size, storage_->size());
}
for (size_t i = 0; i < size; ++i) {
2023-09-17 12:30:17 +02:00
if (i != 0) {
ss << ",";
}
2025-02-27 22:07:31 +01:00
if (has_attribute_value<Blank>(storage, decl, identity, i)) {
if (decl != nullptr && decl->as_entity() && decl->as_entity()->derived()[i]) {
2024-07-10 12:43:01 -07:00
ss << "*";
2024-07-17 09:45:34 +02:00
} else {
2024-07-10 12:43:01 -07:00
ss << "$";
2024-08-23 20:29:07 +02:00
}
2023-09-17 12:30:17 +02:00
} else {
2025-02-27 22:07:31 +01:00
apply_visitor(storage, decl, identity, vis, i);
2023-09-17 12:30:17 +02:00
}
}
ss << ")";
2011-05-08 11:04:32 +00:00
}
2024-08-23 20:29:07 +02:00
unsigned IfcUtil::IfcBaseEntity::set_id(const boost::optional<unsigned>& i) {
2023-09-17 12:30:17 +02:00
if (i) {
return id_ = *i;
}
2024-08-23 20:29:07 +02:00
return id_ = file_->FreshId();
2017-06-05 17:26:58 +02:00
}
namespace {
2023-09-17 12:30:17 +02:00
// @todo remove redundancy with python wrapper code (which is not identical due to
// different handling of enumerations)
IfcUtil::ArgumentType get_argument_type(const IfcParse::declaration* decl, size_t i) {
const IfcParse::parameter_type* pt = 0;
if (decl->as_entity() != nullptr) {
2023-09-17 12:30:17 +02:00
pt = decl->as_entity()->attribute_by_index(i)->type_of_attribute();
if (decl->as_entity()->derived()[i]) {
return IfcUtil::Argument_DERIVED;
}
} else if ((decl->as_type_declaration() != nullptr) && i == 0) {
2023-09-17 12:30:17 +02:00
pt = decl->as_type_declaration()->declared_type();
} else if ((decl->as_enumeration_type() != nullptr) && i == 0) {
2023-09-17 12:30:17 +02:00
return IfcUtil::Argument_ENUMERATION;
}
if (pt == 0) {
return IfcUtil::Argument_UNKNOWN;
}
return IfcUtil::from_parameter_type(pt);
}
2023-09-17 12:30:17 +02:00
} // namespace
2017-07-03 14:18:56 +02:00
class unregister_inverse_visitor {
2023-09-17 12:30:17 +02:00
private:
IfcFile& file_;
2024-08-23 20:29:07 +02:00
const IfcUtil::IfcBaseClass* data_;
2017-07-03 14:18:56 +02:00
2023-09-17 12:30:17 +02:00
public:
2024-08-23 20:29:07 +02:00
unregister_inverse_visitor(IfcFile& file, const IfcUtil::IfcBaseClass* data)
2023-09-17 12:30:17 +02:00
: file_(file),
data_(data) {}
2017-07-03 14:18:56 +02:00
2023-09-17 12:30:17 +02:00
void operator()(IfcUtil::IfcBaseClass* inst, int index) {
2024-08-23 20:29:07 +02:00
file_.unregister_inverse(data_->id(), data_->declaration().as_entity(), inst, index);
2023-09-17 12:30:17 +02:00
}
2017-07-03 14:18:56 +02:00
};
class register_inverse_visitor {
2023-09-17 12:30:17 +02:00
private:
IfcFile& file_;
2024-08-23 20:29:07 +02:00
const IfcUtil::IfcBaseClass* data_;
2017-07-03 14:18:56 +02:00
2023-09-17 12:30:17 +02:00
public:
2024-08-23 20:29:07 +02:00
register_inverse_visitor(IfcFile& file, const IfcUtil::IfcBaseClass* data)
2023-09-17 12:30:17 +02:00
: file_(file),
data_(data) {}
2017-07-03 14:18:56 +02:00
2023-09-17 12:30:17 +02:00
void operator()(IfcUtil::IfcBaseClass* inst, int index) {
file_.register_inverse(data_->id(), data_->declaration().as_entity(), inst->id(), index);
2023-09-17 12:30:17 +02:00
}
2017-07-03 14:18:56 +02:00
};
class add_to_instance_list_visitor {
2023-09-17 12:30:17 +02:00
private:
aggregate_of_instance::ptr& list_;
2017-07-03 14:18:56 +02:00
2023-09-17 12:30:17 +02:00
public:
add_to_instance_list_visitor(aggregate_of_instance::ptr& list)
: list_(list) {}
2017-07-03 14:18:56 +02:00
2023-09-17 12:30:17 +02:00
void operator()(IfcUtil::IfcBaseClass* inst) {
list_->push(inst);
}
2017-07-03 14:18:56 +02:00
};
class apply_individual_instance_visitor {
2023-09-17 12:30:17 +02:00
private:
2024-08-23 20:29:07 +02:00
boost::optional<AttributeValue> attribute_;
2023-09-17 12:30:17 +02:00
int attribute_index_;
2025-02-27 22:07:31 +01:00
const IfcUtil::IfcBaseClass* inst_;
2023-09-17 12:30:17 +02:00
template <typename T>
2024-08-23 20:29:07 +02:00
void apply_attribute_(T& t, const AttributeValue& attr, int index) const {
switch (attr.type()) {
case IfcUtil::Argument_ENTITY_INSTANCE: {
2024-08-23 20:29:07 +02:00
IfcUtil::IfcBaseClass* inst = attr;
2023-09-17 12:30:17 +02:00
t(inst, index);
break;
}
case IfcUtil::Argument_AGGREGATE_OF_ENTITY_INSTANCE: {
2024-08-23 20:29:07 +02:00
aggregate_of_instance::ptr entity_list_attribute = attr;
2023-09-17 12:30:17 +02:00
for (aggregate_of_instance::it it = entity_list_attribute->begin(); it != entity_list_attribute->end(); ++it) {
t(*it, index);
}
break;
}
case IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE: {
2024-08-23 20:29:07 +02:00
aggregate_of_aggregate_of_instance::ptr entity_list_attribute = attr;
2023-09-17 12:30:17 +02:00
for (aggregate_of_aggregate_of_instance::outer_it it = entity_list_attribute->begin(); it != entity_list_attribute->end(); ++it) {
for (aggregate_of_aggregate_of_instance::inner_it jt = it->begin(); jt != it->end(); ++jt) {
t(*jt, index);
}
}
break;
2023-09-17 12:30:17 +02:00
}
default:
break;
}
}
2023-09-17 12:30:17 +02:00
public:
2024-08-23 20:29:07 +02:00
apply_individual_instance_visitor(const AttributeValue& attribute, int idx)
: attribute_(attribute)
, attribute_index_(idx)
{}
2023-09-17 12:30:17 +02:00
2025-02-27 22:07:31 +01:00
apply_individual_instance_visitor(const IfcUtil::IfcBaseClass* data)
: inst_(data)
2024-08-23 20:29:07 +02:00
{}
2023-09-17 12:30:17 +02:00
template <typename T>
void apply(T& t) const {
if (attribute_) {
2024-08-23 20:29:07 +02:00
apply_attribute_(t, *attribute_, attribute_index_);
2023-09-17 12:30:17 +02:00
} else {
2025-02-27 22:07:31 +01:00
const auto& decl = inst_->declaration();
for (size_t i = 0; i < (decl.as_entity() ? decl.as_entity()->attribute_count() : 1); ++i) {
auto attr = inst_->get_attribute_value(i);
2024-08-23 20:29:07 +02:00
apply_attribute_(t, attr, (int) i);
2023-09-17 12:30:17 +02:00
}
}
};
2017-07-03 14:18:56 +02:00
};
2024-08-23 20:29:07 +02:00
template <typename T>
2025-08-26 10:19:47 +02:00
typename std::enable_if<
(!(std::is_pointer<T>::value&& std::is_base_of<IfcUtil::IfcBaseClass, typename std::remove_pointer<T>::type>::value) || std::is_same_v<IfcUtil::IfcBaseClass, std::remove_pointer_t<T>>),
void>::type
IfcUtil::IfcBaseClass::set_attribute_value(size_t i, const T& t) {
2025-03-25 10:15:24 +01:00
if constexpr (std::is_same_v<std::decay_t<T>, double>) {
if (!std::isfinite(t)) {
throw IfcParse::IfcException("Only finite values are allowed");
}
}
if constexpr (std::is_same_v<std::decay_t<T>, std::vector<double>>) {
if (std::any_of(t.begin(), t.end(), [](double d) { return !std::isfinite(d); })) {
throw IfcParse::IfcException("Only finite values are allowed");
}
}
if constexpr (std::is_same_v<std::decay_t<T>, std::vector<std::vector<double>>>) {
for (auto& tt : t) {
if (std::any_of(tt.begin(), tt.end(), [](double d) { return !std::isfinite(d); })) {
throw IfcParse::IfcException("Only finite values are allowed");
}
}
}
2025-02-27 22:07:31 +01:00
auto current_attribute = get_attribute_value(i);
2024-08-23 20:29:07 +02:00
if (file_ != nullptr) {
2023-09-17 12:30:17 +02:00
2024-08-23 20:29:07 +02:00
// Deregister old attribute guid in file guid map.
if (i == 0 && (file_->ifcroot_type() != nullptr) && this->declaration().is(*file_->ifcroot_type())) {
try {
auto guid = (std::string) current_attribute;
auto it = file_->internal_guid_map().find(guid);
2025-02-27 22:07:31 +01:00
if (it != file_->internal_guid_map().end()) {
const std::pair<const std::string, IfcUtil::IfcBaseClass*>& p = *it;
if (p.second == this) {
file_->internal_guid_map().erase(it);
}
2023-09-17 12:30:17 +02:00
}
2024-08-23 20:29:07 +02:00
} catch (IfcParse::IfcException& e) {
2026-06-11 21:04:44 +02:00
file_->logger().Error("SYN", 13, e);
2023-09-17 12:30:17 +02:00
}
}
2017-07-03 14:18:56 +02:00
if constexpr (std::is_same_v<T, IfcUtil::IfcBaseClass*> || std::is_same_v<T, aggregate_of_instance::ptr> || std::is_same_v<T, aggregate_of_aggregate_of_instance::ptr> || std::is_same_v<T, Blank>) {
// Deregister inverse indices in file
unregister_inverse_visitor visitor(*file_, this);
apply_individual_instance_visitor(current_attribute, (int)i).apply(visitor);
}
2023-09-17 12:30:17 +02:00
}
2025-02-27 22:07:31 +01:00
{
void* const storage = file_ ? std::visit([](const auto& m) { return (void*)&m; }, file_->storage_) : nullptr;
if constexpr (std::is_pointer_v<T>) {
if (t) {
data_.set_attribute_value(storage, &declaration(), id() ? id() : identity(), i, t);
2025-02-27 22:07:31 +01:00
} else {
data_.set_attribute_value(storage, &declaration(), id() ? id() : identity(), i, Blank{});
2025-02-27 22:07:31 +01:00
}
} else {
data_.set_attribute_value(storage, &declaration(), id() ? id() : identity(),i, t);
}
}
2025-02-27 22:07:31 +01:00
auto new_attribute = get_attribute_value(i);
2023-09-17 12:30:17 +02:00
2024-08-23 20:29:07 +02:00
if (file_ != nullptr) {
2023-09-17 12:30:17 +02:00
// Register inverse indices in file
if constexpr (std::is_same_v<T, IfcUtil::IfcBaseClass*> || std::is_same_v<T, aggregate_of_instance::ptr> || std::is_same_v<T, aggregate_of_aggregate_of_instance::ptr>) {
register_inverse_visitor visitor(*file_, this);
apply_individual_instance_visitor(new_attribute, (int)i).apply(visitor);
}
2024-08-23 20:29:07 +02:00
// Register new attribute guid in guid map
if (i == 0 && (file_->ifcroot_type() != nullptr) && this->declaration().is(*file_->ifcroot_type())) {
2023-09-17 12:30:17 +02:00
try {
2024-08-23 20:29:07 +02:00
auto guid = (std::string) new_attribute;
auto it = file_->internal_guid_map().find(guid);
if (it != file_->internal_guid_map().end()) {
2026-06-11 21:04:44 +02:00
file_->logger().Warning("VAL", 18, "Duplicate guid " + guid);
2023-09-17 12:30:17 +02:00
}
2025-02-27 22:07:31 +01:00
file_->internal_guid_map().insert({ guid, this });
2023-09-17 12:30:17 +02:00
} catch (IfcParse::IfcException& e) {
2026-06-11 21:04:44 +02:00
file_->logger().Error("SYN", 14, e);
2023-09-17 12:30:17 +02:00
}
}
}
2017-06-05 17:26:58 +02:00
}
2024-08-23 20:29:07 +02:00
template <typename T>
2025-08-26 10:19:47 +02:00
typename std::enable_if<
(!(std::is_pointer<T>::value&& std::is_base_of<IfcUtil::IfcBaseClass, typename std::remove_pointer<T>::type>::value) || std::is_same_v<IfcUtil::IfcBaseClass, std::remove_pointer_t<T>>),
void>::type
IfcUtil::IfcBaseClass::set_attribute_value(const std::string& s, const T& t) {
2024-08-23 20:29:07 +02:00
set_attribute_value(declaration().as_entity()->attribute_index(s), t);
}
//
// Parses the IFC file in fn
// Creates the maps
//
2017-07-23 14:52:04 +02:00
#ifdef USE_MMAP
2026-06-11 21:04:44 +02:00
IfcFile::IfcFile(const std::string& fn, bool mmap, Logger& logger)
: logger_(logger)
, schema_(nullptr)
, ifcroot_type_(nullptr)
, max_id_(0)
, _header(this, logger)
{
initialize(fn, mmap);
}
bool IfcParse::IfcFile::initialize(const std::string& fn, bool mmap) {
std::unique_ptr<FileReader> s;
2025-10-13 20:47:43 +02:00
if (mmap) {
s = std::make_unique<FileReader>(fn, FileReader::mmap_tag{});
} else {
s = std::make_unique<FileReader>(fn);
}
2025-10-13 20:47:43 +02:00
2026-06-11 21:04:44 +02:00
storage_.emplace<1>(this, logger_.get());
std::get<impl::in_memory_file_storage>(storage_).read_from_stream(&*s, schema_, max_id_, types_to_bypass_loading_);
2025-10-13 20:47:43 +02:00
if ((good_ = std::get<impl::in_memory_file_storage>(storage_).good_)) {
// @todo unify these names, it's already confusing enough as it stands
byid_ = decltype(byid_)(&std::get<impl::in_memory_file_storage>(storage_).byid_);
byref_excl_ = decltype(byref_excl_)(&std::get<impl::in_memory_file_storage>(storage_).byref_excl_);
byguid_ = decltype(byguid_)(&std::get<impl::in_memory_file_storage>(storage_).byguid_);
}
ifcroot_type_ = schema_ ? schema_->declaration_by_name("IfcRoot") : nullptr;
2025-10-24 13:32:18 +02:00
return good_ == file_open_status::SUCCESS;
2017-07-23 14:52:04 +02:00
}
2025-10-13 20:47:43 +02:00
#endif
2026-06-11 21:04:44 +02:00
IfcFile::IfcFile(const uninitialized_tag&, Logger& logger)
: logger_(logger), schema_(nullptr), ifcroot_type_(nullptr), max_id_(0), _header(this, logger), good_(file_open_status::UNKNOWN) {}
bool IfcParse::IfcFile::initialize(const std::string& path, filetype ty, bool readonly) {
2025-03-13 13:09:39 +01:00
if (ty == FT_AUTODETECT) {
ty = guess_file_type(path);
}
if (ty == FT_IFCSPF) {
2025-10-13 20:47:43 +02:00
FileReader s(path);
2026-06-11 21:04:44 +02:00
storage_.emplace<1>(this, logger_.get());
std::get<impl::in_memory_file_storage>(storage_).read_from_stream(&s, schema_, max_id_, types_to_bypass_loading_);
2025-02-27 22:07:31 +01:00
2025-10-13 20:47:43 +02:00
if ((good_ = std::get<impl::in_memory_file_storage>(storage_).good_)) {
// @todo unify these names, it's already confusing enough as it stands
byid_ = decltype(byid_)(&std::get<impl::in_memory_file_storage>(storage_).byid_);
byref_excl_ = decltype(byref_excl_)(&std::get<impl::in_memory_file_storage>(storage_).byref_excl_);
byguid_ = decltype(byguid_)(&std::get<impl::in_memory_file_storage>(storage_).byguid_);
}
// byidentity_ = decltype(byidentity_)(&std::get<impl::in_memory_file_storage>(storage_).byidentity_);
2025-03-13 13:09:39 +01:00
} else if (ty == FT_ROCKSDB) {
// This would make some difference, but in the greater light of things, not really significant
// LateBoundEntity is also still large per instance
// instantiate_typed_instances = false;
2025-02-25 21:29:54 +01:00
// @todo this can only be used for databases that already exist, because otherwise there is no way to specify the schema
storage_.emplace<2>(path, this, readonly);
if (std::get<impl::rocks_db_file_storage>(storage_).db == nullptr) {
storage_.emplace<0>();
good_ = file_open_status::READ_ERROR;
} else {
if (std::get<impl::rocks_db_file_storage>(storage_).read_schema(schema_)) {
byid_ = decltype(byid_)(&std::get<impl::rocks_db_file_storage>(storage_).instance_by_name_);
byref_excl_ = decltype(byref_excl_)(&std::get<impl::rocks_db_file_storage>(storage_).byref_excl_);
byguid_ = decltype(byguid_)(&std::get<impl::rocks_db_file_storage>(storage_).byguid_);
} else {
good_ = file_open_status::UNSUPPORTED_SCHEMA;
}
}
// byidentity_ = decltype(byidentity_)(&std::get<impl::rocks_db_file_storage>(storage_).instance_cache_);
2025-03-13 13:09:39 +01:00
} else {
2025-08-31 15:52:46 +02:00
storage_.emplace<0>();
good_ = file_open_status::READ_ERROR;
// throw std::runtime_error("Unsupported file format");
}
ifcroot_type_ = schema_ ? schema_->declaration_by_name("IfcRoot") : nullptr;
return good_ == file_open_status::SUCCESS;
}
void IfcParse::IfcFile::bypass_type(const std::string& type_name) {
types_to_bypass_loading_.insert(type_name);
}
2026-06-11 21:04:44 +02:00
IfcFile::IfcFile(const std::string& path, filetype ty, bool readonly, Logger& logger)
: logger_(logger)
, schema_(nullptr)
, ifcroot_type_(nullptr)
, max_id_(0)
2026-06-11 21:04:44 +02:00
, _header(this, logger)
{
initialize(path, ty, readonly);
}
2017-08-22 10:16:07 +02:00
2026-06-11 21:04:44 +02:00
IfcFile::IfcFile(std::istream& stream, int length, Logger& logger)
: logger_(logger)
, schema_(nullptr)
, ifcroot_type_(nullptr)
2025-09-01 13:25:48 +02:00
, max_id_(0)
2026-06-11 21:04:44 +02:00
, _header(this, logger)
{
2025-10-13 20:47:43 +02:00
FileReader s(FileReader::caller_fed_tag{});
std::string string_data;
string_data.resize(length);
stream.read(string_data.data(), length);
2025-10-22 21:42:15 +02:00
s.pushNextPage(string_data);
2025-10-13 20:47:43 +02:00
2026-06-11 21:04:44 +02:00
storage_.emplace<1>(this, logger_.get());
std::get<impl::in_memory_file_storage>(storage_).read_from_stream(&s, schema_, max_id_, types_to_bypass_loading_);
2025-09-19 13:23:04 +02:00
good_ = std::get<impl::in_memory_file_storage>(storage_).good_;
ifcroot_type_ = schema_ ? schema_->declaration_by_name("IfcRoot") : nullptr;
2025-08-29 14:15:58 +02:00
byid_ = decltype(byid_)(&std::get<impl::in_memory_file_storage>(storage_).byid_);
byref_excl_ = decltype(byref_excl_)(&std::get<impl::in_memory_file_storage>(storage_).byref_excl_);
byguid_ = decltype(byguid_)(&std::get<impl::in_memory_file_storage>(storage_).byguid_);
}
2026-06-11 21:04:44 +02:00
IfcFile::IfcFile(void* data, int length, Logger& logger)
: logger_(logger)
, schema_(nullptr)
, ifcroot_type_(nullptr)
2025-09-01 13:25:48 +02:00
, max_id_(0)
2026-06-11 21:04:44 +02:00
, _header(this, logger)
{
2025-10-13 20:47:43 +02:00
FileReader s(std::string((char*)data, length), FileReader::caller_fed_tag{});
2026-06-11 21:04:44 +02:00
storage_.emplace<1>(this, logger_.get());
std::get<impl::in_memory_file_storage>(storage_).read_from_stream(&s, schema_, max_id_, types_to_bypass_loading_);
2025-09-19 13:23:04 +02:00
good_ = std::get<impl::in_memory_file_storage>(storage_).good_;
ifcroot_type_ = schema_ ? schema_->declaration_by_name("IfcRoot") : nullptr;
2025-08-29 14:15:58 +02:00
byid_ = decltype(byid_)(&std::get<impl::in_memory_file_storage>(storage_).byid_);
byref_excl_ = decltype(byref_excl_)(&std::get<impl::in_memory_file_storage>(storage_).byref_excl_);
byguid_ = decltype(byguid_)(&std::get<impl::in_memory_file_storage>(storage_).byguid_);
2017-12-12 10:33:24 +01:00
}
2026-06-11 21:04:44 +02:00
IfcFile::IfcFile(IfcParse::FileReader* s, Logger& logger)
: logger_(logger)
, schema_(nullptr)
, ifcroot_type_(nullptr)
2025-09-01 13:25:48 +02:00
, max_id_(0)
2026-06-11 21:04:44 +02:00
, _header(this, logger)
{
2026-06-11 21:04:44 +02:00
storage_.emplace<1>(this, logger_.get());
std::get<impl::in_memory_file_storage>(storage_).read_from_stream(s, schema_, max_id_, types_to_bypass_loading_);
2025-09-19 13:23:04 +02:00
good_ = std::get<impl::in_memory_file_storage>(storage_).good_;
ifcroot_type_ = schema_ ? schema_->declaration_by_name("IfcRoot") : nullptr;
2025-08-29 14:15:58 +02:00
byid_ = decltype(byid_)(&std::get<impl::in_memory_file_storage>(storage_).byid_);
byref_excl_ = decltype(byref_excl_)(&std::get<impl::in_memory_file_storage>(storage_).byref_excl_);
byguid_ = decltype(byguid_)(&std::get<impl::in_memory_file_storage>(storage_).byguid_);
2017-12-12 10:33:24 +01:00
}
2026-06-11 21:04:44 +02:00
IfcFile::IfcFile(const IfcParse::schema_definition* schema, filetype ty, const std::string& path, Logger& logger)
: logger_(logger)
, schema_(schema)
, ifcroot_type_(schema_->declaration_by_name("IfcRoot"))
, max_id_(0)
2026-06-11 21:04:44 +02:00
, _header(this, logger)
{
2025-03-13 13:09:39 +01:00
if (ty == FT_AUTODETECT) {
ty = guess_file_type(path);
}
if (ty == FT_IFCSPF) {
2026-06-11 21:04:44 +02:00
storage_.emplace<1>(this, logger_.get());
2025-02-27 22:07:31 +01:00
byid_ = decltype(byid_)(&std::get<impl::in_memory_file_storage>(storage_).byid_);
byref_excl_ = decltype(byref_excl_)(&std::get<impl::in_memory_file_storage>(storage_).byref_excl_);
byguid_ = decltype(byguid_)(&std::get<impl::in_memory_file_storage>(storage_).byguid_);
// byidentity_ = decltype(byidentity_)(&std::get<impl::in_memory_file_storage>(storage_).byidentity_);
2025-03-13 13:09:39 +01:00
} else if (ty == FT_ROCKSDB) {
2025-02-25 21:29:54 +01:00
storage_.emplace<2>(path, this);
2025-02-27 22:07:31 +01:00
byid_ = decltype(byid_)(&std::get<impl::rocks_db_file_storage>(storage_).instance_by_name_);
byref_excl_ = decltype(byref_excl_)(&std::get<impl::rocks_db_file_storage>(storage_).byref_excl_);
byguid_ = decltype(byguid_)(&std::get<impl::rocks_db_file_storage>(storage_).byguid_);
// byidentity_ = decltype(byidentity_)(&std::get<impl::rocks_db_file_storage>(storage_).instance_cache_);
2025-03-13 13:09:39 +01:00
} else {
throw std::runtime_error("Unsupported file format");
2025-02-25 21:29:54 +01:00
}
2023-09-17 12:30:17 +02:00
setDefaultHeaderValues();
}
bool IfcParse::InstanceStreamer::hasSemicolon() const {
2025-10-13 20:47:43 +02:00
auto local_stream = stream_->clone();
2026-06-11 21:04:44 +02:00
auto local_lexer = IfcSpfLexer(&local_stream, logger_.get());
2025-10-22 21:42:15 +02:00
Token t;
try {
t = local_lexer.Next();
} catch (const std::out_of_range&) {
return false;
}
2025-10-13 20:47:43 +02:00
while (t.type != Token_NONE) {
if (TokenFunc::isOperator(t, ';')) {
return true;
}
2025-10-22 21:42:15 +02:00
try {
t = local_lexer.Next();
} catch (const std::out_of_range&) {
// This most likely happens when a page boundary is contained within a string
break;
}
2025-10-13 20:47:43 +02:00
}
return false;
}
size_t IfcParse::InstanceStreamer::semicolonCount() const {
auto local_stream = stream_->clone();
2026-06-11 21:04:44 +02:00
auto local_lexer = IfcSpfLexer(&local_stream, logger_.get());
Token t;
size_t count = 0;
try {
t = local_lexer.Next();
} catch (const std::out_of_range&) {
return false;
}
while (t.type != Token_NONE) {
if (TokenFunc::isOperator(t, ';')) {
count++;
}
try {
t = local_lexer.Next();
} catch (const std::out_of_range&) {
// This most likely happens when a page boundary is contained within a string
break;
}
}
return count;
}
2025-10-22 21:42:15 +02:00
void IfcParse::InstanceStreamer::pushPage(const std::string& page)
2025-10-13 20:47:43 +02:00
{
2025-10-22 21:42:15 +02:00
stream_->pushNextPage(page);
2025-10-13 20:47:43 +02:00
if (good_ == file_open_status::NO_HEADER) {
2026-06-11 21:04:44 +02:00
header_ = new IfcParse::IfcSpfHeader(lexer_, logger_.get());
2025-10-13 20:47:43 +02:00
if (header_->tryRead() && header_->file_schema()->schema_identifiers().size() == 1) {
try {
schema_ = IfcParse::schema_by_name(header_->file_schema()->schema_identifiers().front());
good_ = file_open_status::SUCCESS;
} catch (const IfcParse::IfcException&) {
}
}
storage_.file = nullptr;
storage_.schema = schema_;
storage_.tokens = lexer_;
storage_.references_to_resolve = &references_to_resolve_;
}
}
2026-06-11 21:04:44 +02:00
IfcParse::InstanceStreamer::InstanceStreamer(Logger& logger)
2025-10-13 20:47:43 +02:00
: stream_(new FileReader(FileReader::caller_fed_tag{}))
2026-06-11 21:04:44 +02:00
, lexer_(new IfcSpfLexer(stream_, logger))
, header_(nullptr)
2025-10-13 20:47:43 +02:00
, token_stream_(3, Token{})
, schema_(nullptr)
2026-06-11 21:04:44 +02:00
, storage_(nullptr, logger)
, logger_(logger)
2025-10-13 20:47:43 +02:00
, progress_(0)
{
init_locale();
good_ = file_open_status::NO_HEADER;
}
2026-06-11 21:04:44 +02:00
IfcParse::InstanceStreamer::InstanceStreamer(const std::string& fn, bool mmap, Logger& logger)
2025-10-13 20:47:43 +02:00
: stream_(mmap ? new FileReader(fn, FileReader::mmap_tag{}) : new FileReader(fn))
2026-06-11 21:04:44 +02:00
, lexer_(new IfcSpfLexer(stream_, logger))
, header_(nullptr)
2025-09-10 21:42:55 +02:00
, token_stream_(3, Token{})
, schema_(nullptr)
2026-06-11 21:04:44 +02:00
, storage_(nullptr, logger)
, logger_(logger)
2025-09-10 21:42:55 +02:00
, progress_(0)
{
init_locale();
good_ = file_open_status::NO_HEADER;
2025-10-13 20:47:43 +02:00
if (stream_->size() && !stream_->eof()) {
2026-06-11 21:04:44 +02:00
header_ = new IfcParse::IfcSpfHeader(lexer_, logger_.get());
if (header_->tryRead() && header_->file_schema()->schema_identifiers().size() == 1) {
try {
schema_ = IfcParse::schema_by_name(header_->file_schema()->schema_identifiers().front());
good_ = file_open_status::SUCCESS;
} catch (const IfcParse::IfcException&) {
}
}
storage_.file = nullptr;
storage_.schema = schema_;
storage_.tokens = lexer_;
storage_.references_to_resolve = &references_to_resolve_;
}
}
2026-06-11 21:04:44 +02:00
IfcParse::InstanceStreamer::InstanceStreamer(void* data, int length, Logger& logger)
2025-10-13 20:47:43 +02:00
: stream_(new FileReader(std::string((char*) data, length), FileReader::caller_fed_tag{}))
2026-06-11 21:04:44 +02:00
, lexer_(new IfcSpfLexer(stream_, logger))
, header_(nullptr)
, token_stream_(3, Token{})
, schema_(nullptr)
2026-06-11 21:04:44 +02:00
, storage_(nullptr, logger)
, logger_(logger)
, progress_(0)
{
init_locale();
2025-09-10 21:42:55 +02:00
good_ = file_open_status::NO_HEADER;
2025-10-13 20:47:43 +02:00
if (stream_->size() && !stream_->eof()) {
2026-06-11 21:04:44 +02:00
header_ = new IfcParse::IfcSpfHeader(lexer_, logger_.get());
2025-09-10 21:42:55 +02:00
if (header_->tryRead() && header_->file_schema()->schema_identifiers().size() == 1) {
try {
schema_ = IfcParse::schema_by_name(header_->file_schema()->schema_identifiers().front());
good_ = file_open_status::SUCCESS;
} catch (const IfcParse::IfcException&) {
}
}
storage_.file = nullptr;
storage_.schema = schema_;
storage_.tokens = lexer_;
storage_.references_to_resolve = &references_to_resolve_;
}
}
2026-06-11 21:04:44 +02:00
IfcParse::InstanceStreamer::InstanceStreamer(const IfcParse::schema_definition* schema, IfcParse::IfcSpfLexer* lexer, Logger& logger)
2025-09-10 21:42:55 +02:00
: stream_(nullptr)
, lexer_(lexer)
, header_(nullptr)
, token_stream_(3, Token{})
, schema_(schema)
2026-06-11 21:04:44 +02:00
, storage_(nullptr, logger)
, logger_(logger)
2025-09-10 21:42:55 +02:00
, progress_(0)
{
init_locale();
storage_.file = nullptr;
storage_.schema = schema_;
storage_.tokens = lexer_;
storage_.references_to_resolve = &references_to_resolve_;
}
void IfcParse::impl::in_memory_file_storage::read_from_stream(IfcParse::FileReader* s, const IfcParse::schema_definition*& schema, unsigned int& max_id, const std::set<std::string>& typed_to_bypass) {
2023-09-17 12:30:17 +02:00
// Initialize a "C" locale for locale-independent
// number parsing. See comment above on line 41.
init_locale();
tokens = nullptr;
2025-10-13 20:47:43 +02:00
if (!s->size() || s->eof()) {
// @todo set good on parent file
2023-09-17 12:30:17 +02:00
good_ = file_open_status::READ_ERROR;
return;
}
2026-06-11 21:04:44 +02:00
tokens = new IfcSpfLexer(s, logger());
2017-08-01 16:45:11 +02:00
2023-09-17 12:30:17 +02:00
std::vector<std::string> schemas;
2017-12-12 10:33:24 +01:00
// @todo this line makes no sense
file->header().file(file);
if (file->header().tryRead()) {
2023-09-17 12:30:17 +02:00
try {
2025-02-27 22:07:31 +01:00
schemas = file->header().file_schema()->schema_identifiers();
2023-09-17 12:30:17 +02:00
} catch (...) {
// Purposely empty catch block
}
} else {
good_ = file_open_status::NO_HEADER;
}
if (schemas.size() == 1) {
try {
schema = IfcParse::schema_by_name(schemas.front());
2023-09-17 12:30:17 +02:00
} catch (const IfcParse::IfcException& e) {
good_ = file_open_status::UNSUPPORTED_SCHEMA;
2026-06-11 21:04:44 +02:00
logger().Error("SYN", 15, e);
2023-09-17 12:30:17 +02:00
}
}
if (schema == nullptr) {
2026-06-11 21:04:44 +02:00
logger().Message(Logger::LOG_ERROR, "UNS", 32, "No support for file schema encountered (" + boost::algorithm::join(schemas, ", ") + ")");
2023-09-17 12:30:17 +02:00
return;
}
auto ifcroot_type_ = schema->declaration_by_name("IfcRoot");
2023-09-17 12:30:17 +02:00
2026-06-11 21:04:44 +02:00
InstanceStreamer streamer(schema, tokens, logger());
streamer.bypassTypes(typed_to_bypass);
2023-09-17 12:30:17 +02:00
2026-06-11 21:04:44 +02:00
logger().Status("Scanning file...");
while (streamer) {
2023-09-17 12:30:17 +02:00
2025-10-22 21:42:15 +02:00
auto inst = streamer.readInstance();
2023-09-17 12:30:17 +02:00
if (!inst) {
// No more instances to read
break;
}
auto current_id = std::get<0>(*inst);
2023-09-17 12:30:17 +02:00
auto instance = schema->instantiate(std::get<1>(*inst), std::move(std::get<2>(*inst)));
instance->file_ = file;
2025-10-13 20:47:43 +02:00
instance->id_ = (uint32_t) current_id;
2023-09-17 12:30:17 +02:00
if (instance->declaration().is(*ifcroot_type_)) {
try {
// @nb here we know we're using in-memory so 'nullptr, nullptr, 0' is safe
const std::string guid = instance->data().get_attribute_value(nullptr, nullptr, 0, 0);
if (byguid_.find(guid) != byguid_.end()) {
std::stringstream ss;
ss << "Instance encountered with non-unique GlobalId " << guid;
2026-06-11 21:04:44 +02:00
logger().Message(Logger::LOG_WARNING, "SYN", 16, ss.str());
2023-09-17 12:30:17 +02:00
}
byguid_[guid] = instance;
} catch (const IfcException& ex) {
2026-06-11 21:04:44 +02:00
logger().Message(Logger::LOG_ERROR, "SYN", 17, ex.what());
2023-09-17 12:30:17 +02:00
}
}
2023-09-17 12:30:17 +02:00
const IfcParse::declaration* ty = &instance->declaration();
2023-09-17 12:30:17 +02:00
{
if (bytype_excl_.find(ty) == bytype_excl_.end()) {
bytype_excl_[ty].reset(new aggregate_of_instance());
2023-09-17 12:30:17 +02:00
}
bytype_excl_[ty]->push(instance);
2023-09-17 12:30:17 +02:00
}
if (byid_.find(current_id) != byid_.end()) {
std::stringstream ss;
ss << "Overwriting instance with name #" << current_id;
2026-06-11 21:04:44 +02:00
logger().Message(Logger::LOG_WARNING, "SYN", 18, ss.str());
2023-09-17 12:30:17 +02:00
}
// byidentity_[instance->identity()] = instance;
2025-10-13 20:47:43 +02:00
byid_.insert({(uint32_t) current_id, instance });
// @nb cannot assign to byid_;
// byid_[current_id] = instance;
2017-06-05 17:26:58 +02:00
max_id = (std::max)(max_id, (unsigned int) current_id);
2023-09-17 12:30:17 +02:00
}
2017-06-05 17:26:58 +02:00
good_ = streamer.status();
byref_excl_ = streamer.inverses();
2025-10-08 14:17:47 +02:00
// Move the storage of simple type instances so that they are retained during the lifetime of the file
2025-10-22 21:42:15 +02:00
read_simple_type_instances = streamer.stealInstances();
// Set file ownership on simple type instances, so that when adding them to other files, proper copies are created
for (auto& inst : read_simple_type_instances) {
inst->file_ = file;
}
2026-06-11 21:04:44 +02:00
logger().Status("\rDone scanning file ");
2017-06-05 17:26:58 +02:00
2024-08-23 20:29:07 +02:00
delete tokens;
if (good_ != file_open_status::SUCCESS) {
return;
}
const auto& bypassed = streamer.bypassed_instances();
for (const auto& p : streamer.references()) {
const auto& ref = p.first.name_;
const auto& refattr = p.first.index_;
if (auto* v = std::get_if<reference_or_simple_type>(&p.second)) {
if (auto* name = std::get_if<InstanceReference>(v)) {
if (std::binary_search(bypassed.begin(), bypassed.end(), *name)) {
continue;
}
auto it = byid_.find(*name);
if (it == byid_.end()) {
2026-06-11 21:04:44 +02:00
logger().Error("SYN", 19, "Instance reference #" + std::to_string(*name) + " used by instance #" + std::to_string(ref) + " at attribute index " + std::to_string(refattr) + " not found at offset " + std::to_string(name->file_offset));
} else {
auto* storage = &byid_[p.first.name_]->data();
auto attr_index = p.first.index_;
if (storage->has_attribute_value<IfcUtil::IfcBaseClass*>(nullptr, nullptr, 0, attr_index)) {
2025-08-26 10:20:08 +02:00
IfcUtil::IfcBaseClass* inst = storage->get_attribute_value(nullptr, nullptr, 0, attr_index);
if (!inst->declaration().as_entity()) {
// Probably a case of IfcPropertySetDefinitionSet, divert storage of reference to the simply type instance
storage = &inst->data();
attr_index = 0;
}
}
if (storage->has_attribute_value<Blank>(nullptr, nullptr, 0, attr_index)) {
2025-08-26 10:20:08 +02:00
storage->set_attribute_value(nullptr, nullptr, 0, attr_index, it->second);
} else {
2026-06-11 21:04:44 +02:00
logger().Error("SYN", 20, "Duplicate definition for instance reference");
}
2024-08-23 20:29:07 +02:00
}
} else if (auto* inst = std::get_if<IfcUtil::IfcBaseClass*>(v)) {
byid_[p.first.name_]->data().set_attribute_value(nullptr, nullptr, 0, p.first.index_, *inst);
}
2025-10-13 20:47:43 +02:00
} else if (auto* vv = std::get_if<std::vector<reference_or_simple_type>>(&p.second)) {
aggregate_of_instance::ptr instances(new aggregate_of_instance);
2025-10-13 20:47:43 +02:00
instances->reserve(vv->size());
for (const auto& vi : *vv) {
if (auto* name = std::get_if<InstanceReference>(&vi)) {
if (std::binary_search(bypassed.begin(), bypassed.end(), *name)) {
continue;
}
auto it = byid_.find(*name);
if (it == byid_.end()) {
2026-06-11 21:04:44 +02:00
logger().Error("SYN", 21, "Instance reference #" + std::to_string(*name) + " used by instance #" + std::to_string(ref) + " at attribute index " + std::to_string(refattr) + " not found at offset " + std::to_string(name->file_offset));
} else {
instances->push(it->second);
2024-08-23 20:29:07 +02:00
}
} else if (auto* inst = std::get_if<IfcUtil::IfcBaseClass*>(&vi)) {
instances->push(*inst);
2024-08-23 20:29:07 +02:00
}
}
auto* storage = &byid_[p.first.name_]->data();
auto attr_index = p.first.index_;
if (storage->has_attribute_value<IfcUtil::IfcBaseClass*>(nullptr, nullptr, 0, attr_index)) {
2025-08-26 10:20:08 +02:00
IfcUtil::IfcBaseClass* inst = storage->get_attribute_value(nullptr, nullptr, 0, attr_index);
if (!inst->declaration().as_entity()) {
// Probably a case of IfcPropertySetDefinitionSet, divert storage of reference to the simply type instance
storage = &inst->data();
attr_index = 0;
}
}
if (storage->has_attribute_value<Blank>(nullptr, nullptr, 0, attr_index)) {
2025-08-26 10:20:08 +02:00
storage->set_attribute_value(nullptr, nullptr, 0, attr_index, instances);
} else {
2026-06-11 21:04:44 +02:00
logger().Error("SYN", 22, "Duplicate definition for instance reference");
}
2025-10-13 20:47:43 +02:00
} else if (auto* vvv = std::get_if<std::vector<std::vector<reference_or_simple_type>>>(&p.second)) {
aggregate_of_aggregate_of_instance::ptr instances(new aggregate_of_aggregate_of_instance);
2025-10-13 20:47:43 +02:00
for (const auto& vi : *vvv) {
std::vector<IfcUtil::IfcBaseClass*> inner;
for (const auto& vii : vi) {
if (auto* name = std::get_if<InstanceReference>(&vii)) {
if (std::binary_search(bypassed.begin(), bypassed.end(), *name)) {
continue;
}
auto it = byid_.find(*name);
if (it == byid_.end()) {
2026-06-11 21:04:44 +02:00
logger().Error("SYN", 23, "Instance reference #" + std::to_string(*name) + " used by instance #" + std::to_string(ref) + " at attribute index " + std::to_string(refattr) + " not found at offset " + std::to_string(name->file_offset));
} else {
inner.push_back(it->second);
2024-08-23 20:29:07 +02:00
}
} else if (auto* inst = std::get_if<IfcUtil::IfcBaseClass*>(&vii)) {
inner.push_back(*inst);
2024-08-23 20:29:07 +02:00
}
}
instances->push(inner);
2024-08-23 20:29:07 +02:00
}
auto* storage = &byid_[p.first.name_]->data();
auto attr_index = p.first.index_;
if (storage->has_attribute_value<IfcUtil::IfcBaseClass*>(nullptr, nullptr, 0, attr_index)) {
2025-08-26 10:20:08 +02:00
IfcUtil::IfcBaseClass* inst = storage->get_attribute_value(nullptr, nullptr, 0, attr_index);
if (!inst->declaration().as_entity()) {
// Probably a case of IfcPropertySetDefinitionSet, divert storage of reference to the simply type instance
storage = &inst->data();
attr_index = 0;
}
}
if (storage->has_attribute_value<Blank>(nullptr, nullptr, 0, attr_index)) {
2025-08-26 10:20:08 +02:00
storage->set_attribute_value(nullptr, nullptr, 0, attr_index, instances);
} else {
2026-06-11 21:04:44 +02:00
logger().Error("SYN", 24, "Duplicate definition for instance reference");
}
}
2024-08-23 20:29:07 +02:00
}
2023-09-17 12:30:17 +02:00
2026-06-11 21:04:44 +02:00
logger().Status("Done resolving references");
}
2021-06-26 13:23:55 +02:00
void IfcFile::recalculate_id_counter() {
/*
// @todo
2023-09-17 12:30:17 +02:00
entity_by_id_t::key_type k = 0;
for (auto& p : byid_) {
2023-09-17 12:30:17 +02:00
if (p.first > k) {
k = p.first;
}
}
max_id_ = (unsigned int)k;
*/
2021-06-26 13:23:55 +02:00
}
class traversal_recorder {
2023-09-17 12:30:17 +02:00
aggregate_of_instance::ptr list_;
std::map<int, aggregate_of_instance::ptr> instances_by_level_;
int mode_;
public:
traversal_recorder(int mode) : mode_(mode) {
if (mode == 0) {
list_.reset(new aggregate_of_instance);
}
};
void push_back(int level, IfcUtil::IfcBaseClass* instance) {
if (mode_ == 0) {
list_->push(instance);
} else {
auto& l = instances_by_level_[level];
if (!l) {
l.reset(new aggregate_of_instance);
}
l->push(instance);
}
}
2023-09-17 12:30:17 +02:00
aggregate_of_instance::ptr get_list() const {
if (mode_ == 0) {
return list_;
}
aggregate_of_instance::ptr l(new aggregate_of_instance);
for (const auto& p : instances_by_level_) {
l->push(p.second);
}
return l;
2023-09-17 12:30:17 +02:00
}
};
2017-08-01 19:07:46 +02:00
class traversal_visitor {
2023-09-17 12:30:17 +02:00
private:
std::set<IfcUtil::IfcBaseClass*>& visited_;
traversal_recorder& list_;
int level_;
int max_level_;
public:
traversal_visitor(std::set<IfcUtil::IfcBaseClass*>& visited, traversal_recorder& list, int level, int max_level)
: visited_(visited),
list_(list),
level_(level),
max_level_(max_level) {}
void operator()(IfcUtil::IfcBaseClass* inst, int index);
2017-08-01 19:07:46 +02:00
};
void traverse_(IfcUtil::IfcBaseClass* instance, std::set<IfcUtil::IfcBaseClass*>& visited, traversal_recorder& list, int level, int max_level) {
2023-09-17 12:30:17 +02:00
if (visited.find(instance) != visited.end()) {
return;
}
visited.insert(instance);
list.push_back(level, instance);
2023-09-17 12:30:17 +02:00
if (level >= max_level && max_level > 0) {
return;
}
2023-09-17 12:30:17 +02:00
traversal_visitor visit(visited, list, level + 1, max_level);
2025-02-27 22:07:31 +01:00
apply_individual_instance_visitor(instance).apply(visit);
}
void traversal_visitor::operator()(IfcUtil::IfcBaseClass* inst, int /* index */) {
2023-09-17 12:30:17 +02:00
traverse_(inst, visited_, list_, level_, max_level_);
2017-08-01 19:07:46 +02:00
}
2021-07-29 14:54:51 +02:00
aggregate_of_instance::ptr IfcParse::traverse(IfcUtil::IfcBaseClass* instance, int max_level) {
2023-09-17 12:30:17 +02:00
std::set<IfcUtil::IfcBaseClass*> visited;
traversal_recorder recorder(0);
traverse_(instance, visited, recorder, 0, max_level);
return recorder.get_list();
}
// I'm cheating this isn't breadth-first, but rather we record visited instances
// keeping track of their rank and return a list ordered by rank. Is this equivalent?
aggregate_of_instance::ptr IfcParse::traverse_breadth_first(IfcUtil::IfcBaseClass* instance, int max_level) {
2023-09-17 12:30:17 +02:00
std::set<IfcUtil::IfcBaseClass*> visited;
traversal_recorder recorder(1);
traverse_(instance, visited, recorder, 0, max_level);
return recorder.get_list();
}
/// @note: for backwards compatibility
2021-07-29 14:54:51 +02:00
aggregate_of_instance::ptr IfcFile::traverse(IfcUtil::IfcBaseClass* instance, int max_level) {
2023-09-17 12:30:17 +02:00
return IfcParse::traverse(instance, max_level);
}
/// @note: for backwards compatibility
aggregate_of_instance::ptr IfcFile::traverse_breadth_first(IfcUtil::IfcBaseClass* instance, int max_level) {
2023-09-17 12:30:17 +02:00
return IfcParse::traverse_breadth_first(instance, max_level);
}
void IfcFile::addEntities(aggregate_of_instance::ptr entities) {
for (aggregate_of_instance::it i = entities->begin(); i != entities->end(); ++i) {
2023-09-17 12:30:17 +02:00
addEntity(*i);
}
}
IfcUtil::IfcBaseClass* IfcFile::addEntity(IfcUtil::IfcBaseClass* entity, int id) {
const bool copying_from_other_file =
entity->file_ != nullptr && entity->file_ != this;
if (id != -1) {
bool id_already_exists = false;
try {
2025-03-14 18:56:27 +01:00
if (check_existance_before_adding) {
instance_by_id(id);
id_already_exists = true;
}
} catch (...) {}
if (id_already_exists) {
throw IfcParse::IfcException("An instance with id " + boost::lexical_cast<std::string>(id) + " is already part of this file");
}
2023-09-17 12:30:17 +02:00
}
2023-09-17 12:30:17 +02:00
if (entity->declaration().schema() != schema()) {
throw IfcParse::IfcException("Unabled to add instance from " + entity->declaration().schema()->name() + " schema to file with " + schema()->name() + " schema");
}
2023-09-17 12:30:17 +02:00
// If this instance has been inserted before, return
// a reference to the copy that was created from it.
entity_entity_map_t::iterator mit = entity_file_map_.find(entity->identity());
if (mit != entity_file_map_.end()) {
2023-09-17 12:30:17 +02:00
return mit->second;
}
2023-09-17 12:30:17 +02:00
IfcUtil::IfcBaseClass* new_entity = entity;
// Obtain all forward references by a depth-first
// traversal and add them to the file.
2024-08-23 20:29:07 +02:00
try {
aggregate_of_instance::ptr entity_attributes = traverse(entity, 1);
for (aggregate_of_instance::it it = entity_attributes->begin(); it != entity_attributes->end(); ++it) {
if (*it != entity) {
entity_entity_map_t::iterator mit2 = entity_file_map_.find((*it)->identity());
if (mit2 == entity_file_map_.end()) {
entity_file_map_.insert(entity_entity_map_t::value_type((*it)->identity(), addEntity(*it)));
2023-09-17 12:30:17 +02:00
}
}
}
2024-08-23 20:29:07 +02:00
} catch (...) {
2026-06-11 21:04:44 +02:00
logger().Message(Logger::LOG_ERROR, "SYN", 25, "Failed to visit forward references of", entity);
2023-09-17 12:30:17 +02:00
}
2023-09-17 12:30:17 +02:00
// See whether the instance is already part of a file
2024-08-23 20:29:07 +02:00
if (entity->file_ != nullptr) {
if (entity->file_ == this) {
if (entity->declaration().as_entity() == nullptr) {
2023-09-17 12:30:17 +02:00
// While not a mapping that can be queried, we do need to free the instance later on
// @todo. why (over?)write this when adding from the same file?
std::visit([new_entity](auto& m) {
if constexpr (std::is_same_v<std::decay_t<decltype(m)>, impl::in_memory_file_storage>) {
// @todo not freed yet
m.tbyid_.insert({ new_entity->identity(), new_entity });
}
}, storage_);
2023-09-17 12:30:17 +02:00
}
// If it is part of this file
// nothing else needs to be done.
return entity;
}
// An instance is being added from another file. A copy of the
// container and entity is created. The attribute references
// need to be updated to point to instances in this file.
2024-08-23 20:29:07 +02:00
IfcFile* other_file = entity->file_;
2025-02-27 22:07:31 +01:00
auto* decl = &entity->declaration();
if (storage_.index() == 1) {
if (auto* ent = decl->as_entity()) {
new_entity = schema_->instantiate(decl, in_memory_attribute_storage(ent->attribute_count()));
} else if (auto* typedecl = decl->as_type_declaration()) {
new_entity = schema_->instantiate(decl, in_memory_attribute_storage(1));
}
}
if (storage_.index() == 2) {
new_entity = schema_->instantiate(decl, rocks_db_attribute_storage{});
}
new_entity->file_ = this;
2024-08-23 20:29:07 +02:00
// A new entity instance name is generated and
// the instance is pointed to this file.
if (new_entity->declaration().as_entity() != nullptr) {
if (id == -1) {
new_entity->as<IfcUtil::IfcBaseEntity>()->set_id(FreshId());
} else {
new_entity->as<IfcUtil::IfcBaseEntity>()->set_id((unsigned int)id);
if ((unsigned)id > max_id_) {
max_id_ = (unsigned)id;
}
}
}
2025-02-27 22:07:31 +01:00
void* own_storage = std::visit([](const auto& m) { return (void*)&m; }, storage_);
void* other_storage = std::visit([](const auto& m) { return (void*)&m; }, other_file->storage_);
auto num_attributes = (entity->declaration().as_entity() ? entity->declaration().as_entity()->attribute_count() : 1);
for (size_t i = 0; i < num_attributes; ++i) {
entity->data().apply_visitor(other_storage, decl, entity->id() ? entity->id() : entity->identity(), [this, i, decl, new_entity, own_storage](const auto& v) {
2025-02-27 22:07:31 +01:00
using U = std::decay_t<decltype(v)>;
// only need to copy non-instance attribute values, others are assigned below after mapping
if constexpr (std::is_same_v<U, IfcUtil::IfcBaseClass*>) {
} else if constexpr (std::is_same_v<U, aggregate_of_instance::ptr>) {
} else if constexpr (std::is_same_v<U, aggregate_of_aggregate_of_instance::ptr>) {
} else {
new_entity->set_attribute_value(i, v);
}
}, i);
}
2023-09-17 12:30:17 +02:00
// In case an entity is added that contains geometry, the unit
// information needs to be accounted for for IfcLengthMeasures.
2025-03-14 18:56:27 +01:00
double conversion_factor = calculate_unit_factors ? std::numeric_limits<double>::quiet_NaN() : 1.0;
2025-02-27 22:07:31 +01:00
for (size_t i = 0; i < (new_entity->declaration().as_entity() ? new_entity->declaration().as_entity()->attribute_count() : 1); ++i) {
// old attribute value
auto attr = entity->get_attribute_value(i);
2024-08-23 20:29:07 +02:00
IfcUtil::ArgumentType attr_type = attr.type();
2023-09-17 12:30:17 +02:00
2025-10-13 20:47:43 +02:00
IfcParse::declaration* potentially_length_measure_decl = 0;
if (entity->declaration().as_entity() != nullptr) {
2025-10-13 20:47:43 +02:00
potentially_length_measure_decl = 0;
2023-09-17 12:30:17 +02:00
const parameter_type* pt = entity->declaration().as_entity()->attribute_by_index(i)->type_of_attribute();
while (pt->as_aggregation_type() != nullptr) {
2023-09-17 12:30:17 +02:00
pt = pt->as_aggregation_type()->type_of_element();
}
if (pt->as_named_type() != nullptr) {
2025-10-13 20:47:43 +02:00
potentially_length_measure_decl = pt->as_named_type()->declared_type();
2023-09-17 12:30:17 +02:00
}
}
if (attr_type == IfcUtil::Argument_ENTITY_INSTANCE) {
2024-08-23 20:29:07 +02:00
entity_entity_map_t::const_iterator eit = entity_file_map_.find(((IfcUtil::IfcBaseClass*)(attr))->identity());
if (eit == entity_file_map_.end()) {
2023-09-17 12:30:17 +02:00
throw IfcParse::IfcException("Unable to map instance to file");
}
2025-02-27 22:07:31 +01:00
// @todo previously, we directly use storage::set() not to trigger inverse recalculation which happens at the end
new_entity->set_attribute_value(i, eit->second);
2023-09-17 12:30:17 +02:00
} else if (attr_type == IfcUtil::Argument_AGGREGATE_OF_ENTITY_INSTANCE) {
2024-08-23 20:29:07 +02:00
aggregate_of_instance::ptr instances = attr;
2023-09-17 12:30:17 +02:00
aggregate_of_instance::ptr new_instances(new aggregate_of_instance);
for (aggregate_of_instance::it it = instances->begin(); it != instances->end(); ++it) {
entity_entity_map_t::const_iterator eit = entity_file_map_.find((*it)->identity());
if (eit == entity_file_map_.end()) {
2023-09-17 12:30:17 +02:00
throw IfcParse::IfcException("Unable to map instance to file");
}
new_instances->push(eit->second);
}
2025-02-27 22:07:31 +01:00
new_entity->set_attribute_value(i, new_instances);
2023-09-17 12:30:17 +02:00
} else if (attr_type == IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE) {
2024-08-23 20:29:07 +02:00
aggregate_of_aggregate_of_instance::ptr instances = attr;
2023-09-17 12:30:17 +02:00
aggregate_of_aggregate_of_instance::ptr new_instances(new aggregate_of_aggregate_of_instance);
for (aggregate_of_aggregate_of_instance::outer_it it = instances->begin(); it != instances->end(); ++it) {
std::vector<IfcUtil::IfcBaseClass*> list;
for (aggregate_of_aggregate_of_instance::inner_it jt = it->begin(); jt != it->end(); ++jt) {
entity_entity_map_t::const_iterator eit = entity_file_map_.find((*jt)->identity());
if (eit == entity_file_map_.end()) {
2023-09-17 12:30:17 +02:00
throw IfcParse::IfcException("Unable to map instance to file");
}
list.push_back(eit->second);
}
new_instances->push(list);
}
2024-08-23 20:29:07 +02:00
2025-02-27 22:07:31 +01:00
new_entity->set_attribute_value(i, new_instances);
2025-10-13 20:47:43 +02:00
} else if ((potentially_length_measure_decl != nullptr) && potentially_length_measure_decl->is(*schema()->declaration_by_name("IfcLengthMeasure"))) {
2023-09-17 12:30:17 +02:00
if (boost::math::isnan(conversion_factor)) {
std::pair<IfcUtil::IfcBaseClass*, double> this_file_unit = {nullptr, 1.0};
std::pair<IfcUtil::IfcBaseClass*, double> other_file_unit = {nullptr, 1.0};
try {
this_file_unit = getUnit("LENGTHUNIT");
other_file_unit = other_file->getUnit("LENGTHUNIT");
} catch (IfcParse::IfcException&) {
}
if ((this_file_unit.first != nullptr) && (other_file_unit.first != nullptr)) {
2023-09-17 12:30:17 +02:00
conversion_factor = other_file_unit.second / this_file_unit.second;
} else {
conversion_factor = 1.;
}
}
if (attr_type == IfcUtil::Argument_DOUBLE) {
2024-08-23 20:29:07 +02:00
double v = attr;
2023-09-17 12:30:17 +02:00
v *= conversion_factor;
2025-02-27 22:07:31 +01:00
new_entity->set_attribute_value(i, v);
2023-09-17 12:30:17 +02:00
} else if (attr_type == IfcUtil::Argument_AGGREGATE_OF_DOUBLE) {
2024-08-23 20:29:07 +02:00
std::vector<double> v = attr;
2023-09-17 12:30:17 +02:00
for (std::vector<double>::iterator it = v.begin(); it != v.end(); ++it) {
(*it) *= conversion_factor;
}
2025-02-27 22:07:31 +01:00
new_entity->set_attribute_value(i, v);
2023-09-17 12:30:17 +02:00
} else if (attr_type == IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_DOUBLE) {
2024-08-23 20:29:07 +02:00
std::vector<std::vector<double>> v = attr;
2023-09-17 12:30:17 +02:00
for (std::vector<std::vector<double>>::iterator it = v.begin(); it != v.end(); ++it) {
std::vector<double>& v2 = (*it);
for (std::vector<double>::iterator jt = v2.begin(); jt != v2.end(); ++jt) {
(*jt) *= conversion_factor;
}
}
2025-02-27 22:07:31 +01:00
new_entity->set_attribute_value(i, v);
2023-09-17 12:30:17 +02:00
}
}
}
entity_file_map_.insert(entity_entity_map_t::value_type(entity->identity(), new_entity));
2023-09-17 12:30:17 +02:00
}
2023-09-17 12:30:17 +02:00
// For subtypes of IfcRoot, the GUID mapping needs to be updated.
if (new_entity->declaration().is(*ifcroot_type_)) {
try {
2025-02-27 22:07:31 +01:00
const std::string guid = new_entity->get_attribute_value(0);
if (byguid_.find(guid) != byguid_.end()) {
2023-09-17 12:30:17 +02:00
std::stringstream ss;
ss << "Overwriting entity with guid " << guid;
2026-06-11 21:04:44 +02:00
logger().Message(Logger::LOG_WARNING, "SYN", 26, ss.str());
2023-09-17 12:30:17 +02:00
}
byguid_.insert({ guid, new_entity });
2024-08-23 20:29:07 +02:00
} catch (const std::exception& ex) {
2026-06-11 21:04:44 +02:00
logger().Message(Logger::LOG_ERROR, "SYN", 27, ex.what());
2023-09-17 12:30:17 +02:00
}
}
2023-09-17 12:30:17 +02:00
// The mapping by entity type is updated.
const IfcParse::declaration* ty = &new_entity->declaration();
// @nb happens always because this also registers the type of the instance in rocksdb
// if (ty->as_entity() != nullptr) {
add_type_ref(new_entity);
// }
2023-09-17 12:30:17 +02:00
if (ty->as_entity() != nullptr) {
2023-09-17 12:30:17 +02:00
int new_id = -1;
2024-08-23 20:29:07 +02:00
if (new_entity->file_ == nullptr) {
2023-09-17 12:30:17 +02:00
// For newly created entities ensure a valid ENTITY_INSTANCE_NAME is set
2024-08-23 20:29:07 +02:00
new_entity->file_ = this;
2023-09-17 12:30:17 +02:00
boost::optional<unsigned> id_value;
if (id != -1) {
id_value = (unsigned)id;
if ((unsigned)id > max_id_) {
max_id_ = (unsigned)id;
2023-09-17 12:30:17 +02:00
}
}
2024-08-23 20:29:07 +02:00
new_id = new_entity->as<IfcUtil::IfcBaseEntity>()->set_id(id_value);
2023-09-17 12:30:17 +02:00
} else {
2024-08-23 20:29:07 +02:00
new_id = new_entity->id();
2023-09-17 12:30:17 +02:00
}
/*
if (byid_.find(new_id) != byid_.end()) {
2023-09-17 12:30:17 +02:00
// This should not happen
std::stringstream ss;
ss << "Overwriting entity with id " << new_id;
Logger::Message(Logger::LOG_WARNING, ss.str());
}
*/
// rocksdb instances are assumed to be create with file.create();
std::visit([new_entity](auto& m) {
if constexpr (std::is_same_v<std::decay_t<decltype(m)>, impl::in_memory_file_storage>) {
// @todo not freed yet
m.byid_.insert({ new_entity->id(), new_entity });
}
}, storage_);
2024-08-23 20:29:07 +02:00
} else if (new_entity->file_ == nullptr) {
2023-09-17 12:30:17 +02:00
// For non-entity instances, no mappings are updated, but the file
// pointer has to be set, so that actual copies are created in subsequent
// times.
2024-08-23 20:29:07 +02:00
new_entity->file_ = this;
2023-09-17 12:30:17 +02:00
// rocksdb instances are assumed to be create with file.create();
std::visit([new_entity](auto& m) {
if constexpr (std::is_same_v<std::decay_t<decltype(m)>, impl::in_memory_file_storage>) {
// @todo not freed yet
m.tbyid_.insert({ new_entity->identity(), new_entity });
}
}, storage_);
2023-09-17 12:30:17 +02:00
}
2021-03-13 12:16:40 +01:00
2025-08-28 09:37:09 +02:00
// @todo verify whether this is still needed. If instances are created directly on the file
// with create() (which is a necessity for using rocksdb storage) then it should be sufficient
// to register inverses only on attribute updates.
if (!copying_from_other_file && (ty->as_entity() != nullptr)) {
2023-09-17 12:30:17 +02:00
build_inverses_(new_entity);
}
return new_entity;
}
void IfcFile::removeEntity(IfcUtil::IfcBaseClass* entity) {
2024-08-23 20:29:07 +02:00
const unsigned id = entity->id();
2023-09-17 12:30:17 +02:00
IfcUtil::IfcBaseClass* file_entity = instance_by_id(id);
// 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);
// }
// TODO: Create a set of weak relations. Inverse relations that do not dictate an
// instance to be retained. For example: when deleting an IfcRepresentation, the
// individual IfcRepresentationItems can not be deleted if an IfcStyledItem is
// related. Hence, the IfcRepresentationItem::StyledByItem relation could be
// characterized as weak.
// std::set<IfcSchema::Type::Enum> weak_roots;
if (entity != file_entity) {
throw IfcParse::IfcException("Instance not part of this file");
}
2025-08-31 15:52:46 +02:00
if (batch_mode_) {
batch_deletion_ids_.push_back(id);
} else {
process_deletion_(entity);
}
}
void IfcFile::process_deletion_(IfcUtil::IfcBaseClass* entity) {
aggregate_of_instance::ptr references = instances_by_reference(entity->id());
// Alter entity instances with INVERSE relations to the entity being
// deleted. This is necessary to maintain a valid IFC file, because
// dangling references to it's entities name should be removed. At this
// moment, inversely related instances affected by the removal of the
// entity being deleted are not deleted themselves.
if (references) {
for (aggregate_of_instance::it iit = references->begin(); iit != references->end(); ++iit) {
IfcUtil::IfcBaseEntity* related_instance = (IfcUtil::IfcBaseEntity*)*iit;
2017-12-11 14:47:30 +01:00
if (std::find(batch_deletion_ids_.begin(), batch_deletion_ids_.end(), related_instance->id()) != batch_deletion_ids_.end()) {
continue;
}
2023-09-17 12:30:17 +02:00
2025-02-27 22:07:31 +01:00
const auto& decl = related_instance->declaration();
for (size_t i = 0; i < (decl.as_entity() ? decl.as_entity()->attribute_count() : 1); ++i) {
auto attr = related_instance->get_attribute_value(i);
if (attr.isNull()) {
2023-09-17 12:30:17 +02:00
continue;
}
IfcUtil::ArgumentType attr_type = attr.type();
switch (attr_type) {
case IfcUtil::Argument_ENTITY_INSTANCE: {
IfcUtil::IfcBaseClass* instance_attribute = attr;
if (instance_attribute == entity) {
related_instance->set_attribute_value(i, Blank{});
2023-09-17 12:30:17 +02:00
}
} break;
case IfcUtil::Argument_AGGREGATE_OF_ENTITY_INSTANCE: {
aggregate_of_instance::ptr instance_list = attr;
if (instance_list->contains(entity)) {
instance_list->remove(entity);
if ((instance_list->size() == 0U) && related_instance->declaration().as_entity()->attribute_by_index(i)->optional()) {
// @todo we can also check the lower bound of the attribute type before setting to null.
2024-08-23 20:29:07 +02:00
related_instance->set_attribute_value(i, Blank{});
} else {
related_instance->set_attribute_value(i, instance_list);
2023-09-17 12:30:17 +02:00
}
}
} break;
case IfcUtil::Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE: {
aggregate_of_aggregate_of_instance::ptr instance_list_list = attr;
if (instance_list_list->contains(entity)) {
aggregate_of_aggregate_of_instance::ptr new_list(new aggregate_of_aggregate_of_instance);
for (aggregate_of_aggregate_of_instance::outer_it it = instance_list_list->begin(); it != instance_list_list->end(); ++it) {
std::vector<IfcUtil::IfcBaseClass*> instances = *it;
std::vector<IfcUtil::IfcBaseClass*>::iterator jt;
while ((jt = std::find(instances.begin(), instances.end(), entity)) != instances.end()) {
instances.erase(jt);
2023-09-17 12:30:17 +02:00
}
new_list->push(instances);
2023-09-17 12:30:17 +02:00
}
related_instance->set_attribute_value(i, new_list);
2023-09-17 12:30:17 +02:00
}
} break;
default:
break;
2023-09-17 12:30:17 +02:00
}
}
}
}
2021-03-13 12:16:40 +01:00
2025-02-27 22:07:31 +01:00
if (entity->declaration().is(*ifcroot_type_) && !entity->get_attribute_value(0).isNull()) {
const std::string global_id = entity->get_attribute_value(0);
auto it = byguid_.find(global_id);
if (it != byguid_.end()) {
byguid_.erase(it);
} else {
2026-06-11 21:04:44 +02:00
logger().Warning("VAL", 19, "GlobalId on rooted instance not encountered in map");
}
}
2023-09-17 12:30:17 +02:00
2025-08-29 13:48:00 +02:00
process_deletion_inverse(entity);
2025-08-31 15:52:46 +02:00
byid_.erase(entity->id());
2023-09-17 12:30:17 +02:00
2025-08-31 15:52:46 +02:00
remove_type_ref(entity);
// entity_file_map is in place to prevent duplicate definitions with usage of add().
// Upon deletion the pairs need to be erased.
for (auto it = entity_file_map_.begin(); it != entity_file_map_.end();) {
if (it->second == entity) {
it = entity_file_map_.erase(it);
} else {
++it;
2023-09-17 12:30:17 +02:00
}
}
2023-09-17 12:30:17 +02:00
delete entity;
}
2023-09-17 12:30:17 +02:00
void IfcParse::impl::in_memory_file_storage::process_deletion_inverse(IfcUtil::IfcBaseClass* entity) {
auto id = entity->id();
2023-09-17 12:30:17 +02:00
// Delete inverses into entity
byref_excl_.erase(
byref_excl_.lower_bound({ id, -1, -1 }),
byref_excl_.upper_bound({ id, std::numeric_limits<short>::max(), std::numeric_limits<short>::max() }));
2023-09-17 12:30:17 +02:00
// This is based on traversal which needs instances to still be contained in the map.
// another option would be to keep byid intact for the remainder of this loop
aggregate_of_instance::ptr entity_attributes = traverse(entity, 1);
for (aggregate_of_instance::it it = entity_attributes->begin(); it != entity_attributes->end(); ++it) {
IfcUtil::IfcBaseClass* entity_attribute = *it;
if (entity_attribute == entity) {
continue;
2023-09-17 12:30:17 +02:00
}
const unsigned int name = entity_attribute->id();
// Do not update inverses for simple types (which have id()==0 in IfcOpenShell).
if (name != 0) {
// Find instances entity -> other
// and update inverses from entity into other
auto lower = byref_excl_.lower_bound({ name, -1, -1 });
auto upper = byref_excl_.upper_bound({ name, std::numeric_limits<short>::max(), std::numeric_limits<short>::max() });
for (auto byref_it = lower; byref_it != upper; ++byref_it) {
auto& ids = byref_it->second;
ids.erase(std::remove(ids.begin(), ids.end(), id), ids.end());
2023-09-17 12:30:17 +02:00
}
}
2025-02-24 20:46:29 +01:00
}
}
2024-08-23 20:29:07 +02:00
namespace {
template <typename Fn>
void visit_subtypes(const IfcParse::entity* ent, Fn fn) {
fn(ent);
for (const auto& st : ent->subtypes()) {
visit_subtypes(st, fn);
}
}
template <typename Fn>
void visit_supertypes(const IfcParse::entity* ent, Fn fn) {
fn(ent);
if (ent->supertype()) {
visit_supertypes(ent->supertype(), fn);
}
}
}
2021-07-29 14:54:51 +02:00
aggregate_of_instance::ptr IfcFile::instances_by_type(const IfcParse::declaration* t) {
2024-08-23 20:29:07 +02:00
aggregate_of_instance::ptr insts(new aggregate_of_instance);
if (t->as_entity() != nullptr) {
visit_subtypes(t->as_entity(), [this, &insts](const IfcParse::entity* ent) {
auto subtype_insts = instances_by_type_excl_subtypes(ent);
// @todo stop returning empty shared_ptrs
if (subtype_insts) {
insts->push(subtype_insts);
2024-08-23 20:29:07 +02:00
}
});
}
return insts;
2011-05-08 11:04:32 +00:00
}
2021-07-29 14:54:51 +02:00
aggregate_of_instance::ptr IfcFile::instances_by_type_excl_subtypes(const IfcParse::declaration* t) {
return std::visit([t](auto& x) {
if constexpr (std::is_same_v<std::decay_t<decltype(x)>, impl::in_memory_file_storage>) {
auto it = x.bytype_excl_.find(t);
return (it == x.bytype_excl_.end()) ? aggregate_of_instance::ptr(new aggregate_of_instance) : it->second;
} else if constexpr (std::is_same_v<std::decay_t<decltype(x)>, impl::rocks_db_file_storage>) {
aggregate_of_instance::ptr ret(new aggregate_of_instance);
auto it = x.bytype_.find(t->index_in_schema());
2025-02-27 22:07:31 +01:00
if (it != x.bytype_.end()) {
const auto& s = it->second;
// @todo generalize this, bytype_ should be a map_adapter
std::vector<size_t> vals(s.size() / sizeof(size_t));
memcpy(vals.data(), s.data(), s.size());
for (auto& v : vals) {
ret->push(x.assert_existance(v, IfcParse::impl::rocks_db_file_storage::entityinstance_ref));
2025-02-27 22:07:31 +01:00
}
}
return ret;
} else {
throw std::runtime_error("Storage not initialized");
aggregate_of_instance::ptr ret(new aggregate_of_instance);
return ret;
}
}, storage_);
}
2021-07-29 14:54:51 +02:00
aggregate_of_instance::ptr IfcFile::instances_by_type(const std::string& t) {
2023-09-17 12:30:17 +02:00
return instances_by_type(schema()->declaration_by_name(t));
2012-07-19 00:14:01 +00:00
}
2021-07-29 14:54:51 +02:00
aggregate_of_instance::ptr IfcFile::instances_by_type_excl_subtypes(const std::string& t) {
2023-09-17 12:30:17 +02:00
return instances_by_type_excl_subtypes(schema()->declaration_by_name(t));
2020-07-11 11:55:00 +02:00
}
2021-07-29 14:54:51 +02:00
aggregate_of_instance::ptr IfcFile::instances_by_reference(int t) {
2023-09-17 12:30:17 +02:00
aggregate_of_instance::ptr ret(new aggregate_of_instance);
std::visit([this, t, &ret](auto& x) {
if constexpr (std::is_same_v<std::decay_t<decltype(x)>, impl::in_memory_file_storage>) {
auto lower = x.byref_excl_.lower_bound({ t, -1, -1 });
auto upper = x.byref_excl_.upper_bound({ t, std::numeric_limits<short>::max(), std::numeric_limits<short>::max() });
for (auto it = lower; it != upper; ++it) {
for (auto& i : it->second) {
ret->push(instance_by_id(i));
}
}
}
#ifdef IFOPSH_WITH_ROCKSDB
else if constexpr (std::is_same_v<std::decay_t<decltype(x)>, impl::rocks_db_file_storage>) {
// @todo no lower/upper_bounds() implemented yet
auto prefix = "v|" + std::to_string(t) + "|";
2025-09-01 14:32:13 +02:00
auto it = std::unique_ptr<rocksdb::Iterator>(x.db->NewIterator(rocksdb::ReadOptions()));
it->Seek(prefix);
while (it->Valid() && it->key().starts_with(prefix)) {
std::vector<uint32_t> vals(it->value().size() / sizeof(uint32_t));
memcpy(vals.data(), it->value().data(), it->value().size());
for (auto& v : vals) {
ret->push(instance_by_id(v));
}
it->Next();
}
}
#endif
else {
throw std::runtime_error("Storage not initialized");
2024-08-23 20:29:07 +02:00
}
}, storage_);
2023-09-17 12:30:17 +02:00
return ret;
2011-05-08 11:04:32 +00:00
}
IfcUtil::IfcBaseClass* IfcFile::instance_by_id(int id) {
2025-02-24 20:46:29 +01:00
return std::visit([id](auto& x) {
if constexpr (std::is_same_v<std::decay_t<decltype(x)>, std::monostate>) {
throw std::runtime_error("Storage not initialized");
return (IfcUtil::IfcBaseClass*) nullptr;
} else {
return x.instance_by_id(id);
}
}, storage_);
2011-05-08 11:04:32 +00:00
}
void IfcParse::IfcFile::add_type_ref(IfcUtil::IfcBaseClass* new_entity)
{
std::visit([new_entity](auto& x) {
if constexpr (std::is_same_v<std::decay_t<decltype(x)>, std::monostate>) {
throw std::runtime_error("Storage not initialized");
} else {
return x.add_type_ref(new_entity);
}
}, storage_);
2012-03-13 11:56:52 +00:00
}
void IfcParse::IfcFile::remove_type_ref(IfcUtil::IfcBaseClass* new_entity)
{
std::visit([new_entity](auto& x) {
if constexpr (std::is_same_v<std::decay_t<decltype(x)>, std::monostate>) {
throw std::runtime_error("Storage not initialized");
} else {
return x.remove_type_ref(new_entity);
}
}, storage_);
}
void IfcParse::IfcFile::process_deletion_inverse(IfcUtil::IfcBaseClass* inst)
{
std::visit([inst](auto& x) {
if constexpr (std::is_same_v<std::decay_t<decltype(x)>, std::monostate>) {
throw std::runtime_error("Storage not initialized");
} else {
return x.process_deletion_inverse(inst);
}
}, storage_);
}
IfcUtil::IfcBaseClass* IfcFile::instance_by_guid(const std::string& guid) {
auto it = byguid_.find(guid);
if (it == byguid_.end()) {
throw IfcException("Instance with GlobalId '" + guid + "' not found");
}
return it->second;
}
IfcFile::type_iterator IfcFile::types_begin() const {
return std::visit([](const auto& x) {
if constexpr (std::is_same_v<std::decay_t<decltype(x)>, std::monostate>) {
2025-03-14 10:20:22 +01:00
return IfcFile::type_iterator{ impl::rocks_db_file_storage::rocksdb_types_iterator{} };
} else if constexpr (std::is_same_v<std::decay_t<decltype(x)>, impl::in_memory_file_storage>) {
2025-03-14 10:20:22 +01:00
return IfcFile::type_iterator{ x.bytype_excl_.begin() };
} else if constexpr (std::is_same_v<std::decay_t<decltype(x)>, impl::rocks_db_file_storage>) {
2025-03-14 10:20:22 +01:00
return IfcFile::type_iterator{ impl::rocks_db_file_storage::rocksdb_types_iterator(&x) };
}
}, storage_);
}
IfcFile::type_iterator IfcFile::types_end() const {
return std::visit([](const auto& x) {
if constexpr (std::is_same_v<std::decay_t<decltype(x)>, std::monostate>) {
2025-03-14 10:20:22 +01:00
return IfcFile::type_iterator{ impl::rocks_db_file_storage::rocksdb_types_iterator{} };
} else if constexpr (std::is_same_v<std::decay_t<decltype(x)>, impl::in_memory_file_storage>) {
2025-03-14 10:20:22 +01:00
return IfcFile::type_iterator{ x.bytype_excl_.end() };
} else if constexpr (std::is_same_v<std::decay_t<decltype(x)>, impl::rocks_db_file_storage>) {
2025-03-14 10:20:22 +01:00
return IfcFile::type_iterator{ impl::rocks_db_file_storage::rocksdb_types_iterator{} };
}
}, storage_);
}
std::ostream& operator<<(std::ostream& out, const IfcParse::IfcFile& file) {
file.header().write(out);
typedef std::vector<IfcUtil::IfcBaseClass*> vector_t;
vector_t sorted;
2025-02-25 21:29:54 +01:00
std::transform(file.begin(), file.end(), std::back_inserter(sorted), [&file](const auto& x) { return x.second; });
std::sort(sorted.begin(), sorted.end(), [](const auto& a, const auto& b) { return a->id() < b->id(); });
2018-11-13 14:46:15 +01:00
for (auto& e : sorted) {
// @todo this check should no longer be necessary?
if (e->declaration().as_entity() != nullptr) {
2024-08-23 20:29:07 +02:00
e->toString(out, true);
out << ";" << std::endl;
2023-09-17 12:30:17 +02:00
}
}
out << "ENDSEC;" << std::endl;
out << "END-ISO-10303-21;" << std::endl;
return out;
}
2024-08-23 20:29:07 +02:00
std::string IfcFile::createTimestamp() {
2023-09-17 12:30:17 +02:00
char buf[255];
time_t t;
time(&t);
struct tm* ti = localtime(&t);
2015-01-16 16:26:40 +00:00
2024-08-23 20:29:07 +02:00
std::string result;
if (strftime(buf, 255, "%Y-%m-%dT%H:%M:%S", ti) != 0U) {
2023-09-17 12:30:17 +02:00
result = std::string(buf);
}
return result;
2015-01-10 22:13:52 +00:00
}
2025-09-19 13:23:04 +02:00
const IfcParse::schema_definition* IfcFile::schema() const {
if (schema_ == nullptr) {
throw IfcException("No schema loaded");
}
return schema_;
}
std::vector<int> IfcFile::get_inverse_indices(int instance_id) {
2023-09-17 12:30:17 +02:00
std::vector<int> return_value;
2023-09-17 12:30:17 +02:00
// Mapping of instance id to attribute offset.
std::map<int, std::vector<int>> mapping;
std::visit([&mapping, instance_id](const auto& x) {
if constexpr (std::is_same_v<std::decay_t<decltype(x)>, std::monostate>) {
} else if constexpr (std::is_same_v<std::decay_t<decltype(x)>, impl::in_memory_file_storage>) {
auto lower = x.byref_excl_.lower_bound({ instance_id, -1, -1 });
auto upper = x.byref_excl_.upper_bound({ instance_id, std::numeric_limits<short>::max(), std::numeric_limits<short>::max() });
for (auto it = lower; it != upper; ++it) {
for (auto& i : it->second) {
mapping[i].push_back(std::get<2>(it->first));
}
2023-09-17 12:30:17 +02:00
}
} else if constexpr (std::is_same_v<std::decay_t<decltype(x)>, impl::rocks_db_file_storage>) {
2025-09-14 13:53:20 +02:00
#ifdef IFOPSH_WITH_ROCKSDB
2025-08-31 21:27:21 +02:00
// @todo no lower/upper_bounds() implemented yet
auto prefix = "v|" + std::to_string(instance_id) + "|";
2025-09-01 14:32:13 +02:00
auto it = std::unique_ptr<rocksdb::Iterator>(x.db->NewIterator(rocksdb::ReadOptions()));
2025-08-31 21:27:21 +02:00
it->Seek(prefix);
while (it->Valid() && it->key().starts_with(prefix)) {
std::vector<uint32_t> vals(it->value().size() / sizeof(uint32_t));
memcpy(vals.data(), it->value().data(), it->value().size());
auto tuple = key_from_string<std::tuple<int, int, int>>(it->key().ToString().substr(2));
for (auto& i : vals) {
mapping[i].push_back(std::get<2>(tuple));
}
it->Next();
}
2025-09-14 13:53:20 +02:00
#endif
2023-09-17 12:30:17 +02:00
}
}, storage_);
2023-09-17 12:30:17 +02:00
auto refs = instances_by_reference(instance_id);
for (const auto& ref : *refs) {
2024-08-23 20:29:07 +02:00
auto it = mapping.find(ref->id());
2023-09-17 12:30:17 +02:00
if (it == mapping.end() || it->second.empty()) {
throw IfcException("Internal error");
}
return_value.push_back(it->second.front());
it->second.erase(it->second.begin());
if (it->second.empty()) {
mapping.erase(it);
}
}
2023-09-17 12:30:17 +02:00
// Test whether all mappings where indeed used.
if (!mapping.empty()) {
throw IfcException("Internal error");
}
return return_value;
}
2021-07-29 14:54:51 +02:00
aggregate_of_instance::ptr IfcFile::getInverse(int instance_id, const IfcParse::declaration* type, int attribute_index) {
2023-09-17 12:30:17 +02:00
if (type == nullptr && attribute_index == -1) {
return instances_by_reference(instance_id);
}
2015-01-10 22:13:52 +00:00
2023-09-17 12:30:17 +02:00
aggregate_of_instance::ptr return_value(new aggregate_of_instance);
2024-08-23 20:29:07 +02:00
visit_subtypes(type->as_entity(), [this, attribute_index, instance_id, &return_value](const IfcParse::declaration* ent) {
2015-01-16 16:26:40 +00:00
std::visit([&return_value, this, attribute_index, instance_id, ent](const auto& x) {
if constexpr (std::is_same_v<std::decay_t<decltype(x)>, std::monostate>) {
} else if constexpr (std::is_same_v<std::decay_t<decltype(x)>, impl::in_memory_file_storage>) {
if (attribute_index == -1) {
auto lower = x.byref_excl_.lower_bound({ instance_id, ent->index_in_schema(), -1 });
auto upper = x.byref_excl_.upper_bound({ instance_id, ent->index_in_schema(), std::numeric_limits<short>::max() });
for (auto it = lower; it != upper; ++it) {
for (auto& i : it->second) {
return_value->push(instance_by_id(i));
}
}
} else {
auto it = x.byref_excl_.find({ instance_id, ent->index_in_schema(), attribute_index });
if (it != x.byref_excl_.end()) {
for (auto& i : it->second) {
return_value->push(instance_by_id(i));
}
}
2024-08-23 20:29:07 +02:00
}
}
#ifdef IFOPSH_WITH_ROCKSDB
else if constexpr (std::is_same_v<std::decay_t<decltype(x)>, impl::rocks_db_file_storage>) {
if (attribute_index == -1) {
// @todo no lower/upper_bounds() implemented yet
auto prefix = "v|" + std::to_string(instance_id) + "|" + std::to_string(ent->index_in_schema()) + "|";
2025-09-01 14:32:13 +02:00
auto it = std::unique_ptr<rocksdb::Iterator>(x.db->NewIterator(rocksdb::ReadOptions()));
it->Seek(prefix);
while (it->Valid() && it->key().starts_with(prefix)) {
std::vector<uint32_t> vals(it->value().size() / sizeof(uint32_t));
memcpy(vals.data(), it->value().data(), it->value().size());
for (auto& v : vals) {
return_value->push(instance_by_id(v));
}
it->Next();
}
} else {
auto it = x.byref_excl_.find({ instance_id, ent->index_in_schema(), attribute_index });
if (it != x.byref_excl_.end()) {
for (auto& i : it->second) {
return_value->push(instance_by_id(i));
}
}
}
2023-09-17 12:30:17 +02:00
}
#endif
}, storage_);
2024-08-23 20:29:07 +02:00
});
2023-09-17 12:30:17 +02:00
return return_value;
}
2024-08-23 20:29:07 +02:00
size_t IfcFile::getTotalInverses(int instance_id) {
size_t n = 0;
std::visit([&n, instance_id](const auto& x) {
if constexpr (std::is_same_v<std::decay_t<decltype(x)>, std::monostate>) {
} else if constexpr (std::is_same_v<std::decay_t<decltype(x)>, impl::in_memory_file_storage>) {
auto lower = x.byref_excl_.lower_bound({ instance_id, -1, -1 });
auto upper = x.byref_excl_.upper_bound({ instance_id, std::numeric_limits<short>::max(), std::numeric_limits<short>::max() });
for (auto it = lower; it != upper; ++it) {
n += it->second.size();
}
} else if constexpr (std::is_same_v<std::decay_t<decltype(x)>, impl::rocks_db_file_storage>) {
// @todo
}
}, storage_);
2024-08-23 20:29:07 +02:00
return n;
2023-09-17 12:30:17 +02:00
}
2015-01-16 16:26:40 +00:00
void IfcFile::setDefaultHeaderValues() {
2024-08-23 20:29:07 +02:00
const std::string empty_string;
std::vector<std::string> file_description;
std::vector<std::string> schema_identifiers;
std::vector<std::string> string_vector = {""};
2015-01-16 16:26:40 +00:00
2023-09-17 12:30:17 +02:00
file_description.push_back("ViewDefinition [CoordinationView]");
if (schema() != nullptr) {
2023-09-17 12:30:17 +02:00
schema_identifiers.push_back(schema()->name());
}
2015-01-16 16:26:40 +00:00
2025-02-27 22:07:31 +01:00
header().file_description()->setdescription(file_description);
header().file_description()->setimplementation_level("2;1");
2015-01-16 16:26:40 +00:00
2025-02-27 22:07:31 +01:00
header().file_name()->setname(empty_string);
header().file_name()->settime_stamp(createTimestamp());
header().file_name()->setauthor(string_vector);
header().file_name()->setorganization(string_vector);
header().file_name()->setpreprocessor_version("IfcOpenShell " + std::string(IFCOPENSHELL_VERSION));
header().file_name()->setoriginating_system("IfcOpenShell " + std::string(IFCOPENSHELL_VERSION));
header().file_name()->setauthorization(empty_string);
2015-01-16 16:26:40 +00:00
2025-02-27 22:07:31 +01:00
header().file_schema()->setschema_identifiers(schema_identifiers);
2015-01-16 16:26:40 +00:00
}
std::pair<IfcUtil::IfcBaseClass*, double> IfcFile::getUnit(const std::string& unit_type) {
2023-09-17 12:30:17 +02:00
std::pair<IfcUtil::IfcBaseClass*, double> return_value(0, 1.);
2023-09-17 12:30:17 +02:00
aggregate_of_instance::ptr projects = instances_by_type(schema()->declaration_by_name("IfcProject"));
if (!projects || projects->size() == 0) {
try {
projects = instances_by_type(schema()->declaration_by_name("IfcContext"));
2024-08-23 20:29:07 +02:00
} catch (IfcException&) {
2023-09-17 12:30:17 +02:00
}
}
2023-09-17 12:30:17 +02:00
if (projects && projects->size() == 1) {
IfcUtil::IfcBaseClass* project = *projects->begin();
2025-02-27 22:07:31 +01:00
IfcUtil::IfcBaseClass* unit_assignment = project->get_attribute_value(
2023-09-17 12:30:17 +02:00
project->declaration().as_entity()->attribute_index("UnitsInContext"));
2025-02-27 22:07:31 +01:00
aggregate_of_instance::ptr units = unit_assignment->get_attribute_value(
2023-09-17 12:30:17 +02:00
unit_assignment->declaration().as_entity()->attribute_index("Units"));
for (aggregate_of_instance::it it = units->begin(); it != units->end(); ++it) {
IfcUtil::IfcBaseClass* unit = *it;
if (unit->declaration().is("IfcNamedUnit")) {
2025-02-27 22:07:31 +01:00
const std::string file_unit_type = unit->get_attribute_value(
2023-09-17 12:30:17 +02:00
unit->declaration().as_entity()->attribute_index("UnitType"));
if (file_unit_type != unit_type) {
continue;
}
IfcUtil::IfcBaseClass* siunit = 0;
if (unit->declaration().is("IfcConversionBasedUnit")) {
2025-02-27 22:07:31 +01:00
IfcUtil::IfcBaseClass* mu = unit->get_attribute_value(
2023-09-17 12:30:17 +02:00
unit->declaration().as_entity()->attribute_index("ConversionFactor"));
2025-02-27 22:07:31 +01:00
IfcUtil::IfcBaseClass* vlc = mu->get_attribute_value(
2023-09-17 12:30:17 +02:00
mu->declaration().as_entity()->attribute_index("ValueComponent"));
2025-02-27 22:07:31 +01:00
IfcUtil::IfcBaseClass* unc = mu->get_attribute_value(
2023-09-17 12:30:17 +02:00
mu->declaration().as_entity()->attribute_index("UnitComponent"));
2025-02-27 22:07:31 +01:00
return_value.second *= static_cast<double>(vlc->get_attribute_value(0));
2023-09-17 12:30:17 +02:00
return_value.first = unit;
if (unc->declaration().is("IfcSIUnit")) {
siunit = unc;
}
} else if (unit->declaration().is("IfcSIUnit")) {
return_value.first = siunit = unit;
}
if (siunit != nullptr) {
2025-02-27 22:07:31 +01:00
AttributeValue prefix = siunit->get_attribute_value(
2023-09-17 12:30:17 +02:00
siunit->declaration().as_entity()->attribute_index("Prefix"));
2024-08-23 20:29:07 +02:00
if (!prefix.isNull()) {
return_value.second *= IfcSIPrefixToValue(prefix);
2023-09-17 12:30:17 +02:00
}
}
}
}
}
return return_value;
2016-05-15 15:47:21 +02:00
}
2018-08-29 12:54:25 +02:00
void IfcParse::IfcFile::build_inverses_(IfcUtil::IfcBaseClass* inst) {
2023-09-17 12:30:17 +02:00
std::function<void(IfcUtil::IfcBaseClass*, int)> fn = [this, inst](IfcUtil::IfcBaseClass* attr, int idx) {
if (attr->declaration().as_entity() != nullptr) {
2024-08-23 20:29:07 +02:00
unsigned entity_attribute_id = attr->id();
const auto* decl = inst->declaration().as_entity();
std::visit([entity_attribute_id, decl, idx, inst](auto& x) {
if constexpr (std::is_same_v<std::decay_t<decltype(x)>, std::monostate>) {
} else if constexpr (std::is_same_v<std::decay_t<decltype(x)>, impl::in_memory_file_storage>) {
x.byref_excl_[{entity_attribute_id, decl->index_in_schema(), idx}].push_back(inst->id());
} else if constexpr (std::is_same_v<std::decay_t<decltype(x)>, impl::rocks_db_file_storage>) {
// @todo
}
}, storage_);
2023-09-17 12:30:17 +02:00
}
};
2025-02-27 22:07:31 +01:00
apply_individual_instance_visitor(inst).apply(fn);
2018-08-29 12:54:25 +02:00
}
2025-08-31 15:52:46 +02:00
void IfcParse::IfcFile::unbatch() {
for (auto& id : batch_deletion_ids_) {
process_deletion_(instance_by_id(id));
}
batch_mode_ = false;
batch_deletion_ids_.clear();
}
void IfcParse::IfcFile::reset_identity_cache() {
std::visit([](auto& x) {
if constexpr (std::is_same_v<std::decay_t<decltype(x)>, impl::rocks_db_file_storage>) {
x.instance_cache_.clear();
x.type_instance_cache_.clear();
}
}, storage_);
}
2018-08-29 12:54:25 +02:00
void IfcParse::IfcFile::build_inverses() {
for (const auto& pair : *this) {
2025-02-25 21:29:54 +01:00
build_inverses_(pair.second);
2023-09-17 12:30:17 +02:00
}
}
2021-09-11 12:59:48 +02:00
2025-02-24 20:46:29 +01:00
void IfcParse::IfcFile::register_inverse(unsigned id_from, const IfcParse::entity* from_entity, int inst_id, int attribute_index)
{
std::visit([id_from, from_entity, inst_id, attribute_index](auto& x) {
if constexpr (std::is_same_v<std::decay_t<decltype(x)>, std::monostate>) {
throw std::runtime_error("Storage not initialized");
} else {
return x.register_inverse(id_from, from_entity, inst_id, attribute_index);
}
}, storage_);
}
void IfcParse::IfcFile::unregister_inverse(unsigned id_from, const IfcParse::entity* from_entity, IfcUtil::IfcBaseClass* inst, int attribute_index)
{
std::visit([id_from, from_entity, inst, attribute_index](auto& x) {
if constexpr (std::is_same_v<std::decay_t<decltype(x)>, std::monostate>) {
throw std::runtime_error("Storage not initialized");
} else {
return x.unregister_inverse(id_from, from_entity, inst, attribute_index);
}
}, storage_);
}
2021-09-13 13:17:08 +02:00
std::atomic_uint32_t IfcUtil::IfcBaseClass::counter_(0);
// bool IfcParse::IfcFile::guid_map_ = true;
2024-08-23 20:29:07 +02:00
void IfcUtil::IfcBaseClass::unset_attribute_value(size_t index) {
2025-02-27 22:07:31 +01:00
void* storage = file_ ? std::visit([](const auto& m) { return (void*)&m; }, file_->storage_) : nullptr;
data_.set_attribute_value(storage, &declaration(), id() ? id() : identity(), index, Blank{});
}
AttributeValue IfcUtil::IfcBaseClass::get_attribute_value(size_t index) const {
2025-02-27 22:07:31 +01:00
void* storage = file_ ? std::visit([](const auto& m) { return (void*)&m; }, file_->storage_) : nullptr;
return data_.get_attribute_value(storage, &declaration(), id() ? id() : identity(), index);
2024-08-23 20:29:07 +02:00
}
void IfcUtil::IfcBaseClass::toString(std::ostream& out, bool upper) const
{
const auto *ent = declaration().as_entity();
2025-08-28 11:15:18 +02:00
if (ent != nullptr && declaration().schema() != &Header_section_schema::get_schema()) {
2024-08-23 20:29:07 +02:00
out << "#" << as<IfcUtil::IfcBaseEntity>()->id() << "=";
}
if (upper) {
out << declaration().name_uc();
} else {
out << declaration().name();
}
2025-02-27 22:07:31 +01:00
void* storage = file_ ? std::visit([](const auto& m) { return (void*)&m; }, file_->storage_) : nullptr;
data().toString(storage, &declaration(), id() ? id() : identity(), out, upper);
2024-08-23 20:29:07 +02:00
}
2025-02-27 22:07:31 +01:00
/*
2024-08-23 20:29:07 +02:00
IfcEntityInstanceData::IfcEntityInstanceData(const IfcEntityInstanceData& data)
: storage_(data.size())
2024-08-23 20:29:07 +02:00
{
2025-02-27 22:07:31 +01:00
2024-08-23 20:29:07 +02:00
}
2025-02-27 22:07:31 +01:00
*/
2024-08-23 20:29:07 +02:00
2025-02-27 22:07:31 +01:00
AttributeValue IfcEntityInstanceData::get_attribute_value(void* storage, const IfcParse::declaration* decl, std::size_t identity, size_t index) const
2024-08-23 20:29:07 +02:00
{
2025-03-18 10:41:03 +01:00
if (storage_) {
return AttributeValue(storage_, (uint8_t)index);
} else {
2025-10-13 20:47:43 +02:00
return AttributeValue((IfcParse::impl::rocks_db_file_storage*)storage, identity, decl, (uint8_t) index);
2025-03-18 10:41:03 +01:00
}
2024-08-23 20:29:07 +02:00
}
2025-02-27 22:07:31 +01:00
bool IfcParse::impl::rocks_db_file_storage::read_schema(const IfcParse::schema_definition*& schema) {
#ifdef IFOPSH_WITH_ROCKSDB
2025-02-27 22:07:31 +01:00
std::string value;
auto key = "h|file_schema|0";
db->Get(rocksdb::ReadOptions{}, key, &value);
std::vector<std::string> strings;
if (::impl::deserialize(this, value, strings) && strings.size() == 1) {
try {
schema = schema_by_name(strings[0]);
} catch (IfcException&) {
return false;
}
2025-02-27 22:07:31 +01:00
return true;
}
#endif
2025-02-27 22:07:31 +01:00
return false;
}
IfcUtil::IfcBaseClass::IfcBaseClass(IfcEntityInstanceData&& data)
: identity_(counter_++)
, id_(0)
, file_(nullptr)
, data_(std::move(data))
{
/*
* @todo this is not allowed cannot call virtual func in constructor
if (!declaration().as_entity()) {
// @nb from v0.9 type decl instances have their own id, which may collide with instance names in the file
// but is otherwise unique
id_ = identity_;
}
*/
}
2025-02-27 22:07:31 +01:00
2025-08-26 10:19:47 +02:00
void IfcUtil::IfcBaseClass::set_attribute_value(size_t i, IfcUtil::IfcBaseClass* p) {
set_attribute_value<IfcUtil::IfcBaseClass*>(i, p);
}
void IfcUtil::IfcBaseClass::set_attribute_value(const std::string& name, IfcUtil::IfcBaseClass* p) {
set_attribute_value<IfcUtil::IfcBaseClass*>(name, p);
}
2024-08-23 20:29:07 +02:00
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<Blank>(size_t index, const Blank& value);
2025-02-27 22:07:31 +01:00
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<Derived>(size_t index, const Derived& value);
2024-08-23 20:29:07 +02:00
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<int>(size_t index, const int& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<bool>(size_t index, const bool& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<boost::logic::tribool>(size_t index, const boost::logic::tribool& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<double>(size_t index, const double& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<std::string>(size_t index, const std::string& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<boost::dynamic_bitset<>>(size_t index, const boost::dynamic_bitset<>& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<EnumerationReference>(size_t index, const EnumerationReference& value);
2025-08-26 10:19:47 +02:00
// template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<IfcUtil::IfcBaseClass*>(size_t index, IfcUtil::IfcBaseClass* const& value);
2024-08-23 20:29:07 +02:00
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<std::vector<int>>(size_t index, const std::vector<int>& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<std::vector<double>>(size_t index, const std::vector<double>& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<std::vector<std::string>>(size_t index, const std::vector<std::string>& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<std::vector<boost::dynamic_bitset<>>>(size_t index, const std::vector<boost::dynamic_bitset<>>& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<aggregate_of_instance::ptr>(size_t index, const aggregate_of_instance::ptr& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<std::vector<std::vector<int>>>(size_t index, const std::vector<std::vector<int>>& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<std::vector<std::vector<double>>>(size_t index, const std::vector<std::vector<double>>& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<aggregate_of_aggregate_of_instance::ptr>(size_t index, const aggregate_of_aggregate_of_instance::ptr& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<Blank>(const std::string& name, const Blank& value);
2025-02-27 22:07:31 +01:00
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<Derived>(const std::string& name, const Derived& value);
2024-08-23 20:29:07 +02:00
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<int>(const std::string& name, const int& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<bool>(const std::string& name, const bool& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<boost::logic::tribool>(const std::string& name, const boost::logic::tribool& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<double>(const std::string& name, const double& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<std::string>(const std::string& name, const std::string& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<boost::dynamic_bitset<>>(const std::string& name, const boost::dynamic_bitset<>& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<EnumerationReference>(const std::string& name, const EnumerationReference& value);
2025-08-26 10:19:47 +02:00
// template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<IfcUtil::IfcBaseClass*>(const std::string& name, IfcUtil::IfcBaseClass* const& value);
2024-08-23 20:29:07 +02:00
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<std::vector<int>>(const std::string& name, const std::vector<int>& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<std::vector<double>>(const std::string& name, const std::vector<double>& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<std::vector<std::string>>(const std::string& name, const std::vector<std::string>& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<std::vector<boost::dynamic_bitset<>>>(const std::string& name, const std::vector<boost::dynamic_bitset<>>& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<aggregate_of_instance::ptr>(const std::string& name, const aggregate_of_instance::ptr& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<std::vector<std::vector<int>>>(const std::string& name, const std::vector<std::vector<int>>& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<std::vector<std::vector<double>>>(const std::string& name, const std::vector<std::vector<double>>& value);
template void IFC_PARSE_API IfcUtil::IfcBaseClass::set_attribute_value<aggregate_of_aggregate_of_instance::ptr>(const std::string& name, const aggregate_of_aggregate_of_instance::ptr& value);