2012-08-11 13:24:08 +00:00
/********************************************************************************
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
2026-03-31 15:32:36 +02:00
# include "parse.h"
2023-09-17 12:30:17 +02:00
2026-01-04 10:40:02 +01:00
# include "express.h"
2026-03-31 15:32:36 +02:00
# include "character_decoder.h"
# include "exception.h"
# include "file.h"
# include "logger.h"
# include "schema.h"
# include "si_prefix.h"
# include "file_reader.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>
2019-07-03 12:10:27 +02:00
# include <ctime>
2023-09-17 12:30:17 +02:00
# include <set>
2019-07-03 12:10:27 +02:00
# include <stdio.h>
# include <stdlib.h>
2026-07-26 16:32:47 +10:00
# include <cstring>
2023-09-17 12:30:17 +02:00
# include <string>
2024-08-23 20:29:07 +02:00
# include <iomanip>
2026-06-11 15:51:40 +02:00
# include <charconv>
2026-07-09 22:00:41 +10:00
# include <type_traits>
// Apple clang's libc++ has no floating-point std::from_chars overload (it's
// =deleted), so on macOS doubles are parsed via strtod_l with a cached "C"
// locale — locale-independent, unlike strtod. Other platforms (libstdc++,
// MSVC STL) have working float from_chars and are left unchanged.
# if defined(__APPLE__)
# include <xlocale.h>
# include <locale.h>
# endif
2019-07-03 12:10:27 +02:00
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
2026-03-31 15:32:36 +02:00
using namespace ifcopenshell ;
2011-05-08 11:04:32 +00:00
2026-03-27 20:45:13 +01:00
template < typename Reader >
2026-07-09 13:30:48 +02:00
spf_lexer < Reader > : : spf_lexer ( Reader * stream_ , : : logger & log )
: decoder_ ( nullptr )
, logger_ ( log ) {
2023-11-06 20:29:00 +01:00
stream = stream_ ;
2026-07-09 13:30:48 +02:00
decoder_ = new character_decoder < Reader > ( stream_ , logger_ ) ;
2011-09-25 09:53:22 +00:00
}
2026-03-27 20:45:13 +01:00
template < typename Reader >
2026-03-31 15:32:36 +02:00
spf_lexer < Reader > : : ~ spf_lexer ( ) {
2023-11-06 20:38:19 +01:00
delete decoder_ ;
2011-07-11 09:33:18 +00:00
}
2026-03-27 20:45:13 +01:00
template < typename Reader >
2026-03-31 15:32:36 +02:00
size_t spf_lexer < Reader > : : skip_whitespace ( ) const {
2025-10-13 20:47:43 +02:00
size_t index = 0 ;
while ( ! stream - > eof ( ) ) {
char character = stream - > peek ( ) ;
2023-11-06 20:29:00 +01:00
if ( ( character = = ' ' | | character = = ' \r ' | | character = = ' \n ' | | character = = ' \t ' ) ) {
2025-10-13 20:47:43 +02:00
stream - > increment ( ) ;
2023-11-06 20:29:00 +01:00
+ + index ;
2023-09-17 12:30:17 +02:00
} else {
break ;
}
}
2023-11-06 20:29:00 +01:00
return index ;
2014-04-02 15:04:09 +00:00
}
2026-03-27 20:45:13 +01:00
template < typename Reader >
2026-03-31 15:32:36 +02:00
size_t spf_lexer < Reader > : : skip_comment ( ) const {
2025-10-25 20:14:02 +02:00
if ( stream - > eof ( ) ) {
return 0 ;
}
2025-10-13 20:47:43 +02:00
char character = stream - > peek ( ) ;
2023-11-06 20:29:00 +01:00
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 ( ) ;
2023-11-06 20:29:00 +01:00
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 ;
2023-11-06 20:29:00 +01:00
char intermediate = 0 ;
2025-10-13 20:47:43 +02:00
while ( ! stream - > eof ( ) ) {
character = stream - > peek ( ) ;
stream - > increment ( ) ;
2023-11-06 20:29:00 +01:00
+ + index ;
if ( character = = ' / ' & & intermediate = = ' * ' ) {
2023-09-17 12:30:17 +02:00
break ;
}
2023-11-06 20:29:00 +01:00
intermediate = character ;
2023-09-17 12:30:17 +02:00
}
2023-11-06 20:29:00 +01:00
return index ;
2014-04-02 15:04:09 +00:00
}
2026-03-27 20:45:13 +01:00
template < typename Reader >
2026-03-31 15:32:36 +02:00
std : : string & spf_lexer < Reader > : : get_temp_string ( ) const {
2026-03-26 15:49:28 +01:00
const size_t idx = pool_index + + ;
const size_t slice = idx > > 4 ;
const size_t offset = idx & 0xF ;
while ( stringpool_ . size ( ) < = slice ) {
stringpool_ . push_back ( std : : make_unique < std : : array < std : : string , 16 > > ( ) ) ;
}
2026-03-27 20:45:13 +01:00
// std::wcout << "Num contexts: " << idx << std::endl;
2026-03-26 15:49:28 +01:00
return ( * stringpool_ [ slice ] ) [ offset ] ;
}
namespace {
2026-07-26 19:37:11 +10:00
# if defined(__APPLE__) || defined(__EMSCRIPTEN__)
2026-07-09 22:00:41 +10:00
double parse_double_c ( const char * start , char * * end ) {
static const locale_t loc = newlocale ( LC_NUMERIC_MASK , " C " , ( locale_t ) 0 ) ;
return strtod_l ( start , end , loc ) ;
}
# endif
2026-06-11 15:51:40 +02:00
template < typename T >
bool parse_num_ ( const char * pStart , size_t size , T & val ) {
if ( size = = 0 ) {
2026-03-26 15:49:28 +01:00
return false ;
}
2026-06-11 15:51:40 +02:00
if ( * pStart = = ' + ' ) {
+ + pStart ;
- - size ;
if ( size = = 0 ) {
return false ;
}
}
2026-07-09 22:00:41 +10:00
if constexpr ( std : : is_floating_point_v < T > ) {
2026-07-26 19:37:11 +10:00
# if defined(__APPLE__) || defined(__EMSCRIPTEN__)
2026-07-09 22:00:41 +10:00
// pStart is NUL-terminated at pStart + size (callers pass c_str()), so
// strtod_l stops exactly at the end of a well-formed number. from_chars
// is not instantiated for double here — its float overload is =deleted
2026-07-26 19:37:11 +10:00
// in libc++ (Apple's and Emscripten's).
2026-07-09 22:00:41 +10:00
char * pEnd = nullptr ;
const double result = parse_double_c ( pStart , & pEnd ) ;
if ( pEnd ! = pStart + size ) {
return false ;
}
val = static_cast < T > ( result ) ;
return true ;
# else
auto re = std : : from_chars ( pStart , pStart + size , val ) ;
return re . ec = = std : : errc ( ) & & re . ptr = = pStart + size ;
# endif
} else {
auto re = std : : from_chars ( pStart , pStart + size , val ) ;
return re . ec = = std : : errc ( ) & & re . ptr = = pStart + size ;
2026-03-26 15:49:28 +01:00
}
}
} // namespace
2026-03-27 20:45:13 +01:00
namespace SWAR {
constexpr uint32_t ONES32 = 0x01010101u ;
constexpr uint32_t HIGHS32 = 0x80808080u ;
constexpr uint64_t ONES = 0x0101010101010101ull ;
constexpr uint64_t HIGHS = 0x8080808080808080ull ;
constexpr uint64_t splat ( unsigned char c ) {
return ONES * c ;
}
inline uint32_t has_zero_byte ( uint32_t x ) {
return ( x - ONES32 ) & ~ x & HIGHS32 ;
}
inline uint64_t has_zero_byte ( uint64_t x ) {
return ( x - ONES ) & ~ x & HIGHS ;
}
inline uint32_t eq_mask ( uint32_t x , uint32_t c ) {
return has_zero_byte ( x ^ c ) ;
}
inline uint64_t eq_mask ( uint64_t x , uint64_t c ) {
return has_zero_byte ( x ^ c ) ;
}
namespace chars {
constexpr uint64_t lpar = splat ( ' ( ' ) ;
constexpr uint64_t rpar = splat ( ' ) ' ) ;
constexpr uint64_t eq = splat ( ' = ' ) ;
constexpr uint64_t comma = splat ( ' , ' ) ;
constexpr uint64_t semi = splat ( ' ; ' ) ;
constexpr uint64_t slash = splat ( ' / ' ) ;
constexpr uint64_t space = splat ( ' ' ) ;
constexpr uint64_t cr = splat ( ' \r ' ) ;
constexpr uint64_t lf = splat ( ' \n ' ) ;
constexpr uint64_t tab = splat ( ' \t ' ) ;
constexpr uint64_t quote = splat ( ' " ' ) ;
constexpr uint64_t dot = splat ( ' . ' ) ;
} // namespace chars
2026-03-27 21:04:48 +01:00
template < bool IncludeDot = true >
2026-03-27 20:45:13 +01:00
inline uint64_t has_special_char ( uint64_t x ) {
return eq_mask ( x , chars : : lpar ) |
eq_mask ( x , chars : : rpar ) |
eq_mask ( x , chars : : eq ) |
eq_mask ( x , chars : : comma ) |
eq_mask ( x , chars : : semi ) |
eq_mask ( x , chars : : slash ) |
eq_mask ( x , chars : : space ) |
eq_mask ( x , chars : : cr ) |
eq_mask ( x , chars : : lf ) |
eq_mask ( x , chars : : tab ) |
eq_mask ( x , chars : : quote ) |
2026-03-27 21:04:48 +01:00
( IncludeDot ? eq_mask ( x , chars : : dot ) : uint64_t { 0 } ) ;
2026-03-27 20:45:13 +01:00
}
2026-03-27 21:04:48 +01:00
template < bool IncludeDot = true >
2026-03-27 20:45:13 +01:00
inline uint32_t has_special_char ( uint32_t x ) {
return eq_mask ( x , static_cast < uint32_t > ( chars : : lpar ) ) |
eq_mask ( x , static_cast < uint32_t > ( chars : : rpar ) ) |
eq_mask ( x , static_cast < uint32_t > ( chars : : eq ) ) |
eq_mask ( x , static_cast < uint32_t > ( chars : : comma ) ) |
eq_mask ( x , static_cast < uint32_t > ( chars : : semi ) ) |
eq_mask ( x , static_cast < uint32_t > ( chars : : slash ) ) |
eq_mask ( x , static_cast < uint32_t > ( chars : : space ) ) |
eq_mask ( x , static_cast < uint32_t > ( chars : : cr ) ) |
eq_mask ( x , static_cast < uint32_t > ( chars : : lf ) ) |
eq_mask ( x , static_cast < uint32_t > ( chars : : tab ) ) |
eq_mask ( x , static_cast < uint32_t > ( chars : : quote ) ) |
2026-03-27 21:04:48 +01:00
( IncludeDot ? eq_mask ( x , static_cast < uint32_t > ( chars : : dot ) ) : uint32_t { 0 } ) ;
2026-03-27 20:45:13 +01:00
}
}
2011-07-11 09:33:18 +00:00
//
2026-03-31 15:32:36 +02:00
// Returns the offset of the current token and moves cursor to next
2011-07-11 09:33:18 +00:00
//
2026-03-27 20:45:13 +01:00
template < typename Reader >
2026-03-31 15:32:36 +02:00
token spf_lexer < Reader > : : next ( ) {
2011-07-11 09:33:18 +00:00
2025-10-13 20:47:43 +02:00
if ( stream - > eof ( ) ) {
2026-03-31 15:32:36 +02:00
return token { } ;
2023-09-17 12:30:17 +02:00
}
2025-10-13 20:47:43 +02:00
auto pos = stream - > tell ( ) ;
char character = stream - > read ( ) ;
2011-07-11 09:33:18 +00:00
2026-03-27 20:45:13 +01:00
if ( character = = ' / ' | | character = = ' ' | | character = = ' \r ' | | character = = ' \n ' | | character = = ' \t ' ) {
2026-03-31 15:32:36 +02:00
while ( ( skip_whitespace ( ) ! = 0U ) | | ( skip_comment ( ) ! = 0U ) ) {
2026-03-27 20:45:13 +01:00
}
if ( stream - > eof ( ) ) {
2026-03-31 15:32:36 +02:00
return token { } ;
2026-03-27 20:45:13 +01:00
}
pos = stream - > tell ( ) ;
character = stream - > read ( ) ;
}
2023-09-17 12:30:17 +02:00
// If the cursor is at [()=,;$*] we know token consists of single char
2023-11-06 20:29:00 +01:00
if ( character = = ' ( ' | |
character = = ' ) ' | |
character = = ' = ' | |
character = = ' , ' | |
character = = ' ; ' | |
character = = ' $ ' | |
2025-10-13 20:47:43 +02:00
character = = ' * ' )
{
2026-03-31 15:32:36 +02:00
return token ( pos , character ) ;
2023-09-17 12:30:17 +02:00
}
2011-07-11 09:33:18 +00:00
2026-03-31 15:32:36 +02:00
auto & str = get_temp_string ( ) ;
2026-03-26 15:49:28 +01:00
2025-10-13 20:47:43 +02:00
if ( character = = ' \' ' ) {
2026-03-31 15:32:36 +02:00
// If a string is encountered defer processing to the character_decoder
2025-10-13 20:47:43 +02:00
str = * decoder_ ;
2026-03-31 15:32:36 +02:00
return token ( pos , token : : Token_STRING , str ) ;
2024-08-23 20:29:07 +02:00
} else {
2026-03-31 15:32:36 +02:00
auto ttype = token : : Token_NONE ;
2026-03-26 15:49:28 +01:00
if ( character = = ' " ' | | character = = ' . ' ) {
if ( character = = ' " ' ) {
2026-03-31 15:32:36 +02:00
ttype = token : : Token_BINARY ;
2026-03-26 15:49:28 +01:00
} else {
2026-03-31 15:32:36 +02:00
ttype = token : : Token_ENUMERATION ;
2026-03-26 15:49:28 +01:00
}
str . clear ( ) ;
} else if ( character = = ' # ' ) {
2026-03-31 15:32:36 +02:00
ttype = token : : Token_IDENTIFIER ;
2026-03-26 15:49:28 +01:00
str . clear ( ) ;
} else {
str . assign ( & character , 1 ) ;
}
2025-10-13 20:47:43 +02:00
2026-03-27 21:04:48 +01:00
auto remaining = stream - > remaining ( ) ;
while ( remaining ) {
if ( remaining > = 8 ) {
2026-03-27 20:45:13 +01:00
uint64_t x = stream - > peek_u64 ( ) ;
2026-03-31 15:32:36 +02:00
if ( ( ttype = = token : : Token_NONE ? SWAR : : has_special_char < false > ( x ) : SWAR : : has_special_char < true > ( x ) ) = = 0 ) {
2026-03-27 20:45:13 +01:00
str . append ( reinterpret_cast < const char * > ( & x ) , 8 ) ;
stream - > increment ( 8 ) ;
2026-03-27 21:04:48 +01:00
remaining - = 8 ;
2026-03-27 20:45:13 +01:00
continue ;
}
}
2026-03-27 21:04:48 +01:00
if ( remaining > = 4 ) {
2026-03-27 20:45:13 +01:00
uint32_t x = stream - > peek_u32 ( ) ;
2026-03-31 15:32:36 +02:00
if ( ( ttype = = token : : Token_NONE ? SWAR : : has_special_char < false > ( x ) : SWAR : : has_special_char < true > ( x ) ) = = 0 ) {
2026-03-27 20:45:13 +01:00
str . append ( reinterpret_cast < const char * > ( & x ) , 4 ) ;
stream - > increment ( 4 ) ;
2026-03-27 21:04:48 +01:00
remaining - = 4 ;
2026-03-27 20:45:13 +01:00
continue ;
}
}
2025-10-13 20:47:43 +02:00
// Read character and increment pointer if not starting a new token
2026-03-27 20:45:13 +01:00
char character = stream - > peek ( ) ;
2025-10-13 20:47:43 +02:00
if ( character = = ' ( ' | |
character = = ' ) ' | |
character = = ' = ' | |
character = = ' , ' | |
character = = ' ; ' | |
character = = ' / ' ) {
break ;
}
2025-10-25 13:55:15 +02:00
if ( ! ( character = = ' ' | | character = = ' \r ' | | character = = ' \n ' | | character = = ' \t ' ) ) {
2026-03-31 15:32:36 +02:00
if ( ( ttype = = token : : Token_BINARY & & character = = ' " ' ) | |
( ttype = = token : : Token_ENUMERATION & & character = = ' . ' ) ) {
2026-03-26 15:49:28 +01:00
// Skip
} else {
str . push_back ( character ) ;
}
2025-10-25 13:55:15 +02:00
}
2025-10-13 20:47:43 +02:00
stream - > increment ( ) ;
2026-03-27 21:04:48 +01:00
remaining - = 1 ;
2025-10-13 20:47:43 +02:00
}
2011-07-11 09:33:18 +00:00
2026-03-31 15:32:36 +02:00
if ( ttype = = token : : Token_ENUMERATION & & str . size ( ) = = 1 & & ( str [ 0 ] = = ' T ' | | str [ 0 ] = = ' F ' | | str [ 0 ] = = ' U ' ) ) {
pop_pool_entry ( ) ;
return token ( pos , token : : Token_BOOL , str [ 0 ] ) ;
} else if ( ttype = = token : : Token_IDENTIFIER ) {
2026-03-26 15:49:28 +01:00
int int_val ;
2026-06-11 15:51:40 +02:00
if ( ! parse_num_ ( str . c_str ( ) , str . size ( ) , int_val ) ) {
2026-03-31 15:32:36 +02:00
throw invalid_token_exception ( pos , str , " instance name " ) ;
2026-03-26 15:49:28 +01:00
}
2026-03-31 15:32:36 +02:00
pop_pool_entry ( ) ;
2026-07-19 11:44:31 +03:00
return token ( pos , ttype , ( int64_t ) int_val ) ;
2026-03-31 15:32:36 +02:00
} else if ( ttype = = token : : Token_NONE & & ! str . empty ( ) ) {
2026-07-19 11:44:31 +03:00
int64_t int_val ;
2026-03-26 15:49:28 +01:00
double float_val ;
auto & first = str . front ( ) ;
if ( ( first > = ' A ' & & first < = ' Z ' ) | | ( first > = ' a ' & & first < = ' z ' ) ) {
2026-03-31 15:32:36 +02:00
ttype = token : : Token_KEYWORD ;
return token ( pos , ttype , str ) ;
2026-06-11 15:51:40 +02:00
} else if ( parse_num_ ( str . c_str ( ) , str . size ( ) , int_val ) ) {
2026-03-31 15:32:36 +02:00
ttype = token : : Token_INT ;
pop_pool_entry ( ) ;
return token ( pos , ttype , int_val ) ;
2026-06-11 15:51:40 +02:00
} else if ( parse_num_ ( str . c_str ( ) , str . size ( ) , float_val ) ) {
2026-03-31 15:32:36 +02:00
ttype = token : : Token_FLOAT ;
pop_pool_entry ( ) ;
return token ( pos , float_val ) ;
2026-03-26 15:49:28 +01:00
}
2026-03-31 15:32:36 +02:00
} else if ( ttype = = token : : Token_BINARY | | ttype = = token : : Token_ENUMERATION ) {
return token ( pos , ttype , str ) ;
2023-09-17 12:30:17 +02:00
}
2011-07-11 09:33:18 +00:00
2026-03-31 15:32:36 +02:00
throw invalid_token_exception ( pos , str , " valid token " ) ;
2023-09-17 12:30:17 +02:00
}
2016-06-09 22:25:50 +06:00
}
2026-04-24 13:36:06 +02:00
template class IFC_PARSE_API ifcopenshell : : spf_lexer < file_reader < full_buffer_impl > > ;
template class IFC_PARSE_API ifcopenshell : : spf_lexer < file_reader < paged_file_impl > > ;
template class IFC_PARSE_API ifcopenshell : : spf_lexer < file_reader < pushed_sequential_impl > > ;
2026-03-27 20:45:13 +01:00
# ifdef USE_MMAP
2026-04-24 13:36:06 +02:00
template class IFC_PARSE_API ifcopenshell : : spf_lexer < file_reader < mmap_impl > > ;
2026-03-27 20:45:13 +01:00
# endif
2026-03-31 15:32:36 +02:00
bool token : : is_operator ( ) {
2026-03-26 15:49:28 +01:00
return type = = Token_OPERATOR ;
2023-09-17 12:30:17 +02:00
}
2021-07-29 14:54:51 +02:00
2026-03-31 15:32:36 +02:00
bool token : : is_operator ( char character ) {
2026-03-26 15:49:28 +01:00
return type = = Token_OPERATOR & & value_char = = character ;
2016-06-09 22:25:50 +06:00
}
2026-03-31 15:32:36 +02:00
bool token : : is_identifier ( ) {
2026-03-26 15:49:28 +01:00
return type = = Token_IDENTIFIER ;
2016-06-09 22:25:50 +06:00
}
2026-03-31 15:32:36 +02:00
bool token : : is_string ( ) {
2026-03-26 15:49:28 +01:00
return type = = Token_STRING ;
2016-06-09 22:25:50 +06:00
}
2026-03-31 15:32:36 +02:00
bool token : : is_enumeration ( ) {
2026-03-26 15:49:28 +01:00
// @nb this is a bit confusing?
return type = = Token_ENUMERATION | | type = = Token_BOOL ;
2011-05-08 11:04:32 +00:00
}
2015-02-03 13:53:36 +00:00
2026-03-31 15:32:36 +02:00
bool token : : is_binary ( ) {
2026-03-26 15:49:28 +01:00
return type = = Token_BINARY ;
2011-06-13 07:23:09 +00:00
}
2015-02-03 13:53:36 +00:00
2026-03-31 15:32:36 +02:00
bool token : : is_keyword ( ) {
2026-03-26 15:49:28 +01:00
return type = = Token_KEYWORD ;
2011-05-08 11:04:32 +00:00
}
2015-02-03 13:53:36 +00:00
2026-03-31 15:32:36 +02:00
bool token : : is_int ( ) {
2026-03-26 15:49:28 +01:00
return type = = Token_INT ;
2011-05-08 11:04:32 +00:00
}
2015-02-03 13:53:36 +00:00
2026-03-31 15:32:36 +02:00
bool token : : is_bool ( ) {
2026-03-26 15:49:28 +01:00
// Bool and logical share the same storage type, just logical unknown is stored as 'U'.
return type = = Token_BOOL & & value_char ! = ' U ' ;
2011-05-08 11:04:32 +00:00
}
2015-02-03 13:53:36 +00:00
2026-03-31 15:32:36 +02:00
bool token : : is_logical ( ) {
2026-03-26 15:49:28 +01:00
return type = = Token_BOOL ;
2015-04-07 15:06:56 +00:00
}
2026-03-31 15:32:36 +02:00
bool token : : is_float ( ) {
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
2026-03-26 15:49:28 +01:00
return type = = Token_FLOAT | | type = = Token_INT ;
2016-06-12 11:22:08 +02:00
# else
2026-03-26 15:49:28 +01:00
return type = = Token_FLOAT ;
2016-06-12 11:22:08 +02:00
# endif
2014-11-02 18:36:51 +00:00
}
2015-02-03 13:53:36 +00:00
2026-07-19 11:44:31 +03:00
int64_t token : : as_int ( ) {
2026-03-26 15:49:28 +01:00
if ( type ! = Token_INT ) {
2026-03-31 15:32:36 +02:00
throw invalid_token_exception ( start_pos , to_string ( ) , " integer " ) ;
2023-09-17 12:30:17 +02:00
}
2026-03-26 15:49:28 +01:00
return value_int ;
2016-06-09 22:25:50 +06:00
}
2026-03-31 15:32:36 +02:00
unsigned token : : as_identifier ( ) {
2026-03-26 15:49:28 +01:00
if ( type ! = Token_IDENTIFIER ) {
2026-03-31 15:32:36 +02:00
throw invalid_token_exception ( start_pos , to_string ( ) , " instance name " ) ;
2023-09-17 12:30:17 +02:00
}
2026-03-26 15:49:28 +01:00
return ( unsigned ) value_int ;
2011-06-13 07:23:09 +00:00
}
2015-02-03 13:53:36 +00:00
2026-03-31 15:32:36 +02:00
bool token : : as_bool ( ) {
2026-03-26 15:49:28 +01:00
if ( type ! = Token_BOOL ) {
2026-03-31 15:32:36 +02:00
throw invalid_token_exception ( start_pos , to_string ( ) , " boolean " ) ;
2023-09-17 12:30:17 +02:00
}
2026-03-26 15:49:28 +01:00
return value_char = = ' T ' ;
2021-07-29 14:54:51 +02:00
}
2026-03-31 15:32:36 +02:00
boost : : logic : : tribool token : : as_logical ( ) {
2026-03-26 15:49:28 +01:00
if ( type ! = Token_BOOL ) {
2026-03-31 15:32:36 +02:00
throw invalid_token_exception ( start_pos , to_string ( ) , " logical " ) ;
2023-09-17 12:30:17 +02:00
}
2026-03-26 15:49:28 +01:00
if ( value_int = = ' F ' ) {
2023-09-17 12:30:17 +02:00
return false ;
2023-10-24 12:21:32 +02:00
}
2026-03-26 15:49:28 +01:00
if ( value_int = = ' T ' ) {
2023-09-17 12:30:17 +02:00
return true ;
}
2023-10-24 12:21:32 +02:00
return boost : : logic : : indeterminate ;
2011-05-08 11:04:32 +00:00
}
2015-02-03 13:53:36 +00:00
2026-03-31 15:32:36 +02:00
double token : : as_float ( ) {
2016-06-12 11:22:08 +02:00
# ifdef PERMISSIVE_FLOAT
2026-03-26 15:49:28 +01:00
if ( type = = Token_INT ) {
2023-09-17 12:30:17 +02:00
/// NB: We are being more permissive here then allowed by the standard
2026-03-26 15:49:28 +01:00
return value_int ;
2023-10-24 12:21:32 +02:00
} // ----> continues beyond preprocessor directive
2016-06-12 11:22:08 +02:00
# endif
2026-03-26 15:49:28 +01:00
if ( type = = Token_FLOAT ) {
return value_double ;
2017-03-02 16:12:20 +02:00
}
2026-03-31 15:32:36 +02:00
throw invalid_token_exception ( start_pos , to_string ( ) , " real " ) ;
2011-07-11 09:33:18 +00:00
}
2015-02-03 13:53:36 +00:00
2026-03-31 15:32:36 +02:00
const std : : string & token : : as_string ( ) {
2026-03-26 15:49:28 +01:00
if ( is_string ( ) | | is_enumeration ( ) | | is_binary ( ) | | is_keyword ( ) ) {
// @todo quotes
return * value_string ;
2023-09-17 12:30:17 +02:00
}
2026-03-31 15:32:36 +02:00
throw invalid_token_exception ( start_pos , to_string ( ) , " string " ) ;
2015-04-07 15:06:56 +00:00
}
2026-03-31 15:32:36 +02:00
boost : : dynamic_bitset < > token : : as_binary ( ) {
2026-03-26 15:49:28 +01:00
const std : : string & str = as_string ( ) ;
2024-08-23 20:29:07 +02:00
if ( str . empty ( ) ) {
2026-03-31 15:32:36 +02:00
throw exception ( " token is not a valid binary sequence " ) ;
2023-09-17 12:30:17 +02:00
}
2015-04-07 15:06:56 +00:00
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 ) ) {
2026-03-31 15:32:36 +02:00
throw exception ( " token is not a valid binary sequence " ) ;
2023-09-17 12:30:17 +02:00
}
+ + it ;
2026-03-26 15:49:28 +01:00
unsigned i = ( str . size ( ) - 1 ) * 4 - n ;
2023-09-17 12:30:17 +02:00
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 ;
}
2023-10-24 14:16:26 +02:00
if ( ( value & ( 1 < < ( 3 - j ) ) ) ! = 0 ) {
2023-09-17 12:30:17 +02:00
bitset . set ( i ) ;
}
}
}
2015-04-07 15:06:56 +00:00
2023-09-17 12:30:17 +02:00
return bitset ;
2011-05-08 11:04:32 +00:00
}
2015-02-03 13:53:36 +00:00
2026-03-31 15:32:36 +02:00
std : : string token : : to_string ( ) {
2023-09-17 12:30:17 +02:00
std : : string result ;
2026-03-26 15:49:28 +01:00
if ( type = = Token_OPERATOR | | type = = Token_BOOL ) {
result . push_back ( value_char ) ;
} else if ( type = = Token_INT ) {
result = std : : to_string ( value_int ) ;
} else if ( type = = Token_FLOAT ) {
2025-10-13 20:47:43 +02:00
std : : ostringstream oss ;
2026-03-26 15:49:28 +01:00
oss < < std : : setprecision ( 15 ) < < value_double ;
2025-10-13 20:47:43 +02:00
result = oss . str ( ) ;
} else {
2026-03-26 15:49:28 +01:00
return as_string ( ) ;
2025-10-13 20:47:43 +02:00
}
2023-09-17 12:30:17 +02:00
return result ;
2011-07-11 09:33:18 +00:00
}
2026-06-11 15:51:40 +02:00
namespace {
template < typename Variant , typename T >
struct is_type_in_variant ;
template < typename T , typename First , typename . . . Rest >
struct is_type_in_variant < std : : variant < First , Rest . . . > , T >
{
static constexpr bool value = std : : is_same < T , First > : : value | | is_type_in_variant < std : : variant < Rest . . . > , T > : : value ;
} ;
template < typename T , typename Last >
struct is_type_in_variant < std : : variant < Last > , T >
{
static constexpr bool value = std : : is_same < T , Last > : : value ;
} ;
template < typename Variant , typename T >
constexpr bool is_type_in_variant_v = is_type_in_variant < Variant , T > : : value ;
class parameter_type_view {
const ifcopenshell : : declaration * declaration_ ;
const std : : vector < const ifcopenshell : : attribute * > * attributes_ ;
std : : unique_ptr < ifcopenshell : : named_type > transient_named_type_ ;
public :
parameter_type_view ( const ifcopenshell : : declaration * declaration )
: declaration_ ( declaration )
, attributes_ ( nullptr )
{
if ( declaration_ & & declaration_ - > as_entity ( ) ) {
attributes_ = & declaration_ - > as_entity ( ) - > all_attributes ( ) ;
} else if ( declaration_ & & declaration_ - > as_enumeration_type ( ) ) {
transient_named_type_ . reset ( new ifcopenshell : : named_type ( const_cast < ifcopenshell : : declaration * > ( declaration_ ) ) ) ;
}
}
size_t size ( ) const {
if ( attributes_ ) {
return attributes_ - > size ( ) ;
}
return declaration_ ? 1 : 0 ;
}
const ifcopenshell : : parameter_type * operator [ ] ( size_t index ) const {
if ( attributes_ ) {
return index < attributes_ - > size ( ) ? ( * attributes_ ) [ index ] - > type_of_attribute ( ) : nullptr ;
}
if ( index ! = 0 | | ! declaration_ ) {
return nullptr ;
}
if ( auto * type_declaration = declaration_ - > as_type_declaration ( ) ) {
return type_declaration - > declared_type ( ) ;
}
if ( declaration_ - > as_enumeration_type ( ) ) {
return transient_named_type_ . get ( ) ;
}
return nullptr ;
}
} ;
const ifcopenshell : : parameter_type * unwrap_type_declarations ( const ifcopenshell : : parameter_type * parameter_type ) {
while ( parameter_type & & parameter_type - > as_named_type ( ) & &
parameter_type - > as_named_type ( ) - > declared_type ( ) - > as_type_declaration ( ) ) {
parameter_type = parameter_type - > as_named_type ( ) - > declared_type ( ) - > as_type_declaration ( ) - > declared_type ( ) ;
}
return parameter_type ;
}
ifcopenshell : : declaration * declared_type ( const ifcopenshell : : parameter_type * parameter_type ) {
parameter_type = unwrap_type_declarations ( parameter_type ) ;
return parameter_type & & parameter_type - > as_named_type ( ) ? parameter_type - > as_named_type ( ) - > declared_type ( ) : nullptr ;
}
const ifcopenshell : : aggregation_type * aggregate_parameter_type ( const ifcopenshell : : parameter_type * parameter_type ) {
parameter_type = unwrap_type_declarations ( parameter_type ) ;
return parameter_type ? parameter_type - > as_aggregation_type ( ) : nullptr ;
}
const ifcopenshell : : aggregation_type * nested_aggregation_type ( const ifcopenshell : : aggregation_type * aggregate_type ) {
return aggregate_type ? aggregate_parameter_type ( aggregate_type - > type_of_element ( ) ) : nullptr ;
}
void warn_attribute_count (
const ifcopenshell : : declaration * declaration ,
std : : optional < size_t > instance_name ,
size_t expected_size ,
2026-07-09 13:21:39 +02:00
size_t actual_size ,
2026-07-09 13:30:48 +02:00
: : logger & logger
2026-06-11 15:51:40 +02:00
) {
if ( ! declaration | | expected_size = = actual_size ) {
return ;
}
if ( declaration - > schema ( ) = = & Header_section_schema : : get_schema ( ) ) {
2026-07-09 13:30:48 +02:00
logger . warning ( " VAL " , 15 , " Expected " + std : : to_string ( expected_size ) + " attribute values, found " + std : : to_string ( actual_size ) + " for header entity " + declaration - > name ( ) ) ;
2026-06-11 15:51:40 +02:00
} else {
2026-07-09 13:30:48 +02:00
logger . warning ( " VAL " , 16 , " Expected " + std : : to_string ( expected_size ) + " attribute values, found " + std : : to_string ( actual_size ) + ( instance_name ? std : : string ( " for instance # " + std : : to_string ( * instance_name ) ) : std : : string ( " " ) ) ) ;
2026-06-11 15:51:40 +02:00
}
}
template < typename Fn >
2026-07-09 13:30:48 +02:00
void dispatch_token_direct ( ifcopenshell : : token token , ifcopenshell : : declaration * declaration , int attribute_index , logger & logger , Fn & & fn ) {
2026-06-11 15:51:40 +02:00
if ( token . is_binary ( ) ) {
fn ( token . as_binary ( ) ) ;
} else if ( token . is_bool ( ) ) {
fn ( token . as_bool ( ) ) ;
} else if ( token . is_logical ( ) ) {
fn ( token . as_logical ( ) ) ;
} else if ( token . is_enumeration ( ) ) {
const auto & value = token . as_string ( ) ;
if ( declaration & & declaration - > as_enumeration_type ( ) ) {
try {
fn ( enumeration_reference ( declaration - > as_enumeration_type ( ) , declaration - > as_enumeration_type ( ) - > lookup_enum_offset ( value ) ) ) ;
} catch ( ifcopenshell : : exception & ) {
2026-07-09 13:30:48 +02:00
logger . error ( " VAL " , 12 , " An enumeration literal ' " + value + " ' is not valid for type ' " + declaration - > name ( ) + " ' at offset " + std : : to_string ( token . start_pos ) ) ;
2026-06-11 15:51:40 +02:00
}
} else {
2026-07-09 13:30:48 +02:00
logger . error ( " VAL " , 13 , " An enumeration literal ' " + value + " ' is not expected at attribute index ' " + std : : to_string ( attribute_index ) + " ' at offset " + std : : to_string ( token . start_pos ) ) ;
2026-06-11 15:51:40 +02:00
}
} else if ( token . is_int ( ) ) {
fn ( token . as_int ( ) ) ;
} else if ( token . is_float ( ) ) {
fn ( token . as_float ( ) ) ;
} else if ( token . is_identifier ( ) ) {
fn ( ifcopenshell : : reference_or_simple_type { ifcopenshell : : instance_reference { ( int ) token . as_identifier ( ) , token . start_pos } } ) ;
} else if ( token . is_string ( ) ) {
fn ( token . as_string ( ) ) ;
} else if ( token . is_operator ( ' * ' ) ) {
fn ( derived { } ) ;
}
}
typedef std : : variant <
blank ,
2026-07-19 12:58:23 +03:00
std : : vector < int64_t > ,
2026-06-11 15:51:40 +02:00
std : : vector < double > ,
std : : vector < std : : string > ,
std : : vector < boost : : dynamic_bitset < > > ,
std : : vector < ifcopenshell : : reference_or_simple_type > ,
2026-07-19 12:58:23 +03:00
std : : vector < std : : vector < int64_t > > ,
2026-06-11 15:51:40 +02:00
std : : vector < std : : vector < double > > ,
std : : vector < std : : vector < ifcopenshell : : reference_or_simple_type > >
> direct_aggregate_storage ;
struct direct_aggregate {
direct_aggregate_storage storage ;
size_t pending_empty_aggregates = 0 ;
size_t values = 0 ;
2026-07-09 13:30:48 +02:00
: : logger & logger_ ;
explicit direct_aggregate ( : : logger & logger )
: logger_ ( logger ) { }
2026-06-11 15:51:40 +02:00
template < typename T >
void append ( const T & value ) {
+ + values ;
if constexpr ( is_type_in_variant_v < direct_aggregate_storage , std : : vector < std : : decay_t < T > > > ) {
if constexpr (
2026-07-19 12:58:23 +03:00
std : : is_same_v < std : : decay_t < T > , std : : vector < int64_t > > | |
2026-06-11 15:51:40 +02:00
std : : is_same_v < std : : decay_t < T > , std : : vector < double > > | |
std : : is_same_v < std : : decay_t < T > , std : : vector < ifcopenshell : : reference_or_simple_type > >
) {
if ( storage . index ( ) = = 0 & & pending_empty_aggregates ) {
append_promoted ( value ) ;
return ;
}
}
if ( pending_empty_aggregates ) {
2026-07-09 13:30:48 +02:00
logger_ . error ( " VAL " , 14 , " Inconsistent aggregate valuation while attempting to append " + std : : string ( typeid ( T ) . name ( ) ) + " after an empty nested aggregate " ) ;
2026-06-11 15:51:40 +02:00
pending_empty_aggregates = 0 ;
}
if ( storage . index ( ) = = 0 ) {
storage = std : : vector < std : : decay_t < T > > { value } ;
} else if ( auto * vector = std : : get_if < std : : vector < std : : decay_t < T > > > ( & storage ) ) {
vector - > push_back ( value ) ;
} else {
append_promoted ( value ) ;
}
} else {
2026-07-09 13:30:48 +02:00
logger_ . error ( " UNS " , 31 , std : : string ( " Aggregates of " ) + typeid ( T ) . name ( ) + " are not supported in the IfcOpenShell parser " ) ;
2026-06-11 15:51:40 +02:00
}
}
void append_empty_nested ( ) {
+ + values ;
2026-07-19 12:58:23 +03:00
if ( auto * int_vector = std : : get_if < std : : vector < std : : vector < int64_t > > > ( & storage ) ) {
2026-06-11 15:51:40 +02:00
int_vector - > emplace_back ( ) ;
} else if ( auto * double_vector = std : : get_if < std : : vector < std : : vector < double > > > ( & storage ) ) {
double_vector - > emplace_back ( ) ;
} else if ( auto * reference_vector = std : : get_if < std : : vector < std : : vector < ifcopenshell : : reference_or_simple_type > > > ( & storage ) ) {
reference_vector - > emplace_back ( ) ;
} else if ( storage . index ( ) = = 0 ) {
+ + pending_empty_aggregates ;
} else {
2026-07-09 13:30:48 +02:00
logger_ . error ( " Inconsistent aggregate valuation while attempting to append an empty nested aggregate " ) ;
2026-06-11 15:51:40 +02:00
}
}
private :
template < typename T >
void append_promoted ( const T & value ) {
2026-07-19 12:58:23 +03:00
if constexpr ( std : : is_same_v < std : : decay_t < T > , int64_t > ) {
2026-06-11 15:51:40 +02:00
if ( auto * vector = std : : get_if < std : : vector < double > > ( & storage ) ) {
vector - > push_back ( ( double ) value ) ;
return ;
}
}
if constexpr ( std : : is_same_v < std : : decay_t < T > , double > ) {
2026-07-19 12:58:23 +03:00
if ( auto * vector = std : : get_if < std : : vector < int64_t > > ( & storage ) ) {
2026-06-11 15:51:40 +02:00
std : : vector < double > promoted ( vector - > begin ( ) , vector - > end ( ) ) ;
promoted . push_back ( value ) ;
storage = std : : move ( promoted ) ;
return ;
}
}
2026-07-19 12:58:23 +03:00
if constexpr ( std : : is_same_v < std : : decay_t < T > , std : : vector < int64_t > > ) {
2026-06-11 15:51:40 +02:00
if ( storage . index ( ) = = 0 ) {
2026-07-19 12:58:23 +03:00
std : : vector < std : : vector < int64_t > > promoted ( pending_empty_aggregates ) ;
2026-06-11 15:51:40 +02:00
pending_empty_aggregates = 0 ;
promoted . push_back ( value ) ;
storage = std : : move ( promoted ) ;
return ;
}
2026-07-19 12:58:23 +03:00
if ( auto * vector = std : : get_if < std : : vector < std : : vector < int64_t > > > ( & storage ) ) {
2026-06-11 15:51:40 +02:00
vector - > push_back ( value ) ;
return ;
}
if ( auto * vector = std : : get_if < std : : vector < std : : vector < double > > > ( & storage ) ) {
std : : vector < double > promoted ( value . begin ( ) , value . end ( ) ) ;
vector - > push_back ( std : : move ( promoted ) ) ;
return ;
}
}
if constexpr ( std : : is_same_v < std : : decay_t < T > , std : : vector < double > > ) {
if ( storage . index ( ) = = 0 ) {
std : : vector < std : : vector < double > > promoted ( pending_empty_aggregates ) ;
pending_empty_aggregates = 0 ;
promoted . push_back ( value ) ;
storage = std : : move ( promoted ) ;
return ;
}
if ( auto * vector = std : : get_if < std : : vector < std : : vector < double > > > ( & storage ) ) {
vector - > push_back ( value ) ;
return ;
}
2026-07-19 12:58:23 +03:00
if ( auto * vector = std : : get_if < std : : vector < std : : vector < int64_t > > > ( & storage ) ) {
2026-06-11 15:51:40 +02:00
std : : vector < std : : vector < double > > promoted ;
promoted . reserve ( vector - > size ( ) + 1 ) ;
for ( const auto & nested : * vector ) {
promoted . emplace_back ( nested . begin ( ) , nested . end ( ) ) ;
}
promoted . push_back ( value ) ;
storage = std : : move ( promoted ) ;
return ;
}
}
if constexpr ( std : : is_same_v < std : : decay_t < T > , std : : vector < ifcopenshell : : reference_or_simple_type > > ) {
if ( storage . index ( ) = = 0 ) {
std : : vector < std : : vector < ifcopenshell : : reference_or_simple_type > > promoted ( pending_empty_aggregates ) ;
pending_empty_aggregates = 0 ;
promoted . push_back ( value ) ;
storage = std : : move ( promoted ) ;
return ;
}
if ( auto * vector = std : : get_if < std : : vector < std : : vector < ifcopenshell : : reference_or_simple_type > > > ( & storage ) ) {
vector - > push_back ( value ) ;
return ;
}
}
auto current = std : : visit ( [ ] ( auto v ) {
if constexpr ( ! std : : is_same_v < decltype ( v ) , blank > ) {
return std : : string ( typeid ( typename decltype ( v ) : : value_type ) . name ( ) ) ;
} else {
return std : : string { } ;
}
} , storage ) ;
2026-07-09 13:30:48 +02:00
logger_ . error ( " Inconsistent aggregate valuation while attempting to append " + std : : string ( typeid ( T ) . name ( ) ) + " to an aggregate of " + current ) ;
2026-06-11 15:51:40 +02:00
}
} ;
void append_empty_direct_aggregate ( const ifcopenshell : : aggregation_type * aggregate_type , direct_aggregate & target ) {
if ( ! aggregate_type ) {
target . append_empty_nested ( ) ;
return ;
}
auto argument_type = ifcopenshell : : make_aggregate ( ifcopenshell : : from_parameter_type ( aggregate_type - > type_of_element ( ) ) ) ;
if ( argument_type = = ifcopenshell : : Argument_AGGREGATE_OF_INT ) {
2026-07-19 12:58:23 +03:00
target . storage = std : : vector < int64_t > { } ;
2026-06-11 15:51:40 +02:00
} else if ( argument_type = = ifcopenshell : : Argument_AGGREGATE_OF_DOUBLE ) {
target . storage = std : : vector < double > { } ;
} else if ( argument_type = = ifcopenshell : : Argument_AGGREGATE_OF_STRING ) {
target . storage = std : : vector < std : : string > { } ;
} else if ( argument_type = = ifcopenshell : : Argument_AGGREGATE_OF_BINARY ) {
target . storage = std : : vector < boost : : dynamic_bitset < > > { } ;
} else if ( argument_type = = ifcopenshell : : Argument_AGGREGATE_OF_ENTITY_INSTANCE ) {
target . storage = std : : vector < ifcopenshell : : reference_or_simple_type > { } ;
} else if ( argument_type = = ifcopenshell : : Argument_AGGREGATE_OF_AGGREGATE_OF_INT ) {
2026-07-19 12:58:23 +03:00
target . storage = std : : vector < std : : vector < int64_t > > { } ;
2026-06-11 15:51:40 +02:00
} else if ( argument_type = = ifcopenshell : : Argument_AGGREGATE_OF_AGGREGATE_OF_DOUBLE ) {
target . storage = std : : vector < std : : vector < double > > { } ;
} else if ( argument_type = = ifcopenshell : : Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE ) {
target . storage = std : : vector < std : : vector < ifcopenshell : : reference_or_simple_type > > { } ;
} else {
target . append_empty_nested ( ) ;
}
}
template < typename T >
void set_direct_attribute (
in_memory_attribute_storage & storage ,
std : : optional < size_t > instance_name ,
ifcopenshell : : unresolved_references * references_to_resolve ,
size_t attribute_index ,
int resolve_reference_index ,
const T & value
) {
if constexpr ( std : : is_same_v < std : : decay_t < T > , ifcopenshell : : reference_or_simple_type > ) {
if ( instance_name & & references_to_resolve ) {
references_to_resolve - > push_back ( std : : make_pair (
mutable_attribute_value { ( uint32_t ) * instance_name , resolve_reference_index = = - 1 ? ( uint8_t ) attribute_index : ( uint8_t ) resolve_reference_index } ,
value
) ) ;
}
} else if constexpr ( std : : is_same_v < std : : decay_t < T > , std : : vector < ifcopenshell : : reference_or_simple_type > > ) {
if ( instance_name & & references_to_resolve ) {
references_to_resolve - > push_back ( { { ( uint32_t ) * instance_name , resolve_reference_index = = - 1 ? ( uint8_t ) attribute_index : ( uint8_t ) resolve_reference_index } , value } ) ;
}
} else if constexpr ( std : : is_same_v < std : : decay_t < T > , std : : vector < std : : vector < ifcopenshell : : reference_or_simple_type > > > ) {
if ( instance_name & & references_to_resolve ) {
references_to_resolve - > push_back ( { { ( uint32_t ) * instance_name , resolve_reference_index = = - 1 ? ( uint8_t ) attribute_index : ( uint8_t ) resolve_reference_index } , value } ) ;
}
} else {
storage . set ( attribute_index , value ) ;
}
}
2026-03-27 20:45:13 +01:00
template < typename Reader >
2026-06-11 15:51:40 +02:00
void skip_aggregate ( ifcopenshell : : spf_lexer < Reader > * tokens ) {
size_t depth = 1 ;
while ( depth ) {
token next = tokens - > next ( ) ;
if ( ! next ) {
break ;
}
if ( next . is_operator ( ' ( ' ) ) {
+ + depth ;
} else if ( next . is_operator ( ' ) ' ) ) {
- - depth ;
}
}
}
2023-09-17 12:30:17 +02:00
2026-06-11 15:51:40 +02:00
template < typename Reader >
direct_aggregate read_direct_aggregate (
ifcopenshell : : impl : : in_memory_file_storage & storage ,
ifcopenshell : : spf_lexer < Reader > * tokens ,
std : : optional < size_t > entity_instance_name ,
const ifcopenshell : : entity * entity ,
int attribute_index ,
2026-07-09 13:21:39 +02:00
const ifcopenshell : : aggregation_type * aggregate_type ,
2026-07-09 13:30:48 +02:00
: : logger & logger
2026-06-11 15:51:40 +02:00
) {
2026-07-09 13:30:48 +02:00
direct_aggregate aggregate ( logger ) ;
2026-06-11 15:51:40 +02:00
token next = tokens - > next ( ) ;
2023-09-17 12:30:17 +02:00
2026-03-26 15:49:28 +01:00
while ( next ) {
if ( next . is_operator ( ' , ' ) ) {
} else if ( next . is_operator ( ' ) ' ) ) {
2023-09-17 12:30:17 +02:00
break ;
2026-03-26 15:49:28 +01:00
} else if ( next . is_operator ( ' ( ' ) ) {
2026-07-09 13:30:48 +02:00
auto nested = read_direct_aggregate ( storage , tokens , entity_instance_name , entity , attribute_index , nested_aggregation_type ( aggregate_type ) , logger ) ;
2026-06-11 15:51:40 +02:00
if ( nested . values = = 0 & & nested . storage . index ( ) = = 0 ) {
aggregate . append_empty_nested ( ) ;
} else {
std : : visit ( [ & aggregate ] ( const auto & value ) {
if constexpr ( ! std : : is_same_v < std : : decay_t < decltype ( value ) > , blank > ) {
aggregate . append ( value ) ;
}
} , nested . storage ) ;
}
} else if ( next . is_keyword ( ) ) {
try {
const auto * declaration = ( storage . schema ? storage . schema : storage . file - > schema ( ) ) - > declaration_by_name ( next . as_string ( ) ) ;
tokens - > next ( ) ;
auto data = storage . load ( tokens , entity_instance_name , declaration , entity , attribute_index ) ;
storage . read_simple_type_instances . push_back ( data ) ;
aggregate . append ( ifcopenshell : : reference_or_simple_type { express : : Base ( data ) } ) ;
} catch ( exception & e ) {
2026-07-09 13:21:39 +02:00
logger . error ( " SYN " , 123 , std : : string ( e . what ( ) ) + " at offset " + std : : to_string ( next . start_pos ) ) ;
2026-06-11 15:51:40 +02:00
}
2023-09-17 12:30:17 +02:00
} else {
2026-03-26 15:49:28 +01:00
if ( next . is_identifier ( ) & & entity & & entity_instance_name ) {
2026-06-11 15:51:40 +02:00
storage . register_inverse ( ( unsigned ) * entity_instance_name , entity , next . value_int , attribute_index ) ;
2023-09-17 12:30:17 +02:00
}
2026-07-09 13:30:48 +02:00
dispatch_token_direct ( next , aggregate_type & & aggregate_type - > type_of_element ( ) - > as_named_type ( ) ? aggregate_type - > type_of_element ( ) - > as_named_type ( ) - > declared_type ( ) : nullptr , attribute_index , logger , [ & aggregate ] ( const auto & value ) {
2026-07-19 12:58:23 +03:00
aggregate . append ( value ) ;
2026-06-11 15:51:40 +02:00
} ) ;
}
next = tokens - > next ( ) ;
}
if ( aggregate . values = = 0 ) {
append_empty_direct_aggregate ( aggregate_type , aggregate ) ;
}
return aggregate ;
}
} // namespace
//
// Reads the arguments from a list of tokens directly into instance_data storage.
// Additionally, registers the ids (i.e. #[\d]+) in the inverse map.
//
template < typename Reader >
shared_pointer_type ifcopenshell : : impl : : in_memory_file_storage : : load (
ifcopenshell : : spf_lexer < Reader > * tokens ,
std : : optional < size_t > entity_instance_name ,
const ifcopenshell : : declaration * declaration ,
const ifcopenshell : : entity * entity ,
int attribute_index ,
bool coerce_attribute_count
) {
static_cast < void > ( coerce_attribute_count ) ;
parameter_type_view parameter_types ( declaration ) ;
const size_t expected_size = parameter_types . size ( ) ;
in_memory_attribute_storage storage ( expected_size ) ;
token next = tokens - > next ( ) ;
size_t attribute_index_within_data = 0 ;
size_t values_read = 0 ;
2023-09-17 12:30:17 +02:00
2026-06-11 15:51:40 +02:00
while ( next ) {
if ( next . is_operator ( ' , ' ) ) {
+ + attribute_index_within_data ;
} else if ( next . is_operator ( ' ) ' ) ) {
break ;
} else {
+ + values_read ;
const bool retain_value = attribute_index_within_data < expected_size ;
const ifcopenshell : : parameter_type * parameter_type = retain_value ? parameter_types [ attribute_index_within_data ] : nullptr ;
const int reference_attribute_index = attribute_index = = - 1 ? ( int ) attribute_index_within_data : attribute_index ;
if ( next . is_operator ( ' ( ' ) ) {
if ( retain_value ) {
2026-07-09 13:30:48 +02:00
auto aggregate = read_direct_aggregate ( * this , tokens , entity_instance_name , entity , reference_attribute_index , aggregate_parameter_type ( parameter_type ) , logger_ . get ( ) ) ;
2026-06-11 15:51:40 +02:00
std : : visit ( [ & ] ( const auto & value ) {
if constexpr ( ! std : : is_same_v < std : : decay_t < decltype ( value ) > , blank > ) {
set_direct_attribute ( storage , entity_instance_name , references_to_resolve , attribute_index_within_data , attribute_index , value ) ;
}
} , aggregate . storage ) ;
} else {
skip_aggregate ( tokens ) ;
}
} else if ( next . is_keyword ( ) ) {
2023-09-17 12:30:17 +02:00
try {
2026-06-11 15:51:40 +02:00
const auto * simple_declaration = ( schema ? schema : file - > schema ( ) ) - > declaration_by_name ( next . as_string ( ) ) ;
2026-03-31 15:32:36 +02:00
tokens - > next ( ) ;
2026-06-11 15:51:40 +02:00
if ( retain_value ) {
auto data = load ( tokens , entity_instance_name , simple_declaration , entity , reference_attribute_index ) ;
read_simple_type_instances . push_back ( data ) ;
storage . set ( attribute_index_within_data , express : : Base ( data ) ) ;
} else {
skip_aggregate ( tokens ) ;
}
2026-03-31 15:32:36 +02:00
} catch ( exception & e ) {
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . message ( : : logger : : LOG_ERROR , std : : string ( e . what ( ) ) + " at offset " + std : : to_string ( next . start_pos ) ) ;
2026-06-11 15:51:40 +02:00
- - values_read ;
2023-09-17 12:30:17 +02:00
}
} else {
2026-06-11 15:51:40 +02:00
if ( next . is_identifier ( ) & & entity & & entity_instance_name ) {
register_inverse ( ( unsigned ) * entity_instance_name , entity , next . value_int , reference_attribute_index ) ;
}
if ( retain_value ) {
2026-07-09 13:30:48 +02:00
dispatch_token_direct ( next , declared_type ( parameter_type ) , ( int ) attribute_index_within_data , logger_ . get ( ) , [ & ] ( const auto & value ) {
2026-06-11 15:51:40 +02:00
set_direct_attribute ( storage , entity_instance_name , references_to_resolve , attribute_index_within_data , attribute_index , value ) ;
} ) ;
}
2023-09-17 12:30:17 +02:00
}
}
2026-03-31 15:32:36 +02:00
next = tokens - > next ( ) ;
2023-09-17 12:30:17 +02:00
}
2026-06-11 15:51:40 +02:00
2026-07-09 13:30:48 +02:00
warn_attribute_count ( declaration , entity_instance_name , expected_size , values_read , logger_ . get ( ) ) ;
2026-06-11 15:51:40 +02:00
return ifcopenshell : : make_pointer_type < instance_data > ( file , declaration , ( declaration & & declaration - > as_entity ( ) ) ? ( uint32_t ) entity_instance_name . value_or ( 0 ) : 0 , std : : move ( storage ) ) ;
2022-10-20 20:30:48 +02:00
}
2011-07-11 09:33:18 +00:00
2026-03-27 20:45:13 +01:00
template < typename Reader >
2026-03-31 15:32:36 +02:00
void ifcopenshell : : impl : : in_memory_file_storage : : try_read_semicolon ( ifcopenshell : : spf_lexer < Reader > * tokens ) const {
2025-10-13 20:47:43 +02:00
auto old_offset = tokens - > stream - > tell ( ) ;
2026-03-31 15:32:36 +02:00
token semilocon = tokens - > next ( ) ;
2026-03-26 15:49:28 +01:00
if ( ! semilocon . is_operator ( ' ; ' ) ) {
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
}
2011-07-11 09:33:18 +00:00
2026-03-31 15:32:36 +02:00
void ifcopenshell : : impl : : in_memory_file_storage : : register_inverse ( unsigned id_from , const ifcopenshell : : 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
2026-06-11 15:51:40 +02:00
byref_excl_ . add ( ( uint32_t ) inst_id , ( uint32_t ) id_from , ( uint16_t ) from_entity - > index_in_schema ( ) , attribute_index ) ;
2011-05-08 11:04:32 +00:00
}
2011-07-11 09:33:18 +00:00
2026-03-31 15:32:36 +02:00
void ifcopenshell : : impl : : in_memory_file_storage : : unregister_inverse ( unsigned id_from , const ifcopenshell : : entity * from_entity , const express : : Base & inst , int attribute_index ) {
2026-06-11 15:51:40 +02:00
if ( ! byref_excl_ . remove ( ( uint32_t ) inst . id ( ) , ( uint32_t ) id_from , ( uint16_t ) from_entity - > index_in_schema ( ) , attribute_index ) ) {
2023-09-17 12:30:17 +02:00
// @todo inverses also need to be populated when multiple instances are added to a new file.
2026-03-31 15:32:36 +02:00
// throw ifcopenshell::exception("Instance not found among inverses");
2023-09-17 12:30:17 +02:00
}
2017-07-03 14:18:56 +02:00
}
2025-02-21 16:12:16 +01:00
namespace {
template < typename T >
2025-10-13 20:47:43 +02:00
std : : string to_string_fixed_width ( const T & t , size_t ) {
2025-03-09 21:20:16 +01:00
// @todo currently inactive
2025-02-21 16:12:16 +01:00
std : : ostringstream oss ;
2025-03-09 21:20:16 +01:00
oss < < /*std::setfill('0') << std::setw(w) <<*/ t ;
2025-02-21 16:12:16 +01:00
return oss . str ( ) ;
}
}
2026-03-31 15:32:36 +02:00
void ifcopenshell : : impl : : rocks_db_file_storage : : register_inverse ( unsigned id_from , const ifcopenshell : : entity * from_entity , int inst_id , int attribute_index ) {
2025-09-03 11:11:55 +02:00
# ifdef IFOPSH_WITH_ROCKSDB
2025-02-21 16:12:16 +01:00
static std : : string s ;
2025-03-14 16:16:25 +01:00
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
2025-03-14 16:16:25 +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
/*
2025-03-14 16:16:25 +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, ¤t);
auto new_val = current + s;
2025-03-14 16:16:25 +01:00
db->Put(wopts, key, new_val);
}*/
2025-08-26 13:56:03 +02:00
# endif
2025-02-21 16:12:16 +01:00
}
2026-03-31 15:32:36 +02:00
void ifcopenshell : : impl : : rocks_db_file_storage : : unregister_inverse ( unsigned id_from , const ifcopenshell : : entity * from_entity , const express : : Base & inst , int attribute_index ) {
2025-09-03 11:11:55 +02:00
# ifdef IFOPSH_WITH_ROCKSDB
2025-02-21 16:12:16 +01:00
static std : : string s ;
2026-01-04 10:40:02 +01:00
auto inst_id = inst . id ( ) ;
2025-02-21 16:12:16 +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 ) ;
if ( db - > Get ( rocksdb : : ReadOptions { } , key , & s ) . ok ( ) ) {
2025-03-14 16:16:25 +01:00
std : : vector < uint32_t > vals ( s . size ( ) / sizeof ( uint32_t ) ) ;
2025-02-21 16:12:16 +01:00
memcpy ( vals . data ( ) , s . data ( ) , s . size ( ) ) ;
2025-08-25 12:45:10 +02:00
auto it = std : : find ( vals . begin ( ) , vals . end ( ) , ( uint32_t ) id_from ) ;
if ( it ! = vals . end ( ) ) {
vals . erase ( it ) ;
} else {
2026-07-09 13:30:48 +02:00
file - > logger ( ) . error ( " Unregistering non-existant inverse # " + std : : to_string ( id_from ) + " on instance # " + std : : to_string ( inst_id ) + " at attribute " + std : : to_string ( attribute_index ) ) ;
2025-08-25 12:45:10 +02:00
}
2025-03-14 16:16:25 +01:00
s . resize ( vals . size ( ) * sizeof ( uint32_t ) ) ;
2025-02-21 16:12:16 +01:00
memcpy ( s . data ( ) , vals . data ( ) , s . size ( ) ) ;
2025-03-14 16:16:25 +01:00
db - > Put ( wopts , key , s ) ;
2025-02-21 16:12:16 +01:00
}
2025-08-26 13:56:03 +02:00
# endif
2025-02-21 16:12:16 +01:00
}
2026-03-31 15:32:36 +02:00
void ifcopenshell : : impl : : rocks_db_file_storage : : add_type_ref ( const express : : Base & new_entity )
2025-02-24 20:46:29 +01:00
{
2025-09-03 11:11:55 +02:00
# ifdef IFOPSH_WITH_ROCKSDB
2025-03-09 21:20:16 +01:00
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
2026-01-04 10:40:02 +01:00
if ( new_entity . declaration ( ) . as_entity ( ) ) {
v = new_entity . id ( ) ;
2025-03-09 21:20:16 +01:00
memcpy ( s . data ( ) , & v , sizeof ( size_t ) ) ;
// no merges yet, because the python client doesn't support them
2026-01-04 10:40:02 +01:00
db - > Merge ( wopts , " t| " + std : : to_string ( new_entity . declaration ( ) . index_in_schema ( ) ) , s ) ;
2025-03-14 16:16:25 +01:00
/*{
2025-03-09 21:20:16 +01:00
std::string current;
// @todo this uses the same key-namespace as typedecl instances, not a direct conflict, but also not very clear
2026-01-04 10:40:02 +01:00
auto key = "t|" + std::to_string(new_entity.declaration().index_in_schema());
2025-03-09 21:20:16 +01:00
db->Get(rocksdb::ReadOptions{}, key, ¤t);
auto new_val = current + s;
2025-03-14 16:16:25 +01:00
db->Put(wopts, key, new_val);
}*/
2025-02-27 22:07:31 +01:00
}
// not only mapping also register type
2026-01-04 10:40:02 +01:00
v = new_entity . declaration ( ) . index_in_schema ( ) ;
2025-02-27 22:07:31 +01:00
memcpy ( s . data ( ) , & v , sizeof ( size_t ) ) ;
2026-01-04 10:40:02 +01:00
db - > Put ( wopts , ( new_entity . declaration ( ) . as_entity ( ) ? " i| " : " t| " ) + std : : to_string ( new_entity . id ( ) ? new_entity . id ( ) : new_entity . identity ( ) ) + " |_ " , s ) ;
2025-08-26 13:56:03 +02:00
# endif
2025-02-24 20:46:29 +01:00
}
2026-03-31 15:32:36 +02:00
void ifcopenshell : : impl : : rocks_db_file_storage : : remove_type_ref ( const express : : Base & new_entity )
2025-02-24 20:46:29 +01:00
{
2025-09-03 11:11:55 +02:00
# ifdef IFOPSH_WITH_ROCKSDB
2026-01-04 10:40:02 +01:00
if ( new_entity . declaration ( ) . as_entity ( ) ) {
2025-03-09 21:20:16 +01:00
std : : string s ;
2026-01-04 10:40:02 +01:00
auto key = " t| " + std : : to_string ( new_entity . declaration ( ) . index_in_schema ( ) ) ;
2025-03-09 21:20:16 +01:00
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 ( ) ) ;
2026-01-04 10:40:02 +01:00
vals . erase ( std : : find ( vals . begin ( ) , vals . end ( ) , ( size_t ) new_entity . id ( ) ) ) ;
2025-03-09 21:20:16 +01:00
s . resize ( vals . size ( ) * sizeof ( size_t ) ) ;
memcpy ( s . data ( ) , vals . data ( ) , s . size ( ) ) ;
2025-03-14 16:16:25 +01:00
db - > Put ( wopts , key , s ) ;
2025-03-09 21:20:16 +01:00
}
2025-02-24 20:46:29 +01:00
}
2025-02-27 22:07:31 +01:00
2026-01-04 10:40:02 +01:00
db - > Delete ( wopts , ( new_entity . declaration ( ) . as_entity ( ) ? " i| " : " t| " ) + std : : to_string ( new_entity . id ( ) ? new_entity . id ( ) : new_entity . identity ( ) ) + " |_ " ) ;
2025-08-26 13:56:03 +02:00
# 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_ < < " ) " ;
}
2026-07-26 16:32:47 +10:00
// Shortest decimal representation of 'd' that round-trips back to the
// exact same double (like std::to_chars, or Python's repr).
// Using actual `std::to_chars` requires macOS 13.3+, so we implement this manually,
// until we drop support for older targets.
//
// Mirrors libstdc++'s notation-choice bounds (floating_to_chars.cc,
// __floating_to_chars_shortest) to pick whichever of fixed/scientific is
// shorter for a given digit count and exponent.
static inline void format_double_shortest ( char ( & buf ) [ 64 ] , double d ) {
char sci [ 64 ] ;
int mantissa_length = 17 ;
for ( int prec = 1 ; prec < = 17 ; + + prec ) {
snprintf ( sci , sizeof ( sci ) , " %.*e " , prec - 1 , d ) ;
if ( strtod ( sci , nullptr ) = = d ) {
mantissa_length = prec ;
break ;
}
}
const char * exp_str = strchr ( sci , ' e ' ) ;
const int scientific_exponent = exp_str ? atoi ( exp_str + 1 ) : 0 ;
const int fd_exponent = scientific_exponent - ( mantissa_length - 1 ) ;
int lower_bound = - ( mantissa_length + 3 ) ;
int upper_bound = 5 ;
if ( mantissa_length = = 1 ) {
+ + lower_bound ;
- - upper_bound ;
}
if ( fd_exponent > = lower_bound & & fd_exponent < = upper_bound ) {
const int fixed_precision = fd_exponent < 0 ? - fd_exponent : 0 ;
snprintf ( buf , 64 , " %.*f " , fixed_precision , d ) ;
} else {
snprintf ( buf , 64 , " %.*e " , mantissa_length - 1 , d ) ;
}
}
2024-08-23 20:29:07 +02:00
// 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 ) {
2026-07-26 16:32:47 +10:00
// 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.
char buf [ 64 ] ;
format_double_shortest ( buf , d ) ;
const std : : string str ( buf ) ;
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 ' ) ;
}
2026-07-26 16:32:47 +10:00
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 ) {
2026-07-26 16:32:47 +10:00
result + = ' E ' ;
result + = str . substr ( e + 1 ) ;
2024-08-23 20:29:07 +02:00
}
2026-07-26 16:32:47 +10: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 ) { }
2026-03-31 15:32:36 +02:00
void operator ( ) ( const blank & /*i*/ ) { data_ < < " $ " ; }
void operator ( ) ( const derived & /*i*/ ) { data_ < < " * " ; }
2026-07-19 11:44:31 +03:00
void operator ( ) ( const int64_t & i ) { data_ < < i ; }
2024-08-23 20:29:07 +02:00
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_ ) {
2026-03-31 15:32:36 +02:00
data_ < < static_cast < std : : string > ( character_encoder ( s ) ) ;
2024-08-23 20:29:07 +02:00
} else {
data_ < < ' \' ' < < s < < ' \' ' ;
}
}
2026-07-19 12:58:23 +03:00
void operator ( ) ( const std : : vector < int64_t > & i ) ;
2024-08-23 20:29:07 +02:00
void operator ( ) ( const std : : vector < double > & i ) ;
void operator ( ) ( const std : : vector < std : : string > & i ) ;
void operator ( ) ( const std : : vector < boost : : dynamic_bitset < > > & i ) ;
2026-03-31 15:32:36 +02:00
void operator ( ) ( const enumeration_reference & i ) {
2024-08-23 20:29:07 +02:00
data_ < < " . " < < i . value ( ) < < " . " ;
}
2026-01-04 10:40:02 +01:00
void operator ( ) ( const express : : Base & i ) {
if ( i . declaration ( ) . as_entity ( ) = = nullptr | | i . declaration ( ) . schema ( ) = = & Header_section_schema : : get_schema ( ) ) {
2026-03-31 15:32:36 +02:00
i . to_string ( data_ , upper_ ) ;
2024-08-23 20:29:07 +02:00
} else {
2026-01-04 10:40:02 +01:00
data_ < < " # " < < i . id ( ) ;
2024-08-23 20:29:07 +02:00
}
}
2026-01-04 10:40:02 +01:00
void operator ( ) ( const std : : vector < express : : Base > & i ) {
2024-08-23 20:29:07 +02:00
data_ < < " ( " ;
2026-01-04 10:40:02 +01:00
for ( auto it = i . begin ( ) ; it ! = i . end ( ) ; + + it ) {
if ( it ! = i . begin ( ) ) {
2024-08-23 20:29:07 +02:00
data_ < < " , " ;
}
( * this ) ( * it ) ;
}
data_ < < " ) " ;
}
2026-07-19 12:58:23 +03:00
void operator ( ) ( const std : : vector < std : : vector < int64_t > > & i ) ;
2024-08-23 20:29:07 +02:00
void operator ( ) ( const std : : vector < std : : vector < double > > & i ) ;
2026-01-04 10:40:02 +01:00
void operator ( ) ( const std : : vector < std : : vector < express : : Base > > & i ) {
2024-08-23 20:29:07 +02:00
data_ < < " ( " ;
2026-01-04 10:40:02 +01:00
for ( auto outer_it = i . begin ( ) ; outer_it ! = i . end ( ) ; + + outer_it ) {
if ( outer_it ! = i . begin ( ) ) {
2024-08-23 20:29:07 +02:00
data_ < < " , " ;
}
data_ < < " ( " ;
2026-01-04 10:40:02 +01:00
for ( auto inner_it = outer_it - > begin ( ) ; inner_it ! = outer_it - > end ( ) ; + + inner_it ) {
2024-08-23 20:29:07 +02:00
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_ < < " , " ;
}
2026-03-31 15:32:36 +02:00
std : : string encoder = character_encoder ( * it ) ;
2024-08-23 20:29:07 +02:00
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
2026-07-19 12:58:23 +03:00
void StringBuilderVisitor : : operator ( ) ( const std : : vector < int64_t > & i ) { serialize ( i ) ; }
2024-08-23 20:29:07 +02:00
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 ) ; }
2026-07-19 12:58:23 +03:00
void StringBuilderVisitor : : operator ( ) ( const std : : vector < std : : vector < int64_t > > & i ) {
2024-08-23 20:29:07 +02:00
data_ < < " ( " ;
2026-07-19 12:58:23 +03:00
for ( std : : vector < std : : vector < int64_t > > : : const_iterator it = i . begin ( ) ; it ! = i . end ( ) ; + + it ) {
2024-08-23 20:29:07 +02:00
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
//
2026-03-31 15:32:36 +02:00
void instance_data : : to_string ( 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.
2026-01-04 10:40:02 +01:00
auto size = ( declaration_ & & declaration_ - > as_entity ( ) ? declaration_ - > as_entity ( ) - > attribute_count ( ) : 1 ) ;
2025-08-31 15:52:46 +02:00
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 < < " , " ;
}
2026-03-31 15:32:36 +02:00
if ( has_attribute_value < blank > ( i ) ) {
2026-01-04 10:40:02 +01:00
if ( declaration_ ! = nullptr & & declaration_ - > as_entity ( ) & & declaration_ - > 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 {
2026-01-08 10:35:46 +01:00
get_attribute_value ( i ) . apply_visitor ( vis ) ;
2023-09-17 12:30:17 +02:00
}
}
ss < < " ) " ;
2011-05-08 11:04:32 +00:00
}
2011-07-11 09:33:18 +00:00
2026-01-04 10:40:02 +01:00
/*
2026-03-31 15:32:36 +02:00
unsigned ifcopenshell::IfcBaseEntity::set_id(const std::optional<unsigned>& i) {
2023-09-17 12:30:17 +02:00
if (i) {
return id_ = *i;
}
2026-03-31 15:32:36 +02:00
return id_ = file_->fresh_id();
2017-06-05 17:26:58 +02:00
}
2026-01-04 10:40:02 +01:00
*/
2017-06-05 17:26:58 +02:00
2021-11-19 09:38:28 +01: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)
2026-03-31 15:32:36 +02:00
ifcopenshell : : argument_type get_argument_type ( const ifcopenshell : : declaration * decl , size_t i ) {
const ifcopenshell : : parameter_type * pt = 0 ;
2023-10-24 14:16:26 +02:00
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 ] ) {
2026-03-31 15:32:36 +02:00
return ifcopenshell : : Argument_DERIVED ;
2023-09-17 12:30:17 +02:00
}
2023-10-24 14:16:26 +02:00
} else if ( ( decl - > as_type_declaration ( ) ! = nullptr ) & & i = = 0 ) {
2023-09-17 12:30:17 +02:00
pt = decl - > as_type_declaration ( ) - > declared_type ( ) ;
2023-10-24 14:16:26 +02:00
} else if ( ( decl - > as_enumeration_type ( ) ! = nullptr ) & & i = = 0 ) {
2026-03-31 15:32:36 +02:00
return ifcopenshell : : Argument_ENUMERATION ;
2023-09-17 12:30:17 +02:00
}
if ( pt = = 0 ) {
2026-03-31 15:32:36 +02:00
return ifcopenshell : : Argument_UNKNOWN ;
2023-09-17 12:30:17 +02:00
}
2026-03-31 15:32:36 +02:00
return ifcopenshell : : from_parameter_type ( pt ) ;
2021-11-19 09:38:28 +01:00
}
2023-09-17 12:30:17 +02:00
} // namespace
2021-11-19 09:38:28 +01:00
2017-07-03 14:18:56 +02:00
class unregister_inverse_visitor {
2023-09-17 12:30:17 +02:00
private :
2026-03-31 15:32:36 +02:00
file & file_ ;
2026-01-04 10:40:02 +01:00
const express : : Base data_ ;
2017-07-03 14:18:56 +02:00
2023-09-17 12:30:17 +02:00
public :
2026-03-31 15:32:36 +02:00
unregister_inverse_visitor ( file & file , const express : : Base & data )
2023-09-17 12:30:17 +02:00
: file_ ( file ) ,
data_ ( data ) { }
2017-07-03 14:18:56 +02:00
2026-01-04 10:40:02 +01:00
void operator ( ) ( const express : : Base & inst , int index ) {
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 :
2026-03-31 15:32:36 +02:00
file & file_ ;
2026-01-04 10:40:02 +01:00
const express : : Base data_ ;
2017-07-03 14:18:56 +02:00
2023-09-17 12:30:17 +02:00
public :
2026-03-31 15:32:36 +02:00
register_inverse_visitor ( file & file , const express : : Base & data )
2023-09-17 12:30:17 +02:00
: file_ ( file ) ,
data_ ( data ) { }
2017-07-03 14:18:56 +02:00
2026-01-04 10:40:02 +01:00
void operator ( ) ( const express : : Base & 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 :
2026-01-04 10:40:02 +01:00
std : : vector < express : : Base > * list_ ;
2017-07-03 14:18:56 +02:00
2023-09-17 12:30:17 +02:00
public :
2026-01-04 10:40:02 +01:00
add_to_instance_list_visitor ( std : : vector < express : : Base > * list )
2023-09-17 12:30:17 +02:00
: list_ ( list ) { }
2017-07-03 14:18:56 +02:00
2026-01-04 10:40:02 +01:00
void operator ( ) ( const express : : Base & inst ) {
list_ - > push_back ( inst ) ;
2023-09-17 12:30:17 +02:00
}
2017-07-03 14:18:56 +02:00
} ;
class apply_individual_instance_visitor {
2023-09-17 12:30:17 +02:00
private :
2026-03-31 15:32:36 +02:00
std : : optional < attribute_value > attribute_ ;
2023-09-17 12:30:17 +02:00
int attribute_index_ ;
2026-01-04 10:40:02 +01:00
const express : : Base inst_ ;
2025-02-27 22:07:31 +01:00
2023-09-17 12:30:17 +02:00
template < typename T >
2026-03-31 15:32:36 +02:00
void apply_attribute_ ( T & t , const attribute_value & attr , int index ) const {
2025-03-14 16:16:25 +01:00
switch ( attr . type ( ) ) {
2026-03-31 15:32:36 +02:00
case ifcopenshell : : Argument_ENTITY_INSTANCE : {
2026-01-04 10:40:02 +01:00
express : : Base inst = attr ;
2023-09-17 12:30:17 +02:00
t ( inst , index ) ;
2025-03-14 16:16:25 +01:00
break ;
}
2026-03-31 15:32:36 +02:00
case ifcopenshell : : Argument_AGGREGATE_OF_ENTITY_INSTANCE : {
2026-01-04 10:40:02 +01:00
std : : vector < express : : Base > entity_list_attribute = attr ;
for ( auto & inst : entity_list_attribute ) {
t ( inst , index ) ;
2023-09-17 12:30:17 +02:00
}
2025-03-14 16:16:25 +01:00
break ;
}
2026-03-31 15:32:36 +02:00
case ifcopenshell : : Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE : {
2026-01-04 10:40:02 +01:00
std : : vector < std : : vector < express : : Base > > nested_list_attr = attr ;
for ( auto & vec : nested_list_attr ) {
for ( auto & inst : vec ) {
t ( inst , index ) ;
2023-09-17 12:30:17 +02:00
}
}
2025-03-14 16:16:25 +01:00
break ;
2023-09-17 12:30:17 +02:00
}
2025-03-14 16:16:25 +01:00
default :
break ;
}
}
2023-09-17 12:30:17 +02:00
public :
2026-03-31 15:32:36 +02:00
apply_individual_instance_visitor ( const attribute_value & attribute , int idx )
2024-08-23 20:29:07 +02:00
: attribute_ ( attribute )
, attribute_index_ ( idx )
{ }
2023-09-17 12:30:17 +02:00
2026-01-04 10:40:02 +01:00
apply_individual_instance_visitor ( const express : : Base & data )
2025-02-27 22:07:31 +01:00
: 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 {
2026-01-04 10:40:02 +01:00
const auto & decl = inst_ . declaration ( ) ;
2025-02-27 22:07:31 +01:00
for ( size_t i = 0 ; i < ( decl . as_entity ( ) ? decl . as_entity ( ) - > attribute_count ( ) : 1 ) ; + + i ) {
2026-01-04 10:40:02 +01:00
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 <
2026-01-04 10:40:02 +01:00
( ! std : : is_base_of_v < express : : Base , T > | | std : : is_same_v < express : : Base , T > ) ,
2025-08-26 10:19:47 +02:00
void > : : type
2026-01-04 10:40:02 +01:00
express : : Base : : 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 ) ) {
2026-03-31 15:32:36 +02:00
throw ifcopenshell : : exception ( " Only finite values are allowed " ) ;
2025-03-25 10:15:24 +01:00
}
}
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 ) ; } ) ) {
2026-03-31 15:32:36 +02:00
throw ifcopenshell : : exception ( " Only finite values are allowed " ) ;
2025-03-25 10:15:24 +01:00
}
}
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 ) ; } ) ) {
2026-03-31 15:32:36 +02:00
throw ifcopenshell : : exception ( " Only finite values are allowed " ) ;
2025-03-25 10:15:24 +01:00
}
}
}
2025-02-27 22:07:31 +01:00
auto current_attribute = get_attribute_value ( i ) ;
2023-09-17 12:30:17 +02:00
2026-01-04 10:40:02 +01:00
// Deregister old attribute guid in file guid map.
2026-01-08 10:35:46 +01:00
if ( i = = 0 & & ( file ( ) - > ifcroot_type ( ) ! = nullptr ) & & this - > declaration ( ) . is ( * file ( ) - > ifcroot_type ( ) ) ) {
2026-01-04 10:40:02 +01:00
try {
auto guid = ( std : : string ) current_attribute ;
2026-01-08 10:35:46 +01:00
auto it = file ( ) - > internal_guid_map ( ) . find ( guid ) ;
if ( it ! = file ( ) - > internal_guid_map ( ) . end ( ) ) {
2026-01-04 10:40:02 +01:00
const std : : pair < const std : : string , express : : Base > & p = * it ;
if ( p . second = = * this ) {
2026-01-08 10:35:46 +01:00
file ( ) - > internal_guid_map ( ) . erase ( it ) ;
2023-09-17 12:30:17 +02:00
}
}
2026-03-31 15:32:36 +02:00
} catch ( ifcopenshell : : exception & e ) {
2026-07-09 13:30:48 +02:00
file ( ) - > logger ( ) . error ( e ) ;
2023-09-17 12:30:17 +02:00
}
2026-01-04 10:40:02 +01:00
}
2017-07-03 14:18:56 +02:00
2026-03-31 15:32:36 +02:00
if constexpr ( std : : is_same_v < T , express : : Base > | | std : : is_same_v < T , std : : vector < express : : Base > > | | std : : is_same_v < T , std : : vector < std : : vector < express : : Base > > > | | std : : is_same_v < T , blank > ) {
2026-01-04 10:40:02 +01:00
// Deregister inverse indices in file
2026-01-08 10:35:46 +01:00
unregister_inverse_visitor visitor ( * file ( ) , * this ) ;
2026-01-04 10:40:02 +01:00
apply_individual_instance_visitor ( current_attribute , ( int ) i ) . apply ( visitor ) ;
2023-09-17 12:30:17 +02:00
}
2026-01-04 10:40:02 +01:00
2025-02-27 22:07:31 +01:00
{
2026-01-08 10:35:46 +01:00
void * const storage = std : : visit ( [ ] ( const auto & m ) { return ( void * ) & m ; } , file ( ) - > storage_ ) ;
2026-07-19 12:58:23 +03:00
data ( ) - > set_attribute_value ( i , t ) ;
2024-09-05 14:38:21 +02:00
}
2025-02-27 22:07:31 +01:00
auto new_attribute = get_attribute_value ( i ) ;
2023-09-17 12:30:17 +02:00
2026-01-04 10:40:02 +01:00
// Register inverse indices in file
if constexpr ( std : : is_same_v < T , express : : Base > | | std : : is_same_v < T , std : : vector < express : : Base > > | | std : : is_same_v < T , std : : vector < std : : vector < express : : Base > > > ) {
2026-01-08 10:35:46 +01:00
register_inverse_visitor visitor ( * file ( ) , * this ) ;
2026-01-04 10:40:02 +01:00
apply_individual_instance_visitor ( new_attribute , ( int ) i ) . apply ( visitor ) ;
}
2024-08-23 20:29:07 +02:00
2026-01-04 10:40:02 +01:00
// Register new attribute guid in guid map
2026-01-08 10:35:46 +01:00
if ( i = = 0 & & ( file ( ) - > ifcroot_type ( ) ! = nullptr ) & & this - > declaration ( ) . is ( * file ( ) - > ifcroot_type ( ) ) ) {
2026-01-04 10:40:02 +01:00
try {
auto guid = ( std : : string ) new_attribute ;
2026-01-08 10:35:46 +01:00
auto it = file ( ) - > internal_guid_map ( ) . find ( guid ) ;
if ( it ! = file ( ) - > internal_guid_map ( ) . end ( ) ) {
2026-07-09 13:30:48 +02:00
file ( ) - > logger ( ) . warning ( " Duplicate guid " + guid ) ;
2023-09-17 12:30:17 +02:00
}
2026-01-08 10:35:46 +01:00
file ( ) - > internal_guid_map ( ) . insert ( { guid , * this } ) ;
2026-03-31 15:32:36 +02:00
} catch ( ifcopenshell : : exception & e ) {
2026-07-09 13:30:48 +02:00
file ( ) - > logger ( ) . error ( 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 <
2026-01-04 10:40:02 +01:00
( ! std : : is_base_of_v < express : : Base , T > | | std : : is_same_v < express : : Base , T > ) ,
2025-08-26 10:19:47 +02:00
void > : : type
2026-01-04 10:40:02 +01:00
express : : Base : : 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 ) ;
}
2011-07-11 09:33:18 +00:00
//
// Parses the IFC file in fn
// Creates the maps
//
2017-07-23 14:52:04 +02:00
# ifdef USE_MMAP
2026-07-09 13:30:48 +02:00
file : : file ( const std : : string & fn , bool mmap , : : logger & log )
: logger_ ( log )
, schema_ ( nullptr )
, ifcroot_type_ ( nullptr )
2026-07-28 03:08:55 +02:00
, max_id_ ( 0 ) {
2025-10-24 12:06:59 +02:00
initialize ( fn , mmap ) ;
}
2026-03-31 15:32:36 +02:00
bool ifcopenshell : : file : : initialize ( const std : : string & fn , bool mmap ) {
2025-10-13 20:47:43 +02:00
if ( mmap ) {
2026-03-31 15:32:36 +02:00
file_reader < mmap_impl > s ( fn ) ;
2026-07-09 13:30:48 +02:00
storage_ . emplace < 1 > ( this , logger_ . get ( ) ) ;
2026-03-27 20:45:13 +01:00
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
} else {
2026-03-31 15:32:36 +02:00
file_reader < full_buffer_impl > s ( fn ) ;
2026-07-09 13:30:48 +02:00
storage_ . emplace < 1 > ( this , logger_ . get ( ) ) ;
2026-03-27 20:45:13 +01:00
std : : get < impl : : in_memory_file_storage > ( storage_ ) . read_from_stream ( & s , schema_ , max_id_ , types_to_bypass_loading_ ) ;
2025-10-24 12:06:59 +02: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
2026-01-04 10:40:02 +01:00
byid_ = decltype ( byid_ ) ( & std : : get < impl : : in_memory_file_storage > ( storage_ ) . byid_read_ ) ;
2025-10-13 20:47:43 +02:00
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 ;
2026-07-28 03:08:55 +02:00
header_ . reset ( new spf_header ( this , & logger_ . get ( ) ) ) ;
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-07-09 13:30:48 +02:00
file : : file ( const uninitialized_tag & , : : logger & log )
2026-07-28 03:08:55 +02:00
: good_ ( file_open_status : : UNKNOWN ) , logger_ ( log ) , schema_ ( nullptr ) , ifcroot_type_ ( nullptr ) , max_id_ ( 0 ) , header_ ( nullptr ) { }
2025-10-24 12:06:59 +02:00
2026-03-31 15:32:36 +02:00
bool ifcopenshell : : file : : 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 ) {
2026-03-31 15:32:36 +02:00
file_reader < full_buffer_impl > s ( path ) ;
2026-07-09 13:30:48 +02:00
storage_ . emplace < 1 > ( this , logger_ . get ( ) ) ;
2025-10-24 12:06:59 +02:00
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_ ) ) {
2025-08-27 15:14:20 +02:00
// @todo unify these names, it's already confusing enough as it stands
2026-01-04 10:40:02 +01:00
byid_ = decltype ( byid_ ) ( & std : : get < impl : : in_memory_file_storage > ( storage_ ) . byid_read_ ) ;
2025-08-27 15:14:20 +02:00
byref_excl_ = decltype ( byref_excl_ ) ( & std : : get < impl : : in_memory_file_storage > ( storage_ ) . byref_excl_ ) ;
2025-10-24 12:06:59 +02:00
byguid_ = decltype ( byguid_ ) ( & std : : get < impl : : in_memory_file_storage > ( storage_ ) . byguid_ ) ;
2025-08-27 15:14:20 +02:00
}
2025-03-09 21:20:16 +01:00
// 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-09-03 11:11:55 +02:00
// 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
2025-09-03 11:11:55 +02:00
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 {
2025-09-29 11:29:52 +02:00
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_ ) ;
2026-04-22 12:04:09 +02:00
good_ = file_open_status : : SUCCESS ;
2025-09-29 11:29:52 +02:00
} else {
good_ = file_open_status : : UNSUPPORTED_SCHEMA ;
}
2025-09-03 11:11:55 +02:00
}
2025-03-09 21:20:16 +01:00
// 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");
2025-02-21 16:12:16 +01:00
}
2025-08-29 14:13:21 +02:00
ifcroot_type_ = schema_ ? schema_ - > declaration_by_name ( " IfcRoot " ) : nullptr ;
2026-07-28 03:08:55 +02:00
header_ . reset ( new spf_header ( this , & logger_ . get ( ) ) ) ;
2025-10-24 12:06:59 +02:00
return good_ = = file_open_status : : SUCCESS ;
}
2026-03-31 15:32:36 +02:00
void ifcopenshell : : file : : bypass_type ( const std : : string & type_name ) {
2025-10-24 12:06:59 +02:00
types_to_bypass_loading_ . insert ( type_name ) ;
}
2026-07-09 13:30:48 +02:00
file : : file ( const std : : string & path , filetype ty , bool readonly , : : logger & log )
: logger_ ( log )
, schema_ ( nullptr )
, ifcroot_type_ ( nullptr )
2025-10-24 12:06:59 +02:00
, max_id_ ( 0 )
{
initialize ( path , ty , readonly ) ;
2011-08-20 09:08:56 +00:00
}
2017-08-22 10:16:07 +02:00
2026-07-09 13:30:48 +02:00
file : : file ( std : : istream & stream , int length , : : logger & log )
: logger_ ( log )
, schema_ ( nullptr )
, ifcroot_type_ ( nullptr )
2025-09-01 13:25:48 +02:00
, max_id_ ( 0 )
2025-08-27 15:14:20 +02:00
{
2026-03-31 15:32:36 +02:00
file_reader < pushed_sequential_impl > s ( caller_fed_tag { } ) ;
2025-10-13 20:47:43 +02:00
std : : string string_data ;
string_data . resize ( length ) ;
stream . read ( string_data . data ( ) , length ) ;
2026-03-31 15:32:36 +02:00
s . push_next_page ( string_data ) ;
2025-10-13 20:47:43 +02:00
2026-07-09 13:30:48 +02:00
storage_ . emplace < 1 > ( this , logger_ . get ( ) ) ;
2025-10-24 12:06:59 +02:00
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_ ;
2025-08-29 14:13:21 +02:00
ifcroot_type_ = schema_ ? schema_ - > declaration_by_name ( " IfcRoot " ) : nullptr ;
2026-01-04 10:40:02 +01:00
byid_ = decltype ( byid_ ) ( & std : : get < impl : : in_memory_file_storage > ( storage_ ) . byid_read_ ) ;
2025-08-29 14:15:58 +02:00
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-07-28 03:08:55 +02:00
header_ . reset ( new spf_header ( this , & logger_ . get ( ) ) ) ;
2011-08-20 09:08:56 +00:00
}
2015-02-17 19:48:41 +00:00
2026-07-09 13:30:48 +02:00
file : : file ( void * data , int length , : : logger & log )
: logger_ ( log )
, schema_ ( nullptr )
, ifcroot_type_ ( nullptr )
, max_id_ ( 0 )
2025-08-27 15:14:20 +02:00
{
2026-03-31 15:32:36 +02:00
file_reader < pushed_sequential_impl > s ( std : : string ( ( char * ) data , length ) , caller_fed_tag { } ) ;
2025-10-13 20:47:43 +02:00
2026-07-09 13:30:48 +02:00
storage_ . emplace < 1 > ( this , logger_ . get ( ) ) ;
2025-10-24 12:06:59 +02:00
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_ ;
2025-08-29 14:13:21 +02:00
ifcroot_type_ = schema_ ? schema_ - > declaration_by_name ( " IfcRoot " ) : nullptr ;
2026-01-04 10:40:02 +01:00
byid_ = decltype ( byid_ ) ( & std : : get < impl : : in_memory_file_storage > ( storage_ ) . byid_read_ ) ;
2025-08-29 14:15:58 +02:00
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-07-28 03:08:55 +02:00
header_ . reset ( new spf_header ( this , & logger_ . get ( ) ) ) ;
2017-12-12 10:33:24 +01:00
}
2026-07-09 13:30:48 +02:00
file : : file ( const ifcopenshell : : schema_definition * schema , filetype ty , const std : : string & path , : : logger & log )
: logger_ ( log )
, schema_ ( schema )
2025-02-21 16:12:16 +01:00
, ifcroot_type_ ( schema_ - > declaration_by_name ( " IfcRoot " ) )
, max_id_ ( 0 )
{
2025-03-13 13:09:39 +01:00
if ( ty = = FT_AUTODETECT ) {
ty = guess_file_type ( path ) ;
}
if ( ty = = FT_IFCSPF ) {
2026-07-09 13:30:48 +02:00
storage_ . emplace < 1 > ( this , logger_ . get ( ) ) ;
2025-02-27 22:07:31 +01:00
2026-01-04 10:40:02 +01:00
byid_ = decltype ( byid_ ) ( & std : : get < impl : : in_memory_file_storage > ( storage_ ) . byid_read_ ) ;
2025-03-09 21:20:16 +01:00
byref_excl_ = decltype ( byref_excl_ ) ( & std : : get < impl : : in_memory_file_storage > ( storage_ ) . byref_excl_ ) ;
2025-08-29 13:47:48 +02:00
byguid_ = decltype ( byguid_ ) ( & std : : get < impl : : in_memory_file_storage > ( storage_ ) . byguid_ ) ;
2025-03-09 21:20:16 +01:00
// 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
2025-03-09 21:20:16 +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_ ) ;
2025-08-29 13:47:48 +02:00
byguid_ = decltype ( byguid_ ) ( & std : : get < impl : : rocks_db_file_storage > ( storage_ ) . byguid_ ) ;
2025-03-09 21:20:16 +01:00
// 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
}
2026-07-24 19:17:24 +05:00
header_ . reset ( new spf_header ( this , & logger_ . get ( ) ) ) ;
2026-03-31 15:32:36 +02:00
set_default_header_values ( ) ;
2011-10-27 16:13:29 +00:00
}
2015-02-17 19:48:41 +00:00
2026-03-27 20:45:13 +01:00
namespace {
template < typename Reader >
2026-03-31 15:32:36 +02:00
void read_terminal ( spf_lexer < Reader > & lexer , const std : : string & term , bool trailing_semicolon ) {
if ( lexer . next ( ) . as_string ( ) ! = term ) {
throw exception ( std : : string ( " Expected " + term ) ) ;
2026-03-27 20:45:13 +01:00
}
if ( trailing_semicolon ) {
2026-03-31 15:32:36 +02:00
if ( ! lexer . next ( ) . is_operator ( ' ; ' ) ) {
throw exception ( " Expected ; " ) ;
2026-03-27 20:45:13 +01:00
}
}
}
template < typename Reader >
2026-06-11 15:51:40 +02:00
shared_pointer_type read_header_entity (
2026-03-31 15:32:36 +02:00
ifcopenshell : : file * file ,
ifcopenshell : : impl : : in_memory_file_storage & storage ,
spf_lexer < Reader > & lexer ,
ifcopenshell : : unresolved_references & references_to_resolve ,
const ifcopenshell : : entity & decl ) {
lexer . next ( ) ;
2026-06-11 15:51:40 +02:00
storage . file = file ;
storage . references_to_resolve = & references_to_resolve ;
return storage . load ( & lexer , std : : nullopt , & decl , nullptr , - 1 ) ;
2026-03-27 20:45:13 +01:00
}
template < typename Reader >
void parse_header (
2026-03-31 15:32:36 +02:00
ifcopenshell : : spf_header & header ,
ifcopenshell : : impl : : in_memory_file_storage & storage ,
spf_lexer < Reader > & lexer ,
ifcopenshell : : unresolved_references & references_to_resolve ) {
2026-03-27 20:45:13 +01:00
static const char * const ISO_10303_21 = " ISO-10303-21 " ;
static const char * const HEADER = " HEADER " ;
read_terminal ( lexer , ISO_10303_21 , true ) ;
read_terminal ( lexer , HEADER , true ) ;
read_terminal ( lexer , Header_section_schema : : file_description : : Class ( ) . name_uc ( ) , false ) ;
2026-04-09 16:18:53 +02:00
header . set_file_description ( read_header_entity ( header . owner_file ( ) , storage , lexer , references_to_resolve , Header_section_schema : : file_description : : Class ( ) ) ) ;
2026-03-31 15:32:36 +02:00
if ( ! lexer . next ( ) . is_operator ( ' ; ' ) ) {
throw exception ( " Expected ; " ) ;
2026-03-27 20:45:13 +01:00
}
read_terminal ( lexer , Header_section_schema : : file_name : : Class ( ) . name_uc ( ) , false ) ;
2026-04-09 16:18:53 +02:00
header . set_file_name ( read_header_entity ( header . owner_file ( ) , storage , lexer , references_to_resolve , Header_section_schema : : file_name : : Class ( ) ) ) ;
2026-03-31 15:32:36 +02:00
if ( ! lexer . next ( ) . is_operator ( ' ; ' ) ) {
throw exception ( " Expected ; " ) ;
2026-03-27 20:45:13 +01:00
}
read_terminal ( lexer , Header_section_schema : : file_schema : : Class ( ) . name_uc ( ) , false ) ;
2026-04-09 16:18:53 +02:00
header . set_file_schema ( read_header_entity ( header . owner_file ( ) , storage , lexer , references_to_resolve , Header_section_schema : : file_schema : : Class ( ) ) ) ;
2026-03-31 15:32:36 +02:00
if ( ! lexer . next ( ) . is_operator ( ' ; ' ) ) {
throw exception ( " Expected ; " ) ;
2026-03-27 20:45:13 +01:00
}
}
template < typename Reader >
bool try_parse_header (
2026-03-31 15:32:36 +02:00
ifcopenshell : : spf_header & header ,
ifcopenshell : : impl : : in_memory_file_storage & storage ,
spf_lexer < Reader > & lexer ,
ifcopenshell : : unresolved_references & references_to_resolve ) {
2026-03-27 20:45:13 +01:00
try {
parse_header ( header , storage , lexer , references_to_resolve ) ;
return true ;
} catch ( const std : : exception & e ) {
2026-07-09 13:30:48 +02:00
storage . logger_ . get ( ) . error ( e ) ;
2026-03-27 20:45:13 +01:00
return false ;
}
}
} // namespace
template < typename Reader >
2026-03-31 15:32:36 +02:00
spf_header & ifcopenshell : : instance_streamer < Reader > : : ensure_header ( ) {
2026-03-27 20:45:13 +01:00
if ( header_ ) {
return * header_ ;
}
if ( owner_ ! = nullptr ) {
header_ = & owner_ - > header ( ) ;
2026-04-09 16:18:53 +02:00
header_ - > owner_file ( owner_ ) ;
2026-03-27 20:45:13 +01:00
} else {
2026-07-24 19:17:24 +05:00
owned_header_ = std : : make_unique < spf_header > ( owner_ , & logger_ . get ( ) ) ;
2026-03-27 20:45:13 +01:00
header_ = owned_header_ . get ( ) ;
}
return * header_ ;
}
template < typename Reader >
2026-03-31 15:32:36 +02:00
void ifcopenshell : : instance_streamer < Reader > : : initialize_header ( ) {
2026-03-27 20:45:13 +01:00
storage_ . file = owner_ ;
storage_ . schema = schema_ ;
storage_ . references_to_resolve = & references_to_resolve_ ;
if ( ! lexer_ | | ! stream_ | | ! stream_ - > size ( ) | | stream_ - > eof ( ) ) {
return ;
}
auto & header = ensure_header ( ) ;
if ( try_parse_header ( header , storage_ , * lexer_ , references_to_resolve_ ) & & header . file_schema ( ) . schema_identifiers ( ) . size ( ) = = 1 ) {
try {
2026-03-31 15:32:36 +02:00
schema_ = ifcopenshell : : schema_by_name ( header . file_schema ( ) . schema_identifiers ( ) . front ( ) ) ;
2026-03-27 20:45:13 +01:00
good_ = file_open_status : : SUCCESS ;
2026-03-31 15:32:36 +02:00
} catch ( const ifcopenshell : : exception & ) {
2026-03-27 20:45:13 +01:00
}
}
storage_ . schema = schema_ ;
2026-06-11 15:51:40 +02:00
types_to_bypass_materialized_ . resize ( schema_ - > declarations ( ) . size ( ) , false ) ;
for ( auto & bp : types_to_bypass_ ) {
std : : function < void ( const ifcopenshell : : entity * ) > mark ;
mark = [ & ] ( const ifcopenshell : : entity * e ) {
types_to_bypass_materialized_ [ e - > index_in_schema ( ) ] = true ;
for ( auto & subtype : e - > subtypes ( ) ) {
mark ( subtype ) ;
}
} ;
if ( auto * e = bp - > as_entity ( ) ) {
mark ( e ) ;
}
}
2026-03-27 20:45:13 +01:00
}
template < typename Reader >
2026-03-31 15:32:36 +02:00
bool ifcopenshell : : instance_streamer < Reader > : : has_semicolon ( ) const {
2025-10-13 20:47:43 +02:00
auto local_stream = stream_ - > clone ( ) ;
2026-07-09 13:30:48 +02:00
auto local_lexer = spf_lexer < Reader > ( & local_stream , logger_ . get ( ) ) ;
2026-03-31 15:32:36 +02:00
token t ;
2025-10-22 21:42:15 +02:00
try {
2026-03-31 15:32:36 +02:00
t = local_lexer . next ( ) ;
2025-10-22 21:42:15 +02:00
} catch ( const std : : out_of_range & ) {
return false ;
}
2026-03-31 15:32:36 +02:00
while ( t . type ! = token : : Token_NONE ) {
2026-03-26 15:49:28 +01:00
if ( t . is_operator ( ' ; ' ) ) {
2025-10-13 20:47:43 +02:00
return true ;
2026-03-27 20:45:13 +01:00
}
2025-10-22 21:42:15 +02:00
try {
2026-03-31 15:32:36 +02:00
t = local_lexer . next ( ) ;
2025-10-22 21:42:15 +02:00
} catch ( const std : : out_of_range & ) {
break ;
}
2025-10-13 20:47:43 +02:00
}
2026-03-27 20:45:13 +01:00
return false ;
2025-10-13 20:47:43 +02:00
}
2026-03-27 20:45:13 +01:00
template < typename Reader >
2026-03-31 15:32:36 +02:00
size_t ifcopenshell : : instance_streamer < Reader > : : semicolon_count ( ) const {
2025-10-25 15:08:21 +02:00
auto local_stream = stream_ - > clone ( ) ;
2026-07-09 13:30:48 +02:00
auto local_lexer = spf_lexer < Reader > ( & local_stream , logger_ . get ( ) ) ;
2026-03-31 15:32:36 +02:00
token t ;
2025-10-25 15:08:21 +02:00
size_t count = 0 ;
try {
2026-03-31 15:32:36 +02:00
t = local_lexer . next ( ) ;
2025-10-25 15:08:21 +02:00
} catch ( const std : : out_of_range & ) {
return false ;
}
2026-03-31 15:32:36 +02:00
while ( t . type ! = token : : Token_NONE ) {
2026-03-26 15:49:28 +01:00
if ( t . is_operator ( ' ; ' ) ) {
2025-10-25 15:08:21 +02:00
count + + ;
}
try {
2026-03-31 15:32:36 +02:00
t = local_lexer . next ( ) ;
2025-10-25 15:08:21 +02:00
} catch ( const std : : out_of_range & ) {
break ;
}
}
return count ;
}
2026-03-27 20:45:13 +01:00
template < typename Reader >
2026-03-31 15:32:36 +02:00
void ifcopenshell : : instance_streamer < Reader > : : push_page ( const std : : string & page ) {
stream_ - > push_next_page ( page ) ;
2025-10-13 20:47:43 +02:00
if ( good_ = = file_open_status : : NO_HEADER ) {
2026-03-27 20:45:13 +01:00
initialize_header ( ) ;
2025-10-13 20:47:43 +02:00
}
}
2026-03-27 20:45:13 +01:00
template < typename Reader >
2026-07-09 13:30:48 +02:00
ifcopenshell : : instance_streamer < Reader > : : instance_streamer ( ifcopenshell : : file * f , : : logger & log )
2026-03-27 20:45:13 +01:00
: stream_ ( nullptr )
, header_ ( nullptr )
, owner_ ( f )
2026-03-31 15:32:36 +02:00
, token_stream_ ( 3 , token { } )
2025-10-13 20:47:43 +02:00
, schema_ ( nullptr )
2026-07-09 13:30:48 +02:00
, storage_ ( nullptr , log )
, logger_ ( log )
2025-10-13 20:47:43 +02:00
, progress_ ( 0 )
{
2026-03-31 15:32:36 +02:00
if constexpr ( std : : is_same_v < Reader , file_reader < full_buffer_impl > > ) {
owned_stream_ = std : : make_unique < Reader > ( caller_fed_tag { } ) ;
} else if constexpr ( std : : is_same_v < Reader , file_reader < pushed_sequential_impl > > ) {
2026-03-27 20:45:13 +01:00
owned_stream_ = std : : make_unique < Reader > ( caller_fed_tag { } ) ;
} else {
2026-03-31 15:32:36 +02:00
static_assert ( file_reader_dependent_false_v < Reader > , " Default instance_streamer requires a pushed sequential reader " ) ;
2026-03-27 20:45:13 +01:00
}
stream_ = owned_stream_ . get ( ) ;
2026-07-09 13:30:48 +02:00
lexer_ = std : : make_unique < spf_lexer < Reader > > ( stream_ , logger_ . get ( ) ) ;
2025-10-13 20:47:43 +02:00
good_ = file_open_status : : NO_HEADER ;
2026-01-14 14:03:28 +01:00
storage_ . file = f ;
2026-03-27 20:45:13 +01:00
storage_ . references_to_resolve = & references_to_resolve_ ;
2025-10-13 20:47:43 +02:00
}
2026-03-27 20:45:13 +01:00
template < typename Reader >
2026-07-09 13:30:48 +02:00
ifcopenshell : : instance_streamer < Reader > : : instance_streamer ( const std : : string & fn , bool mmap , ifcopenshell : : file * f , : : logger & log )
2026-03-27 20:45:13 +01:00
: stream_ ( nullptr )
, header_ ( nullptr )
, owner_ ( f )
2026-03-31 15:32:36 +02:00
, token_stream_ ( 3 , token { } )
2025-09-10 21:42:55 +02:00
, schema_ ( nullptr )
2026-07-09 13:30:48 +02:00
, storage_ ( nullptr , log )
, logger_ ( log )
2026-06-11 15:51:40 +02:00
, progress_ ( 0 )
{
if constexpr ( std : : is_same_v < Reader , file_reader < full_buffer_impl > > ) {
2026-03-27 20:45:13 +01:00
( void ) mmap ;
owned_stream_ = std : : make_unique < Reader > ( fn ) ;
# ifdef USE_MMAP
2026-03-31 15:32:36 +02:00
} else if constexpr ( std : : is_same_v < Reader , file_reader < mmap_impl > > ) {
2026-03-27 20:45:13 +01:00
( void ) mmap ;
owned_stream_ = std : : make_unique < Reader > ( fn ) ;
# endif
} else {
2026-03-31 15:32:36 +02:00
static_assert ( file_reader_dependent_false_v < Reader > , " Path-based instance_streamer requires a file-backed reader " ) ;
2025-09-23 11:32:52 +02:00
}
2026-03-27 20:45:13 +01:00
stream_ = owned_stream_ . get ( ) ;
2026-07-09 13:30:48 +02:00
lexer_ = std : : make_unique < spf_lexer < Reader > > ( stream_ , logger_ . get ( ) ) ;
2026-03-27 20:45:13 +01:00
good_ = file_open_status : : NO_HEADER ;
initialize_header ( ) ;
2025-09-23 11:32:52 +02:00
}
2026-03-27 20:45:13 +01:00
template < typename Reader >
2026-07-09 13:30:48 +02:00
ifcopenshell : : instance_streamer < Reader > : : instance_streamer ( void * data , int length , ifcopenshell : : file * f , : : logger & log )
2026-03-27 20:45:13 +01:00
: stream_ ( nullptr )
, header_ ( nullptr )
, owner_ ( f )
2026-03-31 15:32:36 +02:00
, token_stream_ ( 3 , token { } )
2025-09-23 11:32:52 +02:00
, schema_ ( nullptr )
2026-07-09 13:30:48 +02:00
, storage_ ( nullptr , log )
, logger_ ( log )
2025-09-23 11:32:52 +02:00
, progress_ ( 0 )
{
2026-03-31 15:32:36 +02:00
if constexpr ( std : : is_same_v < Reader , file_reader < full_buffer_impl > > ) {
owned_stream_ = std : : make_unique < Reader > ( std : : string ( ( char * ) data , length ) , caller_fed_tag { } ) ;
} else if constexpr ( std : : is_same_v < Reader , file_reader < pushed_sequential_impl > > ) {
2026-03-27 20:45:13 +01:00
owned_stream_ = std : : make_unique < Reader > ( std : : string ( ( char * ) data , length ) , caller_fed_tag { } ) ;
} else {
2026-03-31 15:32:36 +02:00
static_assert ( file_reader_dependent_false_v < Reader > , " Buffer-based instance_streamer requires a pushed sequential reader " ) ;
2025-09-10 21:42:55 +02:00
}
2026-03-27 20:45:13 +01:00
stream_ = owned_stream_ . get ( ) ;
2026-07-09 13:30:48 +02:00
lexer_ = std : : make_unique < spf_lexer < Reader > > ( stream_ , logger_ . get ( ) ) ;
2026-03-27 20:45:13 +01:00
good_ = file_open_status : : NO_HEADER ;
initialize_header ( ) ;
2025-09-10 21:42:55 +02:00
}
2026-03-27 20:45:13 +01:00
template < typename Reader >
2026-07-09 13:30:48 +02:00
ifcopenshell : : instance_streamer < Reader > : : instance_streamer ( Reader * stream , ifcopenshell : : file * f , : : logger & log )
2026-03-27 20:45:13 +01:00
: stream_ ( stream )
2025-09-10 21:42:55 +02:00
, header_ ( nullptr )
2026-03-27 20:45:13 +01:00
, owner_ ( f )
2026-03-31 15:32:36 +02:00
, token_stream_ ( 3 , token { } )
2026-03-27 20:45:13 +01:00
, schema_ ( nullptr )
2026-07-09 13:30:48 +02:00
, storage_ ( nullptr , log )
, logger_ ( log )
2026-06-11 15:51:40 +02:00
, progress_ ( 0 )
{
2026-07-09 13:30:48 +02:00
lexer_ = std : : make_unique < spf_lexer < Reader > > ( stream_ , logger_ . get ( ) ) ;
2026-03-27 20:45:13 +01:00
good_ = file_open_status : : NO_HEADER ;
initialize_header ( ) ;
2025-09-10 21:42:55 +02:00
}
2026-03-27 20:45:13 +01:00
template < typename Reader >
2026-03-31 15:32:36 +02:00
void ifcopenshell : : instance_streamer < Reader > : : bypass_types ( const std : : set < std : : string > & type_names ) {
2026-03-27 20:45:13 +01:00
for ( auto & name : type_names ) {
try {
types_to_bypass_ . push_back ( schema_ - > declaration_by_name ( name ) ) ;
2026-03-31 15:32:36 +02:00
} catch ( const exception & ) {
2026-03-27 20:45:13 +01:00
continue ;
}
}
}
template < typename Reader >
2026-06-11 15:51:40 +02:00
std : : optional < std : : tuple < size_t , const ifcopenshell : : declaration * , shared_pointer_type > > ifcopenshell : : instance_streamer < Reader > : : read_instance ( ) {
std : : optional < std : : tuple < size_t , const ifcopenshell : : declaration * , shared_pointer_type > > return_value ;
2026-03-27 20:45:13 +01:00
if ( yield_header_instances_ & & header_ & & yielded_header_instances_ < 3 ) {
if ( yielded_header_instances_ = = 0 ) {
return_value . emplace (
0 ,
& header_ - > file_description ( ) . declaration ( ) ,
2026-06-11 15:51:40 +02:00
# ifdef IFOPSH_SAFE_INSTANCE
2026-03-27 20:45:13 +01:00
header_ - > file_description ( ) . data_weak ( ) . lock ( ) ) ;
2026-06-11 15:51:40 +02:00
# else
header_ - > file_description ( ) . data_weak ( ) ) ;
# endif
2026-03-27 20:45:13 +01:00
} else if ( yielded_header_instances_ = = 1 ) {
return_value . emplace (
0 ,
& header_ - > file_name ( ) . declaration ( ) ,
2026-06-11 15:51:40 +02:00
# ifdef IFOPSH_SAFE_INSTANCE
2026-03-27 20:45:13 +01:00
header_ - > file_name ( ) . data_weak ( ) . lock ( ) ) ;
2026-06-11 15:51:40 +02:00
# else
header_ - > file_name ( ) . data_weak ( ) ) ;
# endif
2026-03-27 20:45:13 +01:00
} else if ( yielded_header_instances_ = = 2 ) {
return_value . emplace (
0 ,
& header_ - > file_schema ( ) . declaration ( ) ,
2026-06-11 15:51:40 +02:00
# ifdef IFOPSH_SAFE_INSTANCE
2026-03-27 20:45:13 +01:00
header_ - > file_schema ( ) . data_weak ( ) . lock ( ) ) ;
2026-06-11 15:51:40 +02:00
# else
header_ - > file_schema ( ) . data_weak ( ) ) ;
# endif
2026-03-27 20:45:13 +01:00
}
yielded_header_instances_ + = 1 ;
return return_value ;
}
unsigned current_id = 0 ;
while ( good_ & & ! lexer_ - > stream - > eof ( ) & & ! current_id ) {
2026-03-31 15:32:36 +02:00
if ( token_stream_ [ 0 ] . type = = ifcopenshell : : token : : Token_IDENTIFIER & &
token_stream_ [ 1 ] . type = = ifcopenshell : : token : : Token_OPERATOR & &
2026-03-27 20:45:13 +01:00
token_stream_ [ 1 ] . value_char = = ' = ' & &
2026-03-31 15:32:36 +02:00
token_stream_ [ 2 ] . type = = ifcopenshell : : token : : Token_KEYWORD ) {
2026-03-27 20:45:13 +01:00
current_id = token_stream_ [ 0 ] . as_identifier ( ) ;
2026-03-31 15:32:36 +02:00
const ifcopenshell : : declaration * entity_type ;
2026-03-27 20:45:13 +01:00
try {
entity_type = schema_ - > declaration_by_name ( token_stream_ [ 2 ] . as_string ( ) ) ;
2026-03-31 15:32:36 +02:00
} catch ( const exception & ex ) {
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . message ( : : logger : : LOG_ERROR , std : : string ( ex . what ( ) ) + " at offset " + std : : to_string ( token_stream_ [ 2 ] . start_pos ) ) ;
2026-03-27 20:45:13 +01:00
current_id = 0 ;
goto advance ;
}
if ( entity_type - > as_entity ( ) = = nullptr ) {
2026-07-26 16:32:47 +10:00
logger_ . get ( ) . message ( : : logger : : LOG_ERROR , " Non-entity type " + entity_type - > name ( ) + " at offset " + std : : to_string ( token_stream_ [ 2 ] . start_pos ) ) ;
current_id = 0 ;
2026-03-27 20:45:13 +01:00
goto advance ;
}
2026-06-11 15:51:40 +02:00
if ( types_to_bypass_materialized_ [ entity_type - > index_in_schema ( ) ] ) {
bypassed_instances_ . push_back ( current_id ) ;
current_id = 0 ;
goto advance ;
2026-03-27 20:45:13 +01:00
}
2026-03-31 15:32:36 +02:00
lexer_ - > next ( ) ;
2026-03-27 20:45:13 +01:00
try {
2026-06-11 15:51:40 +02:00
auto data = storage_ . load ( lexer_ . get ( ) , current_id , entity_type , entity_type - > as_entity ( ) , - 1 , coerce_attribute_count ) ;
if ( ( ( + + progress_ ) % 1000 ) = = 0 ) {
std : : stringstream ss ;
ss < < " \r # " < < current_id ;
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . status ( ss . str ( ) , false ) ;
2026-06-11 15:51:40 +02:00
}
return_value . emplace (
( size_t ) current_id ,
entity_type ,
data ) ;
2026-03-31 15:32:36 +02:00
} catch ( const invalid_token_exception & e ) {
2026-03-27 20:45:13 +01:00
good_ = file_open_status : : INVALID_SYNTAX ;
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . error ( e ) ;
2026-03-27 20:45:13 +01:00
break ;
}
}
advance :
2026-03-31 15:32:36 +02:00
token next_token ;
2026-03-27 20:45:13 +01:00
try {
2026-03-31 15:32:36 +02:00
next_token = lexer_ - > next ( ) ;
} catch ( const exception & e ) {
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . message ( : : logger : : LOG_ERROR , std : : string ( e . what ( ) ) + " . Parsing terminated " ) ;
2026-03-27 20:45:13 +01:00
} catch ( . . . ) {
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . message ( : : logger : : LOG_ERROR , " Parsing terminated " ) ;
2026-03-27 20:45:13 +01:00
}
if ( ! lexer_ - > stream - > eof ( ) & & ! next_token ) {
good_ = file_open_status : : INVALID_SYNTAX ;
break ;
}
token_stream_ . push_back ( next_token ) ;
}
2026-03-31 15:32:36 +02:00
stream_ - > drop_pages ( ) ;
lexer_ - > reset_pool ( ) ;
2026-03-27 20:45:13 +01:00
return return_value ;
}
2026-04-21 11:55:04 +02:00
template class IFC_PARSE_API ifcopenshell : : instance_streamer < file_reader < full_buffer_impl > > ;
2026-03-31 15:32:36 +02:00
2026-03-27 20:45:13 +01:00
template < typename Reader >
2026-03-31 15:32:36 +02:00
void ifcopenshell : : impl : : in_memory_file_storage : : read_from_stream ( Reader * s , const ifcopenshell : : schema_definition * & schema , unsigned int & max_id , const std : : set < std : : string > & typed_to_bypass ) {
2026-03-27 20:45:13 +01:00
schema = nullptr ;
2025-08-25 12:45:10 +02:00
2025-10-13 20:47:43 +02:00
if ( ! s - > size ( ) | | s - > eof ( ) ) {
2023-09-17 12:30:17 +02:00
good_ = file_open_status : : READ_ERROR ;
return ;
}
2015-01-05 17:46:23 +00:00
2023-09-17 12:30:17 +02:00
std : : vector < std : : string > schemas ;
2017-12-12 10:33:24 +01:00
2026-07-09 13:30:48 +02:00
instance_streamer < Reader > streamer ( s , file , logger_ . get ( ) ) ;
2026-03-31 15:32:36 +02:00
streamer . yield_header_instances ( false ) ;
2025-02-21 16:12:16 +01:00
2026-03-27 20:45:13 +01:00
if ( const auto * header = streamer . header ( ) ) {
2023-09-17 12:30:17 +02:00
try {
2026-03-27 20:45:13 +01:00
schemas = header - > file_schema ( ) . schema_identifiers ( ) ;
2023-09-17 12:30:17 +02:00
} catch ( . . . ) {
}
}
2026-03-27 20:45:13 +01:00
schema = streamer . schema ( ) ;
if ( schema = = nullptr & & schemas . size ( ) = = 1 ) {
2023-09-17 12:30:17 +02:00
try {
2026-03-31 15:32:36 +02:00
schema = ifcopenshell : : schema_by_name ( schemas . front ( ) ) ;
} catch ( const ifcopenshell : : exception & e ) {
2023-09-17 12:30:17 +02:00
good_ = file_open_status : : UNSUPPORTED_SCHEMA ;
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . error ( e ) ;
2023-09-17 12:30:17 +02:00
}
}
2025-08-27 15:14:20 +02:00
if ( schema = = nullptr ) {
2026-03-27 20:45:13 +01:00
if ( schemas . empty ( ) ) {
good_ = streamer . status ( ) ;
} else {
good_ = file_open_status : : UNSUPPORTED_SCHEMA ;
}
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . message ( : : logger : : LOG_ERROR , " No support for file schema encountered ( " + boost : : algorithm : : join ( schemas , " , " ) + " ) " ) ;
2023-09-17 12:30:17 +02:00
return ;
}
2025-02-21 16:12:16 +01:00
auto ifcroot_type_ = schema - > declaration_by_name ( " IfcRoot " ) ;
2026-03-31 15:32:36 +02:00
streamer . bypass_types ( typed_to_bypass ) ;
2023-09-17 12:30:17 +02:00
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . status ( " Scanning file... " ) ;
2015-01-05 17:46:23 +00:00
2025-08-25 12:45:10 +02:00
while ( streamer ) {
2026-03-31 15:32:36 +02:00
auto inst = streamer . read_instance ( ) ;
2023-09-17 12:30:17 +02:00
2025-08-25 12:45:10 +02:00
if ( ! inst ) {
break ;
2026-03-27 20:45:13 +01:00
}
2025-10-24 12:06:59 +02:00
2025-08-25 12:45:10 +02:00
auto current_id = std : : get < 0 > ( * inst ) ;
2026-01-04 10:40:02 +01:00
express : : Base instance ( std : : get < 2 > ( * inst ) ) ;
2023-09-17 12:30:17 +02:00
2026-01-04 10:40:02 +01:00
if ( instance . declaration ( ) . is ( * ifcroot_type_ ) ) {
2025-08-25 12:45:10 +02:00
try {
2026-01-04 10:40:02 +01:00
const std : : string guid = instance . get_attribute_value ( 0 ) ;
2025-08-25 12:45:10 +02:00
if ( byguid_ . find ( guid ) ! = byguid_ . end ( ) ) {
std : : stringstream ss ;
ss < < " Instance encountered with non-unique GlobalId " < < guid ;
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . message ( : : logger : : LOG_WARNING , ss . str ( ) ) ;
2023-09-17 12:30:17 +02:00
}
2025-08-25 12:45:10 +02:00
byguid_ [ guid ] = instance ;
2026-03-31 15:32:36 +02:00
} catch ( const exception & ex ) {
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . message ( : : logger : : LOG_ERROR , ex . what ( ) ) ;
2023-09-17 12:30:17 +02:00
}
2025-08-25 12:45:10 +02:00
}
2023-09-17 12:30:17 +02:00
2026-03-31 15:32:36 +02:00
const ifcopenshell : : declaration * ty = & instance . declaration ( ) ;
2026-03-27 20:45:13 +01:00
bytype_excl_ [ ty ] . push_back ( instance ) ;
2023-09-17 12:30:17 +02:00
2025-08-25 12:45:10 +02:00
if ( byid_ . find ( current_id ) ! = byid_ . end ( ) ) {
std : : stringstream ss ;
ss < < " Overwriting instance with name # " < < current_id ;
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . message ( : : logger : : LOG_WARNING , ss . str ( ) ) ;
2023-09-17 12:30:17 +02:00
}
2026-01-04 10:40:02 +01:00
byid_ . insert ( { ( uint32_t ) current_id , std : : get < 2 > ( * inst ) } ) ;
2026-03-27 20:45:13 +01: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
2026-03-27 20:45:13 +01:00
good_ = streamer . status ( ) ;
2026-06-11 15:51:40 +02:00
byref_excl_ = std : : move ( streamer . inverses ( ) ) ;
byref_excl_ . sort ( ) ;
2026-03-31 15:32:36 +02:00
read_simple_type_instances = streamer . steal_instances ( ) ;
2025-08-25 12:45:10 +02:00
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . status ( " \r Done scanning file " ) ;
2017-06-05 17:26:58 +02:00
2025-01-23 16:20:06 +01:00
if ( good_ ! = file_open_status : : SUCCESS ) {
return ;
}
2025-10-24 12:06:59 +02:00
const auto & bypassed = streamer . bypassed_instances ( ) ;
2025-08-25 12:45:10 +02:00
for ( const auto & p : streamer . references ( ) ) {
2024-09-23 15:10:37 +02:00
const auto & ref = p . first . name_ ;
const auto & refattr = p . first . index_ ;
2026-07-18 00:45:55 +01:00
auto owner_it = byid_ . find ( ref ) ;
if ( owner_it = = byid_ . end ( ) ) {
logger_ . get ( ) . error ( " Instance # " + std : : to_string ( ref ) + " referenced at attribute index " + std : : to_string ( refattr ) + " not found " ) ;
continue ;
}
auto & owner = owner_it - > second ;
2025-08-25 12:45:10 +02:00
if ( auto * v = std : : get_if < reference_or_simple_type > ( & p . second ) ) {
2026-03-31 15:32:36 +02:00
if ( auto * name = std : : get_if < instance_reference > ( v ) ) {
2025-10-24 12:06:59 +02:00
if ( std : : binary_search ( bypassed . begin ( ) , bypassed . end ( ) , * name ) ) {
continue ;
}
2025-02-21 16:12:16 +01:00
auto it = byid_ . find ( * name ) ;
2024-09-23 15:10:37 +02:00
if ( it = = byid_ . end ( ) ) {
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . error ( " 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 ) ) ;
2024-09-23 15:10:37 +02:00
} else {
2026-07-18 00:45:55 +01:00
auto & storage = owner ;
2025-04-11 15:52:40 +02:00
auto attr_index = p . first . index_ ;
2026-04-09 16:18:53 +02:00
if ( storage - > template has_attribute_value < express : : Base > ( attr_index ) ) {
2026-01-04 10:40:02 +01:00
express : : Base inst = storage - > get_attribute_value ( attr_index ) ;
2026-07-18 00:45:55 +01:00
if ( inst & & ! inst . declaration ( ) . as_entity ( ) ) {
2025-08-26 10:17:57 +02:00
// Probably a case of IfcPropertySetDefinitionSet, divert storage of reference to the simply type instance
2026-06-11 15:51:40 +02:00
# ifdef IFOPSH_SAFE_INSTANCE
2026-01-04 10:40:02 +01:00
storage = inst . data_weak ( ) . lock ( ) ;
2026-06-11 15:51:40 +02:00
# else
storage = inst . data_weak ( ) ;
# endif
2025-04-11 15:52:40 +02:00
attr_index = 0 ;
}
}
2026-04-09 16:18:53 +02:00
if ( storage - > template has_attribute_value < blank > ( attr_index ) ) {
2026-01-04 10:40:02 +01:00
storage - > set_attribute_value ( attr_index , express : : Base ( it - > second ) ) ;
2025-04-11 15:52:40 +02:00
} else {
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . error ( " Duplicate definition for instance reference " ) ;
2025-04-11 15:52:40 +02:00
}
2024-08-23 20:29:07 +02:00
}
2026-01-04 10:40:02 +01:00
} else if ( auto inst = std : : get_if < express : : Base > ( v ) ) {
2026-07-18 00:45:55 +01:00
owner - > set_attribute_value ( p . first . index_ , * inst ) ;
2024-09-23 15:10:37 +02:00
}
2025-10-13 20:47:43 +02:00
} else if ( auto * vv = std : : get_if < std : : vector < reference_or_simple_type > > ( & p . second ) ) {
2026-01-04 10:40:02 +01:00
std : : vector < express : : Base > instances ;
instances . reserve ( vv - > size ( ) ) ;
2025-10-13 20:47:43 +02:00
for ( const auto & vi : * vv ) {
2026-03-31 15:32:36 +02:00
if ( auto * name = std : : get_if < instance_reference > ( & vi ) ) {
2025-10-24 12:06:59 +02:00
if ( std : : binary_search ( bypassed . begin ( ) , bypassed . end ( ) , * name ) ) {
continue ;
}
2025-02-21 16:12:16 +01:00
auto it = byid_ . find ( * name ) ;
2024-09-23 15:10:37 +02:00
if ( it = = byid_ . end ( ) ) {
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . error ( " 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 ) ) ;
2024-09-23 15:10:37 +02:00
} else {
2026-01-04 10:40:02 +01:00
instances . push_back ( express : : Base ( it - > second ) ) ;
2024-08-23 20:29:07 +02:00
}
2026-01-04 10:40:02 +01:00
} else if ( auto * inst = std : : get_if < express : : Base > ( & vi ) ) {
instances . push_back ( * inst ) ;
2024-08-23 20:29:07 +02:00
}
2024-09-23 15:10:37 +02:00
}
2025-04-11 15:52:40 +02:00
2026-07-18 00:45:55 +01:00
auto & storage = owner ;
2025-04-11 15:52:40 +02:00
auto attr_index = p . first . index_ ;
2025-08-26 10:17:57 +02:00
2026-04-09 16:18:53 +02:00
if ( storage - > template has_attribute_value < express : : Base > ( attr_index ) ) {
2026-01-04 10:40:02 +01:00
express : : Base inst = storage - > get_attribute_value ( attr_index ) ;
2026-07-18 00:45:55 +01:00
if ( inst & & ! inst . declaration ( ) . as_entity ( ) ) {
2025-08-26 10:17:57 +02:00
// Probably a case of IfcPropertySetDefinitionSet, divert storage of reference to the simply type instance
2026-06-11 15:51:40 +02:00
# ifdef IFOPSH_SAFE_INSTANCE
2026-01-04 10:40:02 +01:00
storage = inst . data_weak ( ) . lock ( ) ;
2026-06-11 15:51:40 +02:00
# else
storage = inst . data_weak ( ) ;
# endif
2025-04-11 15:52:40 +02:00
attr_index = 0 ;
}
}
2026-04-09 16:18:53 +02:00
if ( storage - > template has_attribute_value < blank > ( attr_index ) ) {
2026-01-04 10:40:02 +01:00
storage - > set_attribute_value ( attr_index , instances ) ;
2025-04-11 15:52:40 +02:00
} else {
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . error ( " Duplicate definition for instance reference " ) ;
2025-04-11 15:52:40 +02:00
}
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 ) ) {
2026-01-04 10:40:02 +01:00
std : : vector < std : : vector < express : : Base > > instances ;
2025-10-13 20:47:43 +02:00
for ( const auto & vi : * vvv ) {
2026-01-04 10:40:02 +01:00
auto & inner = instances . emplace_back ( ) ;
2024-09-23 15:10:37 +02:00
for ( const auto & vii : vi ) {
2026-03-31 15:32:36 +02:00
if ( auto * name = std : : get_if < instance_reference > ( & vii ) ) {
2025-10-24 12:06:59 +02:00
if ( std : : binary_search ( bypassed . begin ( ) , bypassed . end ( ) , * name ) ) {
continue ;
}
2025-02-21 16:12:16 +01:00
auto it = byid_ . find ( * name ) ;
2024-09-23 15:10:37 +02:00
if ( it = = byid_ . end ( ) ) {
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . error ( " 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 ) ) ;
2024-09-23 15:10:37 +02:00
} else {
2026-01-04 10:40:02 +01:00
inner . push_back ( express : : Base ( it - > second ) ) ;
2024-08-23 20:29:07 +02:00
}
2026-01-04 10:40:02 +01:00
} else if ( auto * inst = std : : get_if < express : : Base > ( & vii ) ) {
2024-09-23 15:10:37 +02:00
inner . push_back ( * inst ) ;
2024-08-23 20:29:07 +02:00
}
}
}
2025-04-11 15:52:40 +02:00
2026-07-18 00:45:55 +01:00
auto & storage = owner ;
2025-04-11 15:52:40 +02:00
auto attr_index = p . first . index_ ;
2025-08-26 10:17:57 +02:00
2026-04-09 16:18:53 +02:00
if ( storage - > template has_attribute_value < express : : Base > ( attr_index ) ) {
2026-01-04 10:40:02 +01:00
express : : Base inst = storage - > get_attribute_value ( attr_index ) ;
2026-07-18 00:45:55 +01:00
if ( inst & & ! inst . declaration ( ) . as_entity ( ) ) {
2025-08-26 10:17:57 +02:00
// Probably a case of IfcPropertySetDefinitionSet, divert storage of reference to the simply type instance
2026-06-11 15:51:40 +02:00
# ifdef IFOPSH_SAFE_INSTANCE
2026-01-04 10:40:02 +01:00
storage = inst . data_weak ( ) . lock ( ) ;
2026-06-11 15:51:40 +02:00
# else
storage = inst . data_weak ( ) ;
# endif
2025-04-11 15:52:40 +02:00
attr_index = 0 ;
}
}
2026-04-09 16:18:53 +02:00
if ( storage - > template has_attribute_value < blank > ( attr_index ) ) {
2026-01-04 10:40:02 +01:00
storage - > set_attribute_value ( attr_index , instances ) ;
2025-04-11 15:52:40 +02:00
} else {
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . error ( " Duplicate definition for instance reference " ) ;
2025-04-11 15:52:40 +02:00
}
2024-09-23 15:10:37 +02:00
}
2024-08-23 20:29:07 +02:00
}
2023-09-17 12:30:17 +02:00
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . status ( " Done resolving references " ) ;
2011-07-11 09:33:18 +00:00
}
2015-02-17 19:48:41 +00:00
2026-03-31 15:32:36 +02:00
template void ifcopenshell : : impl : : in_memory_file_storage : : read_from_stream ( file_reader < full_buffer_impl > * s , const ifcopenshell : : schema_definition * & schema , unsigned int & max_id , const std : : set < std : : string > & typed_to_bypass ) ;
template void ifcopenshell : : impl : : in_memory_file_storage : : read_from_stream ( file_reader < pushed_sequential_impl > * s , const ifcopenshell : : schema_definition * & schema , unsigned int & max_id , const std : : set < std : : string > & typed_to_bypass ) ;
2026-03-27 20:45:13 +01:00
# ifdef USE_MMAP
2026-03-31 15:32:36 +02:00
template void ifcopenshell : : impl : : in_memory_file_storage : : read_from_stream ( file_reader < mmap_impl > * s , const ifcopenshell : : schema_definition * & schema , unsigned int & max_id , const std : : set < std : : string > & typed_to_bypass ) ;
2026-03-27 20:45:13 +01:00
# endif
2026-03-31 15:32:36 +02:00
void file : : recalculate_id_counter ( ) {
2025-02-21 16:12:16 +01:00
/*
// @todo
2023-09-17 12:30:17 +02:00
entity_by_id_t::key_type k = 0;
2023-11-06 20:38:19 +01:00
for (auto& p : byid_) {
2023-09-17 12:30:17 +02:00
if (p.first > k) {
k = p.first;
}
}
2025-02-21 16:12:16 +01:00
max_id_ = (unsigned int)k;
*/
2021-06-26 13:23:55 +02:00
}
2021-08-12 13:35:45 +02:00
class traversal_recorder {
2026-01-04 10:40:02 +01:00
std : : vector < express : : Base > list_ ;
std : : map < int , std : : vector < express : : Base > > instances_by_level_ ;
2023-09-17 12:30:17 +02:00
int mode_ ;
public :
traversal_recorder ( int mode ) : mode_ ( mode ) {
} ;
2026-01-04 10:40:02 +01:00
void push_back ( int level , const express : : Base & instance ) {
2023-09-17 12:30:17 +02:00
if ( mode_ = = 0 ) {
2026-01-04 10:40:02 +01:00
list_ . push_back ( instance ) ;
2023-09-17 12:30:17 +02:00
} else {
auto & l = instances_by_level_ [ level ] ;
2026-01-04 10:40:02 +01:00
l . push_back ( instance ) ;
2023-09-17 12:30:17 +02:00
}
}
2021-08-12 13:35:45 +02:00
2026-01-04 10:40:02 +01:00
std : : vector < express : : Base > get_list ( ) const {
2023-09-17 12:30:17 +02:00
if ( mode_ = = 0 ) {
return list_ ;
}
2026-01-04 10:40:02 +01:00
std : : vector < express : : Base > l ;
2023-10-24 16:11:26 +02:00
for ( const auto & p : instances_by_level_ ) {
2026-01-04 10:40:02 +01:00
l . insert ( l . end ( ) , p . second . begin ( ) , p . second . end ( ) ) ;
2023-10-24 12:21:32 +02:00
}
return l ;
2023-09-17 12:30:17 +02:00
}
2021-08-12 13:35:45 +02:00
} ;
2017-08-01 19:07:46 +02:00
class traversal_visitor {
2023-09-17 12:30:17 +02:00
private :
2026-01-04 10:40:02 +01:00
std : : set < express : : Base > & visited_ ;
2023-09-17 12:30:17 +02:00
traversal_recorder & list_ ;
int level_ ;
int max_level_ ;
public :
2026-01-04 10:40:02 +01:00
traversal_visitor ( std : : set < express : : Base > & visited , traversal_recorder & list , int level , int max_level )
2023-09-17 12:30:17 +02:00
: visited_ ( visited ) ,
list_ ( list ) ,
level_ ( level ) ,
max_level_ ( max_level ) { }
2026-01-04 10:40:02 +01:00
void operator ( ) ( const express : : Base & inst , int index ) ;
2017-08-01 19:07:46 +02:00
} ;
2026-01-04 10:40:02 +01:00
void traverse_ ( const express : : Base & instance , std : : set < express : : Base > & 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 ) ;
2015-02-17 19:48:41 +00:00
2023-09-17 12:30:17 +02:00
if ( level > = max_level & & max_level > 0 ) {
return ;
}
2015-02-17 19:48:41 +00:00
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 ) ;
2015-02-17 19:48:41 +00:00
}
2026-01-04 10:40:02 +01:00
void traversal_visitor : : operator ( ) ( const express : : Base & 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
}
2026-03-31 15:32:36 +02:00
std : : vector < express : : Base > ifcopenshell : : traverse ( const express : : Base & instance , int max_level ) {
2026-01-04 10:40:02 +01:00
std : : set < express : : Base > visited ;
2023-11-06 20:29:00 +01:00
traversal_recorder recorder ( 0 ) ;
traverse_ ( instance , visited , recorder , 0 , max_level ) ;
return recorder . get_list ( ) ;
2021-08-12 13:35:45 +02:00
}
// 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?
2026-03-31 15:32:36 +02:00
std : : vector < express : : Base > ifcopenshell : : traverse_breadth_first ( const express : : Base & instance , int max_level ) {
2026-01-04 10:40:02 +01:00
std : : set < express : : Base > visited ;
2023-11-06 20:29:00 +01:00
traversal_recorder recorder ( 1 ) ;
traverse_ ( instance , visited , recorder , 0 , max_level ) ;
return recorder . get_list ( ) ;
2015-02-17 19:48:41 +00:00
}
2017-06-20 18:54:30 +02:00
/// @note: for backwards compatibility
2026-03-31 15:32:36 +02:00
std : : vector < express : : Base > file : : traverse ( const express : : Base & instance , int max_level ) {
return ifcopenshell : : traverse ( instance , max_level ) ;
2017-06-20 18:54:30 +02:00
}
2021-08-12 13:35:45 +02:00
/// @note: for backwards compatibility
2026-03-31 15:32:36 +02:00
std : : vector < express : : Base > file : : traverse_breadth_first ( const express : : Base & instance , int max_level ) {
return ifcopenshell : : traverse_breadth_first ( instance , max_level ) ;
2021-08-12 13:35:45 +02:00
}
2026-03-31 15:32:36 +02:00
express : : Base file : : add_entity ( const express : : Base & entity , int id ) {
2026-01-08 10:35:46 +01:00
if ( entity . file ( ) = = this ) {
2026-01-04 10:40:02 +01:00
return entity ;
2023-09-17 12:30:17 +02:00
}
2021-06-27 11:14:26 +02:00
2026-01-04 10:40:02 +01:00
if ( entity . declaration ( ) . schema ( ) ! = schema ( ) ) {
2026-03-31 15:32:36 +02:00
throw ifcopenshell : : exception ( " Unabled to add instance from " + entity . declaration ( ) . schema ( ) - > name ( ) + " schema to file with " + schema ( ) - > name ( ) + " schema " ) ;
2023-09-17 12:30:17 +02:00
}
2018-11-04 15:16:08 +01:00
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.
2026-01-04 10:40:02 +01:00
entity_entity_map_t : : iterator mit = entity_file_map_ . find ( entity . identity ( ) ) ;
2023-11-06 20:38:19 +01:00
if ( mit ! = entity_file_map_ . end ( ) ) {
2023-09-17 12:30:17 +02:00
return mit - > second ;
}
2015-02-17 19:48:41 +00:00
2023-09-17 12:30:17 +02:00
// Obtain all forward references by a depth-first
// traversal and add them to the file.
2024-08-23 20:29:07 +02:00
try {
2026-01-04 10:40:02 +01:00
auto entity_attributes = traverse ( entity , 1 ) ;
for ( auto it = entity_attributes . begin ( ) + 1 ; it ! = entity_attributes . end ( ) ; + + it ) {
2024-08-23 20:29:07 +02:00
if ( * it ! = entity ) {
2026-01-04 10:40:02 +01:00
entity_entity_map_t : : iterator mit2 = entity_file_map_ . find ( it - > identity ( ) ) ;
2024-08-23 20:29:07 +02:00
if ( mit2 = = entity_file_map_ . end ( ) ) {
2026-03-31 15:32:36 +02:00
entity_file_map_ . insert ( entity_entity_map_t : : value_type ( it - > identity ( ) , add_entity ( * it ) ) ) ;
2023-09-17 12:30:17 +02:00
}
}
}
2024-08-23 20:29:07 +02:00
} catch ( . . . ) {
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . message ( : : logger : : LOG_ERROR , " Failed to visit forward references of " , entity ) ;
2023-09-17 12:30:17 +02:00
}
2015-02-17 19:48:41 +00:00
2026-01-04 10:40:02 +01:00
// 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.
2026-03-31 15:32:36 +02:00
file * other_file = entity . file ( ) ;
2026-01-08 11:49:29 +01:00
auto new_entity = create ( & entity . declaration ( ) , id ) ;
2026-01-04 10:40:02 +01:00
auto * decl = & entity . declaration ( ) ;
auto num_attributes = ( decl - > as_entity ( ) ? decl - > as_entity ( ) - > attribute_count ( ) : 1 ) ;
for ( size_t i = 0 ; i < num_attributes ; + + i ) {
2026-01-08 10:35:46 +01:00
entity . get_attribute_value ( i ) . apply_visitor ( [ this , i , decl , & new_entity ] ( const auto & v ) {
2026-01-04 10:40:02 +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 , express : : Base > ) {
} else if constexpr ( std : : is_same_v < U , std : : vector < express : : Base > > ) {
} else if constexpr ( std : : is_same_v < U , std : : vector < std : : vector < express : : Base > > > ) {
2025-03-09 21:20:16 +01:00
} else {
2026-01-04 10:40:02 +01:00
new_entity . set_attribute_value ( i , v ) ;
2025-03-09 21:20:16 +01:00
}
2026-01-08 10:35:46 +01:00
} ) ;
2026-01-04 10:40:02 +01:00
}
2025-02-27 22:07:31 +01:00
2026-01-04 10:40:02 +01:00
// In case an entity is added that contains geometry, the unit
// information needs to be accounted for for IfcLengthMeasures.
double conversion_factor = calculate_unit_factors ? std : : numeric_limits < double > : : quiet_NaN ( ) : 1.0 ;
for ( size_t i = 0 ; i < num_attributes ; + + i ) {
// old attribute value
auto attr = entity . get_attribute_value ( i ) ;
2026-03-31 15:32:36 +02:00
ifcopenshell : : argument_type attr_type = attr . type ( ) ;
2026-01-04 10:40:02 +01:00
2026-03-31 15:32:36 +02:00
ifcopenshell : : declaration * potentially_length_measure_decl = 0 ;
2026-01-04 10:40:02 +01:00
if ( decl - > as_entity ( ) ! = nullptr ) {
potentially_length_measure_decl = 0 ;
const parameter_type * pt = decl - > as_entity ( ) - > attribute_by_index ( i ) - > type_of_attribute ( ) ;
while ( pt - > as_aggregation_type ( ) ! = nullptr ) {
pt = pt - > as_aggregation_type ( ) - > type_of_element ( ) ;
}
if ( pt - > as_named_type ( ) ! = nullptr ) {
potentially_length_measure_decl = pt - > as_named_type ( ) - > declared_type ( ) ;
}
}
2026-03-31 15:32:36 +02:00
if ( attr_type = = ifcopenshell : : Argument_ENTITY_INSTANCE ) {
2026-01-04 10:40:02 +01:00
entity_entity_map_t : : const_iterator eit = entity_file_map_ . find ( ( ( express : : Base ) ( attr ) ) . identity ( ) ) ;
if ( eit = = entity_file_map_ . end ( ) ) {
2026-03-31 15:32:36 +02:00
throw ifcopenshell : : exception ( " Unable to map instance to file " ) ;
2026-01-04 10:40:02 +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 ) ;
2026-03-31 15:32:36 +02:00
} else if ( attr_type = = ifcopenshell : : Argument_AGGREGATE_OF_ENTITY_INSTANCE ) {
2026-01-04 10:40:02 +01:00
std : : vector < express : : Base > instances = attr ;
std : : vector < express : : Base > new_instances ;
for ( auto & i : instances ) {
entity_entity_map_t : : const_iterator eit = entity_file_map_ . find ( i . identity ( ) ) ;
2023-11-06 20:38:19 +01:00
if ( eit = = entity_file_map_ . end ( ) ) {
2026-03-31 15:32:36 +02:00
throw ifcopenshell : : exception ( " Unable to map instance to file " ) ;
2023-09-17 12:30:17 +02:00
}
2026-01-04 10:40:02 +01:00
new_instances . push_back ( eit - > second ) ;
}
new_entity . set_attribute_value ( i , new_instances ) ;
2026-03-31 15:32:36 +02:00
} else if ( attr_type = = ifcopenshell : : Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE ) {
2026-01-04 10:40:02 +01:00
std : : vector < std : : vector < express : : Base > > instances = attr ;
std : : vector < std : : vector < express : : Base > > new_instances ;
for ( auto & v : instances ) {
new_instances . emplace_back ( ) ;
for ( auto & i : v ) {
entity_entity_map_t : : const_iterator eit = entity_file_map_ . find ( i . identity ( ) ) ;
2023-11-06 20:38:19 +01:00
if ( eit = = entity_file_map_ . end ( ) ) {
2026-03-31 15:32:36 +02:00
throw ifcopenshell : : exception ( " Unable to map instance to file " ) ;
2023-09-17 12:30:17 +02:00
}
2026-01-04 10:40:02 +01:00
new_instances . back ( ) . push_back ( eit - > second ) ;
2023-09-17 12:30:17 +02:00
}
2026-01-04 10:40:02 +01:00
}
2024-08-23 20:29:07 +02:00
2026-01-04 10:40:02 +01:00
new_entity . set_attribute_value ( i , new_instances ) ;
} else if ( ( potentially_length_measure_decl ! = nullptr ) & & potentially_length_measure_decl - > is ( * schema ( ) - > declaration_by_name ( " IfcLengthMeasure " ) ) ) {
if ( boost : : math : : isnan ( conversion_factor ) ) {
std : : pair < express : : Base , double > this_file_unit = { express : : Base { } , 1.0 } ;
std : : pair < express : : Base , double > other_file_unit = { express : : Base { } , 1.0 } ;
try {
2026-03-31 15:32:36 +02:00
this_file_unit = get_unit ( " LENGTHUNIT " ) ;
other_file_unit = other_file - > get_unit ( " LENGTHUNIT " ) ;
} catch ( ifcopenshell : : exception & ) {
2023-09-17 12:30:17 +02:00
}
2026-01-04 10:40:02 +01:00
if ( this_file_unit . first & & other_file_unit . first ) {
conversion_factor = other_file_unit . second / this_file_unit . second ;
} else {
conversion_factor = 1. ;
}
}
2026-03-31 15:32:36 +02:00
if ( attr_type = = ifcopenshell : : Argument_DOUBLE ) {
2026-01-04 10:40:02 +01:00
double v = attr ;
v * = conversion_factor ;
new_entity . set_attribute_value ( i , v ) ;
2026-03-31 15:32:36 +02:00
} else if ( attr_type = = ifcopenshell : : Argument_AGGREGATE_OF_DOUBLE ) {
2026-01-04 10:40:02 +01:00
std : : vector < double > v = attr ;
for ( std : : vector < double > : : iterator it = v . begin ( ) ; it ! = v . end ( ) ; + + it ) {
( * it ) * = conversion_factor ;
}
new_entity . set_attribute_value ( i , v ) ;
2026-03-31 15:32:36 +02:00
} else if ( attr_type = = ifcopenshell : : Argument_AGGREGATE_OF_AGGREGATE_OF_DOUBLE ) {
2026-01-04 10:40:02 +01:00
std : : vector < std : : vector < double > > v = attr ;
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 ;
2023-09-17 12:30:17 +02:00
}
}
2026-01-04 10:40:02 +01:00
new_entity . set_attribute_value ( i , v ) ;
2023-09-17 12:30:17 +02:00
}
}
}
2017-08-16 21:46:39 +02:00
2026-01-04 10:40:02 +01:00
entity_file_map_ . insert ( entity_entity_map_t : : value_type ( entity . identity ( ) , new_entity ) ) ;
2023-09-17 12:30:17 +02:00
// For subtypes of IfcRoot, the GUID mapping needs to be updated.
2026-01-07 13:51:48 +01:00
/*
2026-01-04 10:40:02 +01:00
if (decl->is(*ifcroot_type_)) {
2023-09-17 12:30:17 +02:00
try {
2026-01-04 10:40:02 +01:00
const std::string guid = new_entity.get_attribute_value(0);
2023-11-06 20:38:19 +01:00
if (byguid_.find(guid) != byguid_.end()) {
2023-09-17 12:30:17 +02:00
std::stringstream ss;
ss << "Overwriting entity with guid " << guid;
2026-07-09 13:30:48 +02:00
logger_.get().message(::logger::LOG_WARNING, ss.str());
2023-09-17 12:30:17 +02:00
}
2025-02-21 16:12:16 +01:00
byguid_.insert({ guid, new_entity });
2024-08-23 20:29:07 +02:00
} catch (const std::exception& ex) {
2026-07-09 13:30:48 +02:00
logger_.get().message(::logger::LOG_ERROR, ex.what());
2023-09-17 12:30:17 +02:00
}
}
2026-01-07 13:51:48 +01:00
*/
2017-08-16 21:46:39 +02:00
2026-01-07 13:51:48 +01:00
// add_type_ref(new_entity);
2023-09-17 12:30:17 +02:00
return new_entity ;
2015-02-17 19:48:41 +00:00
}
2026-03-31 15:32:36 +02:00
void file : : remove_entity ( const express : : Base & entity ) {
2026-01-04 10:40:02 +01:00
auto id = entity . id ( ) ;
2023-09-17 12:30:17 +02:00
2026-01-04 10:40:02 +01:00
auto file_entity = instance_by_id ( id ) ;
2023-09-17 12:30:17 +02:00
2026-03-31 15:32:36 +02:00
// Attention when running remove_entity inside a loop over a list of entities to be removed.
2023-09-17 12:30:17 +02:00
// 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) {
2026-01-04 10:40:02 +01:00
// express::Base *const inst = *it;
2026-03-31 15:32:36 +02:00
// model->remove_entity(inst);
2023-09-17 12:30:17 +02:00
// }
// 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 ) {
2026-03-31 15:32:36 +02:00
throw ifcopenshell : : exception ( " Instance not part of this file " ) ;
2023-09-17 12:30:17 +02:00
}
2015-02-17 19:48:41 +00:00
2025-08-31 15:52:46 +02:00
if ( batch_mode_ ) {
batch_deletion_ids_ . push_back ( id ) ;
} else {
process_deletion_ ( entity ) ;
2026-01-13 08:45:04 +01:00
byid_ . erase ( entity . id ( ) ) ;
2025-08-31 15:52:46 +02:00
}
}
2026-03-31 15:32:36 +02:00
void file : : process_deletion_ ( const express : : Base & entity ) {
2025-08-31 15:52:46 +02:00
2026-01-04 10:40:02 +01:00
auto references = instances_by_reference ( entity . id ( ) ) ;
2015-02-17 19:48:41 +00:00
2025-02-21 16:12:16 +01:00
// 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.
2026-01-04 10:40:02 +01:00
if ( ! references . empty ( ) ) {
for ( auto & related_instance : references ) {
if ( std : : find ( batch_deletion_ids_ . begin ( ) , batch_deletion_ids_ . end ( ) , related_instance . id ( ) ) ! = batch_deletion_ids_ . end ( ) ) {
2025-02-21 16:12:16 +01:00
continue ;
}
2023-09-17 12:30:17 +02:00
2026-01-04 10:40:02 +01:00
const auto & decl = related_instance . declaration ( ) ;
2025-02-27 22:07:31 +01:00
for ( size_t i = 0 ; i < ( decl . as_entity ( ) ? decl . as_entity ( ) - > attribute_count ( ) : 1 ) ; + + i ) {
2026-01-04 10:40:02 +01:00
auto attr = related_instance . get_attribute_value ( i ) ;
2025-02-21 16:12:16 +01:00
if ( attr . isNull ( ) ) {
2023-09-17 12:30:17 +02:00
continue ;
}
2026-03-31 15:32:36 +02:00
ifcopenshell : : argument_type attr_type = attr . type ( ) ;
2025-02-21 16:12:16 +01:00
switch ( attr_type ) {
2026-03-31 15:32:36 +02:00
case ifcopenshell : : Argument_ENTITY_INSTANCE : {
2026-01-04 10:40:02 +01:00
express : : Base instance_attribute = attr ;
2025-02-21 16:12:16 +01:00
if ( instance_attribute = = entity ) {
2026-03-31 15:32:36 +02:00
related_instance . set_attribute_value ( i , blank { } ) ;
2023-09-17 12:30:17 +02:00
}
2025-02-21 16:12:16 +01:00
} break ;
2026-03-31 15:32:36 +02:00
case ifcopenshell : : Argument_AGGREGATE_OF_ENTITY_INSTANCE : {
2026-01-04 10:40:02 +01:00
std : : vector < express : : Base > instance_list = attr ;
auto it = std : : remove ( instance_list . begin ( ) , instance_list . end ( ) , entity ) ;
if ( it ! = instance_list . end ( ) ) {
instance_list . erase ( it , instance_list . end ( ) ) ;
if ( instance_list . empty ( ) & & related_instance . declaration ( ) . as_entity ( ) - > attribute_by_index ( i ) - > optional ( ) ) {
2025-02-21 16:12:16 +01:00
// @todo we can also check the lower bound of the attribute type before setting to null.
2026-03-31 15:32:36 +02:00
related_instance . set_attribute_value ( i , blank { } ) ;
2025-02-21 16:12:16 +01:00
} else {
2026-01-04 10:40:02 +01:00
related_instance . set_attribute_value ( i , instance_list ) ;
2023-09-17 12:30:17 +02:00
}
2025-02-21 16:12:16 +01:00
}
} break ;
2026-03-31 15:32:36 +02:00
case ifcopenshell : : Argument_AGGREGATE_OF_AGGREGATE_OF_ENTITY_INSTANCE : {
2026-01-04 10:40:02 +01:00
std : : vector < std : : vector < express : : Base > > instance_list_list = attr ;
bool updated = false ;
for ( auto & li : instance_list_list ) {
auto it = std : : remove ( li . begin ( ) , li . end ( ) , entity ) ;
if ( it ! = li . end ( ) ) {
li . erase ( it , li . end ( ) ) ;
updated = true ;
2023-09-17 12:30:17 +02:00
}
}
2026-01-04 10:40:02 +01:00
related_instance . set_attribute_value ( i , instance_list_list ) ;
2025-02-21 16:12:16 +01:00
} break ;
default :
break ;
2023-09-17 12:30:17 +02:00
}
}
}
2025-02-21 16:12:16 +01:00
}
2021-03-13 12:16:40 +01:00
2026-01-04 10:40:02 +01:00
if ( entity . declaration ( ) . is ( * ifcroot_type_ ) & & ! entity . get_attribute_value ( 0 ) . isNull ( ) ) {
const std : : string global_id = entity . get_attribute_value ( 0 ) ;
2025-02-21 16:12:16 +01:00
auto it = byguid_ . find ( global_id ) ;
if ( it ! = byguid_ . end ( ) ) {
byguid_ . erase ( it ) ;
} else {
2026-07-09 13:30:48 +02:00
logger_ . get ( ) . warning ( " GlobalId on rooted instance not encountered in map " ) ;
2025-02-21 16:12:16 +01:00
}
}
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
remove_type_ref ( entity ) ;
2025-02-21 16:12:16 +01:00
// entity_file_map is in place to prevent duplicate definitions with usage of add().
// Upon deletion the pairs need to be erased.
2026-01-04 10:40:02 +01:00
// @todo this is not strictly necessary anymore and can be amortized to e.g 1/100 times
// to delete the expired weak_ptrs.
2025-02-21 16:12:16 +01:00
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
}
2025-02-21 16:12:16 +01:00
}
}
2023-09-17 12:30:17 +02:00
2026-03-31 15:32:36 +02:00
void ifcopenshell : : impl : : in_memory_file_storage : : process_deletion_inverse ( const express : : Base & entity ) {
2026-01-04 10:40:02 +01:00
auto id = entity . id ( ) ;
2023-09-17 12:30:17 +02:00
2025-02-21 16:12:16 +01:00
// Delete inverses into entity
2026-04-10 14:54:47 +02:00
byref_excl_ . erase ( id ) ;
2026-06-11 15:51:40 +02:00
byref_excl_ . remove_source ( id ) ;
2012-07-18 19:32:58 +00:00
}
2015-02-17 19:48:41 +00:00
2024-08-23 20:29:07 +02:00
namespace {
template < typename Fn >
2026-03-31 15:32:36 +02:00
void visit_subtypes ( const ifcopenshell : : entity * ent , Fn fn ) {
2024-08-23 20:29:07 +02:00
fn ( ent ) ;
for ( const auto & st : ent - > subtypes ( ) ) {
visit_subtypes ( st , fn ) ;
}
}
template < typename Fn >
2026-03-31 15:32:36 +02:00
void visit_supertypes ( const ifcopenshell : : entity * ent , Fn fn ) {
2024-08-23 20:29:07 +02:00
fn ( ent ) ;
if ( ent - > supertype ( ) ) {
visit_supertypes ( ent - > supertype ( ) , fn ) ;
}
}
}
2026-03-31 15:32:36 +02:00
std : : vector < express : : Base > file : : instances_by_type ( const ifcopenshell : : declaration * t ) {
2026-01-04 10:40:02 +01:00
std : : vector < express : : Base > insts ;
2024-08-23 20:29:07 +02:00
if ( t - > as_entity ( ) ! = nullptr ) {
2026-03-31 15:32:36 +02:00
visit_subtypes ( t - > as_entity ( ) , [ this , & insts ] ( const ifcopenshell : : entity * ent ) {
2025-02-21 16:12:16 +01:00
auto subtype_insts = instances_by_type_excl_subtypes ( ent ) ;
2026-01-04 10:40:02 +01:00
insts . insert ( insts . end ( ) , subtype_insts . begin ( ) , subtype_insts . end ( ) ) ;
2024-08-23 20:29:07 +02:00
} ) ;
}
return insts ;
2011-05-08 11:04:32 +00:00
}
2015-02-17 19:48:41 +00:00
2026-03-31 15:32:36 +02:00
std : : vector < express : : Base > file : : instances_by_type_excl_subtypes ( const ifcopenshell : : declaration * t ) {
2025-02-21 16:12:16 +01:00
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 ) ;
2026-01-04 10:40:02 +01:00
return ( it = = x . bytype_excl_ . end ( ) ) ? std : : vector < express : : Base > { } : it - > second ;
2025-02-21 16:12:16 +01:00
} else if constexpr ( std : : is_same_v < std : : decay_t < decltype ( x ) > , impl : : rocks_db_file_storage > ) {
2026-01-04 10:40:02 +01:00
std : : vector < express : : Base > ret ;
2025-02-21 16:12:16 +01:00
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 ) {
2026-03-31 15:32:36 +02:00
ret . push_back ( x . assert_existance ( v , ifcopenshell : : impl : : rocks_db_file_storage : : entityinstance_ref ) ) ;
2025-02-27 22:07:31 +01:00
}
2025-02-21 16:12:16 +01:00
}
return ret ;
} else {
throw std : : runtime_error ( " Storage not initialized " ) ;
2026-01-04 10:40:02 +01:00
std : : vector < express : : Base > ret ;
2025-02-21 16:12:16 +01:00
return ret ;
}
} , storage_ ) ;
2017-08-16 21:46:39 +02:00
}
2026-03-31 15:32:36 +02:00
std : : vector < express : : Base > file : : 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
}
2015-02-17 19:48:41 +00:00
2026-03-31 15:32:36 +02:00
std : : vector < express : : Base > file : : 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
}
2026-03-31 15:32:36 +02:00
std : : vector < express : : Base > file : : instances_by_reference ( int t ) {
2026-01-04 10:40:02 +01:00
std : : vector < express : : Base > ret ;
2025-02-21 16:12:16 +01:00
std : : visit ( [ this , t , & ret ] ( auto & x ) {
if constexpr ( std : : is_same_v < std : : decay_t < decltype ( x ) > , impl : : in_memory_file_storage > ) {
2026-06-11 15:51:40 +02:00
auto range = x . byref_excl_ . equal_range ( ( uint32_t ) t ) ;
ret . reserve ( ret . size ( ) + ( size_t ) std : : distance ( range . first , range . second ) ) ;
for ( auto it = range . first ; it ! = range . second ; + + it ) {
ret . push_back ( instance_by_id ( it - > source_id ) ) ;
2025-02-21 16:12:16 +01:00
}
2025-08-26 13:56:03 +02:00
}
2025-09-03 11:11:55 +02:00
# ifdef IFOPSH_WITH_ROCKSDB
2025-08-26 13:56:03 +02:00
else if constexpr ( std : : is_same_v < std : : decay_t < decltype ( x ) > , impl : : rocks_db_file_storage > ) {
2025-03-14 16:16:25 +01:00
// @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 ( ) ) ) ;
2025-03-14 16:16:25 +01: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 ( ) ) ;
for ( auto & v : vals ) {
2026-01-04 10:40:02 +01:00
ret . push_back ( instance_by_id ( v ) ) ;
2025-03-14 16:16:25 +01:00
}
it - > Next ( ) ;
}
2025-08-26 13:56:03 +02:00
}
# endif
else {
2025-02-21 16:12:16 +01:00
throw std : : runtime_error ( " Storage not initialized " ) ;
2024-08-23 20:29:07 +02:00
}
2025-02-21 16:12:16 +01:00
} , storage_ ) ;
2023-09-17 12:30:17 +02:00
return ret ;
2011-05-08 11:04:32 +00:00
}
2015-02-17 19:48:41 +00:00
2026-03-31 15:32:36 +02:00
express : : Base file : : instance_by_id ( int id ) {
2025-02-24 20:46:29 +01:00
return std : : visit ( [ id ] ( auto & x ) {
2025-02-21 16:12:16 +01:00
if constexpr ( std : : is_same_v < std : : decay_t < decltype ( x ) > , std : : monostate > ) {
throw std : : runtime_error ( " Storage not initialized " ) ;
2026-01-04 10:40:02 +01:00
return express : : Base { } ;
2025-02-21 16:12:16 +01:00
} else {
return x . instance_by_id ( id ) ;
}
} , storage_ ) ;
2011-05-08 11:04:32 +00:00
}
2015-02-17 19:48:41 +00:00
2026-03-31 15:32:36 +02:00
void ifcopenshell : : file : : add_type_ref ( const express : : Base & new_entity )
2025-02-21 16:12:16 +01:00
{
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
}
2011-07-25 14:56:40 +00:00
2025-02-21 16:12:16 +01:00
2026-03-31 15:32:36 +02:00
void ifcopenshell : : file : : remove_type_ref ( const express : : Base & new_entity ) {
2025-02-21 16:12:16 +01:00
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_ ) ;
2012-08-11 13:24:08 +00:00
}
2026-03-31 15:32:36 +02:00
void ifcopenshell : : file : : process_deletion_inverse ( const express : : Base & inst ) {
2025-02-21 16:12:16 +01:00
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_ ) ;
2012-03-16 15:21:52 +00:00
}
2015-02-17 19:48:41 +00:00
2026-03-31 15:32:36 +02:00
express : : Base file : : instance_by_guid ( const std : : string & guid ) {
2025-02-21 16:12:16 +01:00
auto it = byguid_ . find ( guid ) ;
if ( it = = byguid_ . end ( ) ) {
2026-03-31 15:32:36 +02:00
throw exception ( " Instance with GlobalId ' " + guid + " ' not found " ) ;
2025-02-21 16:12:16 +01:00
}
return it - > second ;
2012-03-16 15:21:52 +00:00
}
2012-08-11 13:24:08 +00:00
2026-03-31 15:32:36 +02:00
file : : type_iterator file : : types_begin ( ) const {
2025-02-21 16:12:16 +01:00
return std : : visit ( [ ] ( const auto & x ) {
if constexpr ( std : : is_same_v < std : : decay_t < decltype ( x ) > , std : : monostate > ) {
2026-03-31 15:32:36 +02:00
return file : : type_iterator { impl : : rocks_db_file_storage : : rocksdb_types_iterator { } } ;
2025-02-21 16:12:16 +01:00
} else if constexpr ( std : : is_same_v < std : : decay_t < decltype ( x ) > , impl : : in_memory_file_storage > ) {
2026-03-31 15:32:36 +02:00
return file : : type_iterator { x . bytype_excl_ . begin ( ) } ;
2025-02-21 16:12:16 +01:00
} else if constexpr ( std : : is_same_v < std : : decay_t < decltype ( x ) > , impl : : rocks_db_file_storage > ) {
2026-03-31 15:32:36 +02:00
return file : : type_iterator { impl : : rocks_db_file_storage : : rocksdb_types_iterator ( & x ) } ;
2025-02-21 16:12:16 +01:00
}
} , storage_ ) ;
2017-08-16 21:46:39 +02:00
}
2026-03-31 15:32:36 +02:00
file : : type_iterator file : : types_end ( ) const {
2025-02-21 16:12:16 +01:00
return std : : visit ( [ ] ( const auto & x ) {
if constexpr ( std : : is_same_v < std : : decay_t < decltype ( x ) > , std : : monostate > ) {
2026-03-31 15:32:36 +02:00
return file : : type_iterator { impl : : rocks_db_file_storage : : rocksdb_types_iterator { } } ;
2025-02-21 16:12:16 +01:00
} else if constexpr ( std : : is_same_v < std : : decay_t < decltype ( x ) > , impl : : in_memory_file_storage > ) {
2026-03-31 15:32:36 +02:00
return file : : type_iterator { x . bytype_excl_ . end ( ) } ;
2025-02-21 16:12:16 +01:00
} else if constexpr ( std : : is_same_v < std : : decay_t < decltype ( x ) > , impl : : rocks_db_file_storage > ) {
2026-03-31 15:32:36 +02:00
return file : : type_iterator { impl : : rocks_db_file_storage : : rocksdb_types_iterator { } } ;
2025-02-21 16:12:16 +01:00
}
} , storage_ ) ;
2017-08-16 21:46:39 +02:00
}
2026-03-31 15:32:36 +02:00
std : : ostream & operator < < ( std : : ostream & out , const ifcopenshell : : file & file ) {
2023-11-06 20:29:00 +01:00
file . header ( ) . write ( out ) ;
2012-07-18 19:32:58 +00:00
2026-01-04 10:40:02 +01:00
typedef std : : vector < express : : Base > vector_t ;
2025-02-21 16:12:16 +01:00
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 ; } ) ;
2026-01-04 10:40:02 +01:00
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
2025-02-21 16:12:16 +01:00
for ( auto & e : sorted ) {
// @todo this check should no longer be necessary?
2026-01-04 10:40:02 +01:00
if ( e . declaration ( ) . as_entity ( ) ! = nullptr ) {
2026-03-31 15:32:36 +02:00
e . to_string ( out , true ) ;
2024-08-23 20:29:07 +02:00
out < < " ; " < < std : : endl ;
2023-09-17 12:30:17 +02:00
}
}
2012-07-18 19:32:58 +00:00
2023-11-06 20:29:00 +01:00
out < < " ENDSEC; " < < std : : endl ;
out < < " END-ISO-10303-21; " < < std : : endl ;
2012-08-11 13:24:08 +00:00
2023-11-06 20:29:00 +01:00
return out ;
2012-08-11 13:24:08 +00:00
}
2012-11-17 15:19:38 +00:00
2026-03-31 15:32:36 +02:00
std : : string file : : create_timestamp ( ) {
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 ;
2023-10-24 14:16:26 +02:00
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
}
2026-03-31 15:32:36 +02:00
const ifcopenshell : : schema_definition * file : : schema ( ) const {
2025-09-19 13:23:04 +02:00
if ( schema_ = = nullptr ) {
2026-03-31 15:32:36 +02:00
throw exception ( " No schema loaded " ) ;
2025-09-19 13:23:04 +02:00
}
return schema_ ;
}
2026-05-06 11:41:43 +02:00
std : : vector < int > file : : get_inverse_indices_by_id ( int instance_id ) {
2023-09-17 12:30:17 +02:00
std : : vector < int > return_value ;
2022-10-20 13:35:02 +02:00
2023-09-17 12:30:17 +02:00
// Mapping of instance id to attribute offset.
std : : map < int , std : : vector < int > > mapping ;
2026-06-11 15:51:40 +02:00
bool handled = false ;
2023-09-17 12:30:17 +02:00
2026-06-11 15:51:40 +02:00
std : : visit ( [ & mapping , & return_value , & handled , instance_id ] ( auto & x ) {
2025-02-21 16:12:16 +01:00
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 > ) {
2026-06-11 15:51:40 +02:00
handled = true ;
auto range = x . byref_excl_ . equal_range ( ( uint32_t ) instance_id ) ;
return_value . reserve ( ( size_t ) std : : distance ( range . first , range . second ) ) ;
for ( auto it = range . first ; it ! = range . second ; + + it ) {
return_value . push_back ( it - > attribute_index ) ;
2023-09-17 12:30:17 +02:00
}
2025-02-21 16:12:16 +01: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
}
2025-02-21 16:12:16 +01:00
} , storage_ ) ;
2023-09-17 12:30:17 +02:00
2026-06-11 15:51:40 +02:00
if ( handled ) {
return return_value ;
}
2023-09-17 12:30:17 +02:00
auto refs = instances_by_reference ( instance_id ) ;
2026-01-04 10:40:02 +01:00
for ( const auto & ref : refs ) {
auto it = mapping . find ( ref . id ( ) ) ;
2023-09-17 12:30:17 +02:00
if ( it = = mapping . end ( ) | | it - > second . empty ( ) ) {
2026-03-31 15:32:36 +02:00
throw exception ( " Internal error " ) ;
2023-09-17 12:30:17 +02:00
}
return_value . push_back ( it - > second . front ( ) ) ;
it - > second . erase ( it - > second . begin ( ) ) ;
if ( it - > second . empty ( ) ) {
mapping . erase ( it ) ;
}
}
2022-10-20 13:35:02 +02:00
2023-09-17 12:30:17 +02:00
// Test whether all mappings where indeed used.
if ( ! mapping . empty ( ) ) {
2026-03-31 15:32:36 +02:00
throw exception ( " Internal error " ) ;
2023-09-17 12:30:17 +02:00
}
return return_value ;
2022-10-20 13:35:02 +02:00
}
2026-03-31 15:32:36 +02:00
std : : vector < express : : Entity > file : : get_inverse ( int instance_id , const ifcopenshell : : declaration * type , int attribute_index ) {
2026-01-04 10:40:02 +01:00
std : : vector < express : : Entity > return_value ;
2023-09-17 12:30:17 +02:00
if ( type = = nullptr & & attribute_index = = - 1 ) {
2026-01-04 10:40:02 +01:00
// @todo this is silly.
auto r = instances_by_reference ( instance_id ) ;
for ( auto & i : r ) {
return_value . push_back ( i . as < express : : Entity > ( ) ) ;
}
return return_value ;
2023-09-17 12:30:17 +02:00
}
2026-01-04 10:40:02 +01:00
2026-06-11 15:51:40 +02:00
std : : visit ( [ & return_value , this , attribute_index , instance_id , type ] ( auto & x ) {
2026-04-10 14:54:47 +02:00
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 > ) {
2026-06-11 15:51:40 +02:00
std : : vector < uint8_t > source_types ( schema ( ) - > declarations ( ) . size ( ) , 0 ) ;
visit_subtypes ( type - > as_entity ( ) , [ & source_types ] ( const ifcopenshell : : declaration * ent ) {
source_types [ ent - > index_in_schema ( ) ] = 1 ;
} ) ;
auto range = x . byref_excl_ . equal_range ( ( uint32_t ) instance_id ) ;
for ( auto it = range . first ; it ! = range . second ; + + it ) {
if ( it - > source_entity < source_types . size ( ) & & source_types [ it - > source_entity ] & &
( attribute_index = = - 1 | | it - > attribute_index = = attribute_index ) ) {
return_value . push_back ( instance_by_id ( it - > source_id ) . template as < express : : Entity > ( ) ) ;
}
2025-08-26 13:56:03 +02:00
}
2026-04-10 14:54:47 +02:00
}
# ifdef IFOPSH_WITH_ROCKSDB
else if constexpr ( std : : is_same_v < std : : decay_t < decltype ( x ) > , impl : : rocks_db_file_storage > ) {
visit_subtypes ( type - > as_entity ( ) , [ this , attribute_index , instance_id , & return_value , & x ] ( const ifcopenshell : : declaration * ent ) {
2025-03-09 21:20:16 +01:00
if ( attribute_index = = - 1 ) {
// @todo no lower/upper_bounds() implemented yet
2025-03-13 12:34:54 +01:00
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 ( ) ) ) ;
2025-03-09 21:20:16 +01:00
it - > Seek ( prefix ) ;
while ( it - > Valid ( ) & & it - > key ( ) . starts_with ( prefix ) ) {
2025-03-13 12:34:54 +01:00
std : : vector < uint32_t > vals ( it - > value ( ) . size ( ) / sizeof ( uint32_t ) ) ;
2025-03-09 21:20:16 +01:00
memcpy ( vals . data ( ) , it - > value ( ) . data ( ) , it - > value ( ) . size ( ) ) ;
for ( auto & v : vals ) {
2026-01-07 20:36:07 +01:00
return_value . push_back ( instance_by_id ( v ) . template as < express : : Entity > ( ) ) ;
2025-03-09 21:20:16 +01:00
}
2025-03-13 12:34:54 +01:00
it - > Next ( ) ;
2025-03-09 21:20:16 +01:00
}
} else {
2026-04-10 14:54:47 +02:00
auto it = x . byref_excl_ . find ( { instance_id , ent - > index_in_schema ( ) , attribute_index } ) ;
2025-03-09 21:20:16 +01:00
if ( it ! = x . byref_excl_ . end ( ) ) {
for ( auto & i : it - > second ) {
2026-01-07 20:36:07 +01:00
return_value . push_back ( instance_by_id ( i ) . template as < express : : Entity > ( ) ) ;
2025-03-09 21:20:16 +01:00
}
}
}
2026-04-10 14:54:47 +02:00
} ) ;
}
2025-08-26 13:56:03 +02:00
# endif
2026-04-10 14:54:47 +02:00
} , storage_ ) ;
2021-11-30 13:25:02 +11:00
2023-09-17 12:30:17 +02:00
return return_value ;
2021-11-30 13:25:02 +11:00
}
2026-03-31 15:32:36 +02:00
size_t file : : get_total_inverses ( int instance_id ) {
2026-01-14 14:00:50 +01:00
std : : set < uint32_t > counted_ids ;
2025-02-21 16:12:16 +01:00
2026-06-11 15:51:40 +02:00
std : : visit ( [ & counted_ids , instance_id ] ( auto & x ) {
2025-02-21 16:12:16 +01:00
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 > ) {
2026-06-11 15:51:40 +02:00
auto range = x . byref_excl_ . equal_range ( ( uint32_t ) instance_id ) ;
for ( auto it = range . first ; it ! = range . second ; + + it ) {
counted_ids . insert ( it - > source_id ) ;
2025-02-21 16:12:16 +01:00
}
} else if constexpr ( std : : is_same_v < std : : decay_t < decltype ( x ) > , impl : : rocks_db_file_storage > ) {
// @todo
}
} , storage_ ) ;
2026-01-14 14:00:50 +01:00
return counted_ids . size ( ) ;
2023-09-17 12:30:17 +02:00
}
2021-11-30 13:25:02 +11:00
2026-03-31 15:32:36 +02:00
void file : : set_default_header_values ( ) {
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 ;
2024-09-02 14:38:23 +05:00
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] " ) ;
2023-10-24 14:16:26 +02:00
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
2026-01-04 10:40:02 +01:00
header ( ) . file_description ( ) . setdescription ( file_description ) ;
header ( ) . file_description ( ) . setimplementation_level ( " 2;1 " ) ;
2015-01-16 16:26:40 +00:00
2026-01-04 10:40:02 +01:00
header ( ) . file_name ( ) . setname ( empty_string ) ;
2026-03-31 15:32:36 +02:00
header ( ) . file_name ( ) . settime_stamp ( create_timestamp ( ) ) ;
2026-01-04 10:40:02 +01:00
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
2026-01-04 10:40:02 +01:00
header ( ) . file_schema ( ) . setschema_identifiers ( schema_identifiers ) ;
2015-01-16 16:26:40 +00:00
}
2026-03-31 15:32:36 +02:00
std : : pair < express : : Base , double > file : : get_unit ( const std : : string & unit_type ) {
2026-01-04 10:40:02 +01:00
std : : pair < express : : Base , double > return_value ( express : : Base { } , 1. ) ;
2021-09-24 09:03:58 +02:00
2026-01-04 10:40:02 +01:00
auto projects = instances_by_type ( schema ( ) - > declaration_by_name ( " IfcProject " ) ) ;
if ( projects . empty ( ) ) {
2023-09-17 12:30:17 +02:00
try {
projects = instances_by_type ( schema ( ) - > declaration_by_name ( " IfcContext " ) ) ;
2026-03-31 15:32:36 +02:00
} catch ( exception & ) {
2023-09-17 12:30:17 +02:00
}
}
2017-12-13 18:05:10 +01:00
2026-01-04 10:40:02 +01:00
if ( ! projects . empty ( ) ) {
auto project = * projects . begin ( ) ;
2023-09-17 12:30:17 +02:00
2026-01-04 10:40:02 +01:00
express : : Base unit_assignment = project . as < express : : Entity > ( ) . get ( " UnitsInContext " ) ;
2023-09-17 12:30:17 +02:00
2026-01-04 10:40:02 +01:00
std : : vector < express : : Base > units = unit_assignment . as < express : : Entity > ( ) . get ( " Units " ) ;
2023-09-17 12:30:17 +02:00
2026-01-04 10:40:02 +01:00
for ( auto & unit : units ) {
if ( unit . declaration ( ) . is ( " IfcNamedUnit " ) ) {
const std : : string file_unit_type = unit . as < express : : Entity > ( ) . get ( " UnitType " ) ;
2023-09-17 12:30:17 +02:00
if ( file_unit_type ! = unit_type ) {
continue ;
}
2026-01-04 10:40:02 +01:00
express : : Base siunit ;
if ( unit . declaration ( ) . is ( " IfcConversionBasedUnit " ) ) {
express : : Base mu = unit . as < express : : Entity > ( ) . get ( " ConversionFactor " ) ;
express : : Base vlc = mu . as < express : : Entity > ( ) . get ( " ValueComponent " ) ;
express : : Base unc = mu . as < express : : Entity > ( ) . get ( " UnitComponent " ) ;
return_value . second * = static_cast < double > ( vlc . get_attribute_value ( 0 ) ) ;
2023-09-17 12:30:17 +02:00
return_value . first = unit ;
2026-01-04 10:40:02 +01:00
if ( unc . declaration ( ) . is ( " IfcSIUnit " ) ) {
2023-09-17 12:30:17 +02:00
siunit = unc ;
}
2026-01-04 10:40:02 +01:00
} else if ( unit . declaration ( ) . is ( " IfcSIUnit " ) ) {
2023-09-17 12:30:17 +02:00
return_value . first = siunit = unit ;
}
2026-01-04 10:40:02 +01:00
if ( siunit ) {
2026-03-31 15:32:36 +02:00
attribute_value prefix = siunit . as < express : : Entity > ( ) . get ( " Prefix " ) ;
2024-08-23 20:29:07 +02:00
if ( ! prefix . isNull ( ) ) {
2026-03-31 15:32:36 +02:00
return_value . second * = si_prefix_to_value ( 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
2026-03-31 15:32:36 +02:00
void ifcopenshell : : file : : build_inverses_ ( const express : : Base & inst ) {
2026-01-04 10:40:02 +01:00
std : : function < void ( const express : : Base & , int ) > fn = [ this , inst ] ( const express : : Base & attr , int idx ) {
if ( attr . declaration ( ) . as_entity ( ) ! = nullptr ) {
unsigned entity_attribute_id = attr . id ( ) ;
const auto * decl = inst . declaration ( ) . as_entity ( ) ;
2025-02-21 16:12:16 +01:00
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 > ) {
2026-06-11 15:51:40 +02:00
x . byref_excl_ . add ( entity_attribute_id , inst . id ( ) , ( uint16_t ) decl - > index_in_schema ( ) , idx ) ;
2025-02-21 16:12:16 +01:00
} 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
}
2026-03-31 15:32:36 +02:00
void ifcopenshell : : file : : unbatch ( ) {
2025-08-31 15:52:46 +02:00
for ( auto & id : batch_deletion_ids_ ) {
process_deletion_ ( instance_by_id ( id ) ) ;
}
2026-01-13 08:45:04 +01:00
// keep in memory until all deletions are processed
for ( auto & id : batch_deletion_ids_ ) {
byid_ . erase ( id ) ;
}
2025-08-31 15:52:46 +02:00
batch_mode_ = false ;
batch_deletion_ids_ . clear ( ) ;
}
2026-03-31 15:32:36 +02:00
void ifcopenshell : : file : : reset_identity_cache ( ) {
2025-09-11 13:55:07 +02:00
std : : visit ( [ ] ( auto & x ) {
if constexpr ( std : : is_same_v < std : : decay_t < decltype ( x ) > , impl : : rocks_db_file_storage > ) {
2026-05-08 10:07:34 +02:00
std : : lock_guard < std : : mutex > lock ( x . instance_cache_mutex_ ) ;
2025-09-11 13:55:07 +02:00
x . instance_cache_ . clear ( ) ;
x . type_instance_cache_ . clear ( ) ;
}
} , storage_ ) ;
}
2026-03-31 15:32:36 +02:00
void ifcopenshell : : file : : build_inverses ( ) {
2023-10-24 16:11:26 +02:00
for ( const auto & pair : * this ) {
2026-01-04 10:40:02 +01:00
build_inverses_ ( express : : Base ( pair . second ) ) ;
2023-09-17 12:30:17 +02:00
}
2020-09-30 09:38:01 +02:00
}
2021-09-11 12:59:48 +02:00
2026-03-31 15:32:36 +02:00
void ifcopenshell : : file : : register_inverse ( unsigned id_from , const ifcopenshell : : entity * from_entity , int inst_id , int attribute_index )
2025-02-24 20:46:29 +01:00
{
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_ ) ;
}
2026-03-31 15:32:36 +02:00
void ifcopenshell : : file : : unregister_inverse ( unsigned id_from , const ifcopenshell : : entity * from_entity , const express : : Base & inst , int attribute_index )
2025-02-24 20:46:29 +01:00
{
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_ ) ;
}
2026-03-31 15:32:36 +02:00
std : : atomic_uint32_t instance_data : : counter_ ( 0 ) ;
2021-09-13 13:17:08 +02:00
2026-03-31 15:32:36 +02:00
// bool ifcopenshell::file::guid_map_ = true;
2024-05-12 16:57:34 +02:00
2026-01-04 10:40:02 +01:00
void express : : Base : : unset_attribute_value ( size_t index ) {
2026-03-31 15:32:36 +02:00
data ( ) - > set_attribute_value ( index , blank { } ) ;
2025-02-21 16:12:16 +01:00
}
2026-03-31 15:32:36 +02:00
attribute_value express : : Base : : get_attribute_value ( size_t index ) const {
2026-01-04 10:40:02 +01:00
return data ( ) - > get_attribute_value ( index ) ;
2024-08-23 20:29:07 +02:00
}
2026-03-31 15:32:36 +02:00
void express : : Base : : to_string ( std : : ostream & out , bool upper ) const
2024-08-23 20:29:07 +02:00
{
const auto * ent = declaration ( ) . as_entity ( ) ;
2025-08-28 11:15:18 +02:00
if ( ent ! = nullptr & & declaration ( ) . schema ( ) ! = & Header_section_schema : : get_schema ( ) ) {
2026-01-04 10:40:02 +01:00
out < < " # " < < id ( ) < < " = " ;
2024-08-23 20:29:07 +02:00
}
if ( upper ) {
out < < declaration ( ) . name_uc ( ) ;
} else {
out < < declaration ( ) . name ( ) ;
}
2026-03-31 15:32:36 +02:00
data ( ) - > to_string ( out , upper ) ;
2024-08-23 20:29:07 +02:00
}
2026-03-31 15:32:36 +02:00
ifcopenshell : : file * express : : Base : : file ( ) const {
2026-01-08 10:35:46 +01:00
return data ( ) - > file ( ) ;
}
2025-02-27 22:07:31 +01:00
/*
2026-03-31 15:32:36 +02:00
instance_data::instance_data(const instance_data& data)
2025-02-21 16:12:16 +01:00
: 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
2026-03-31 15:32:36 +02:00
attribute_value instance_data : : get_attribute_value ( size_t index ) const
2024-08-23 20:29:07 +02:00
{
2025-03-18 10:41:03 +01:00
if ( storage_ ) {
2026-03-31 15:32:36 +02:00
return attribute_value ( storage_ , ( uint8_t ) index ) ;
2025-03-18 10:41:03 +01:00
} else {
2026-03-31 15:32:36 +02:00
auto * const storage = std : : visit ( [ ] ( auto & m ) - > ifcopenshell : : impl : : rocks_db_file_storage * {
2026-01-04 10:40:02 +01:00
using U = std : : decay_t < decltype ( m ) > ;
2026-03-31 15:32:36 +02:00
if constexpr ( std : : is_same_v < U , ifcopenshell : : impl : : rocks_db_file_storage > ) {
2026-01-04 10:40:02 +01:00
return & m ;
} else {
return nullptr ;
}
} , file_ - > storage_ ) ;
2026-04-22 18:11:01 +02:00
return attribute_value ( storage , id_ ? id_ : identity_ , declaration_ , ( uint8_t ) index ) ;
2025-03-18 10:41:03 +01:00
}
2024-08-23 20:29:07 +02:00
}
2026-03-31 15:32:36 +02:00
bool ifcopenshell : : impl : : rocks_db_file_storage : : read_schema ( const ifcopenshell : : schema_definition * & schema ) {
2025-09-03 11:11:55 +02:00
# 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 ) {
2025-09-29 11:29:52 +02:00
try {
schema = schema_by_name ( strings [ 0 ] ) ;
2026-03-31 15:32:36 +02:00
} catch ( exception & ) {
2025-09-29 11:29:52 +02:00
return false ;
}
2025-02-27 22:07:31 +01:00
return true ;
}
2025-08-26 13:56:03 +02:00
# endif
2025-02-27 22:07:31 +01:00
return false ;
}
2026-01-04 10:40:02 +01:00
/*
2026-03-31 15:32:36 +02:00
express::Base::IfcBaseClass(instance_data&& data)
2025-03-09 21:20:16 +01:00
: 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_;
}
2026-01-04 10:40:02 +01:00
}
2025-03-09 21:20:16 +01:00
*/
2026-01-04 10:40:02 +01:00
void express : : Base : : set_attribute_value ( size_t i , const express : : Base & p ) {
set_attribute_value < express : : Base > ( i , p ) ;
}
void express : : Base : : set_attribute_value ( const std : : string & name , const express : : Base & p ) {
set_attribute_value < express : : Base > ( name , p ) ;
}
2026-03-31 15:32:36 +02:00
template void IFC_PARSE_API express : : Base : : set_attribute_value < blank > ( size_t index , const blank & value ) ;
template void IFC_PARSE_API express : : Base : : set_attribute_value < derived > ( size_t index , const derived & value ) ;
2026-07-19 11:44:31 +03:00
template void IFC_PARSE_API express : : Base : : set_attribute_value < int64_t > ( size_t index , const int64_t & value ) ;
2026-01-04 10:40:02 +01:00
template void IFC_PARSE_API express : : Base : : set_attribute_value < bool > ( size_t index , const bool & value ) ;
template void IFC_PARSE_API express : : Base : : set_attribute_value < boost : : logic : : tribool > ( size_t index , const boost : : logic : : tribool & value ) ;
template void IFC_PARSE_API express : : Base : : set_attribute_value < double > ( size_t index , const double & value ) ;
template void IFC_PARSE_API express : : Base : : set_attribute_value < std : : string > ( size_t index , const std : : string & value ) ;
template void IFC_PARSE_API express : : Base : : set_attribute_value < boost : : dynamic_bitset < > > ( size_t index , const boost : : dynamic_bitset < > & value ) ;
2026-03-31 15:32:36 +02:00
template void IFC_PARSE_API express : : Base : : set_attribute_value < enumeration_reference > ( size_t index , const enumeration_reference & value ) ;
2026-07-19 12:58:23 +03:00
template void IFC_PARSE_API express : : Base : : set_attribute_value < std : : vector < int64_t > > ( size_t index , const std : : vector < int64_t > & value ) ;
2026-01-04 10:40:02 +01:00
template void IFC_PARSE_API express : : Base : : set_attribute_value < std : : vector < double > > ( size_t index , const std : : vector < double > & value ) ;
template void IFC_PARSE_API express : : Base : : set_attribute_value < std : : vector < std : : string > > ( size_t index , const std : : vector < std : : string > & value ) ;
template void IFC_PARSE_API express : : Base : : set_attribute_value < std : : vector < boost : : dynamic_bitset < > > > ( size_t index , const std : : vector < boost : : dynamic_bitset < > > & value ) ;
template void IFC_PARSE_API express : : Base : : set_attribute_value < std : : vector < express : : Base > > ( size_t index , const std : : vector < express : : Base > & value ) ;
2026-07-19 12:58:23 +03:00
template void IFC_PARSE_API express : : Base : : set_attribute_value < std : : vector < std : : vector < int64_t > > > ( size_t index , const std : : vector < std : : vector < int64_t > > & value ) ;
2026-01-04 10:40:02 +01:00
template void IFC_PARSE_API express : : Base : : set_attribute_value < std : : vector < std : : vector < double > > > ( size_t index , const std : : vector < std : : vector < double > > & value ) ;
template void IFC_PARSE_API express : : Base : : set_attribute_value < std : : vector < std : : vector < express : : Base > > > ( size_t index , const std : : vector < std : : vector < express : : Base > > & value ) ;
2026-03-31 15:32:36 +02:00
template void IFC_PARSE_API express : : Base : : set_attribute_value < blank > ( const std : : string & name , const blank & value ) ;
template void IFC_PARSE_API express : : Base : : set_attribute_value < derived > ( const std : : string & name , const derived & value ) ;
2026-07-19 11:44:31 +03:00
template void IFC_PARSE_API express : : Base : : set_attribute_value < int64_t > ( const std : : string & name , const int64_t & value ) ;
2026-01-04 10:40:02 +01:00
template void IFC_PARSE_API express : : Base : : set_attribute_value < bool > ( const std : : string & name , const bool & value ) ;
template void IFC_PARSE_API express : : Base : : set_attribute_value < boost : : logic : : tribool > ( const std : : string & name , const boost : : logic : : tribool & value ) ;
template void IFC_PARSE_API express : : Base : : set_attribute_value < double > ( const std : : string & name , const double & value ) ;
template void IFC_PARSE_API express : : Base : : set_attribute_value < std : : string > ( const std : : string & name , const std : : string & value ) ;
template void IFC_PARSE_API express : : Base : : set_attribute_value < boost : : dynamic_bitset < > > ( const std : : string & name , const boost : : dynamic_bitset < > & value ) ;
2026-03-31 15:32:36 +02:00
template void IFC_PARSE_API express : : Base : : set_attribute_value < enumeration_reference > ( const std : : string & name , const enumeration_reference & value ) ;
2026-07-19 12:58:23 +03:00
template void IFC_PARSE_API express : : Base : : set_attribute_value < std : : vector < int64_t > > ( const std : : string & name , const std : : vector < int64_t > & value ) ;
2026-01-04 10:40:02 +01:00
template void IFC_PARSE_API express : : Base : : set_attribute_value < std : : vector < double > > ( const std : : string & name , const std : : vector < double > & value ) ;
template void IFC_PARSE_API express : : Base : : set_attribute_value < std : : vector < std : : string > > ( const std : : string & name , const std : : vector < std : : string > & value ) ;
template void IFC_PARSE_API express : : Base : : set_attribute_value < std : : vector < boost : : dynamic_bitset < > > > ( const std : : string & name , const std : : vector < boost : : dynamic_bitset < > > & value ) ;
template void IFC_PARSE_API express : : Base : : set_attribute_value < std : : vector < express : : Base > > ( const std : : string & name , const std : : vector < express : : Base > & value ) ;
2026-07-19 12:58:23 +03:00
template void IFC_PARSE_API express : : Base : : set_attribute_value < std : : vector < std : : vector < int64_t > > > ( const std : : string & name , const std : : vector < std : : vector < int64_t > > & value ) ;
2026-01-04 10:40:02 +01:00
template void IFC_PARSE_API express : : Base : : set_attribute_value < std : : vector < std : : vector < double > > > ( const std : : string & name , const std : : vector < std : : vector < double > > & value ) ;
template void IFC_PARSE_API express : : Base : : set_attribute_value < std : : vector < std : : vector < express : : Base > > > ( const std : : string & name , const std : : vector < std : : vector < express : : Base > > & value ) ;
namespace express {
template < typename T >
T Entity : : get_value ( const std : : string & name ) const {
auto attr = get ( name ) ;
T v = attr ;
return v ;
2025-03-09 21:20:16 +01:00
}
2025-02-27 22:07:31 +01:00
2026-01-04 10:40:02 +01:00
template < typename T >
T Entity : : get_value ( const std : : string & name , const T & default_value ) const {
auto attr = get ( name ) ;
if ( attr . isNull ( ) ) {
return default_value ;
}
T v = attr ;
return v ;
}
} // namespace express
2026-07-19 12:58:23 +03:00
template int64_t IFC_PARSE_API express : : Entity : : get_value < int64_t > ( const std : : string & ) const ;
2026-01-04 10:40:02 +01:00
template bool IFC_PARSE_API express : : Entity : : get_value < bool > ( const std : : string & ) const ;
template boost : : logic : : tribool IFC_PARSE_API express : : Entity : : get_value < boost : : logic : : tribool > ( const std : : string & ) const ;
template double IFC_PARSE_API express : : Entity : : get_value < double > ( const std : : string & ) const ;
template std : : string IFC_PARSE_API express : : Entity : : get_value < std : : string > ( const std : : string & ) const ;
template express : : Base IFC_PARSE_API express : : Entity : : get_value < express : : Base > ( const std : : string & ) const ;
template boost : : dynamic_bitset < > IFC_PARSE_API express : : Entity : : get_value < boost : : dynamic_bitset < > > ( const std : : string & ) const ;
2026-03-31 15:32:36 +02:00
template enumeration_reference IFC_PARSE_API express : : Entity : : get_value < enumeration_reference > ( const std : : string & ) const ;
2026-07-19 12:58:23 +03:00
template std : : vector < int64_t > IFC_PARSE_API express : : Entity : : get_value < std : : vector < int64_t > > ( const std : : string & ) const ;
2026-01-04 10:40:02 +01:00
template std : : vector < double > IFC_PARSE_API express : : Entity : : get_value < std : : vector < double > > ( const std : : string & ) const ;
template std : : vector < std : : string > IFC_PARSE_API express : : Entity : : get_value < std : : vector < std : : string > > ( const std : : string & ) const ;
template std : : vector < boost : : dynamic_bitset < > > IFC_PARSE_API express : : Entity : : get_value < std : : vector < boost : : dynamic_bitset < > > > ( const std : : string & ) const ;
template std : : vector < express : : Base > IFC_PARSE_API express : : Entity : : get_value < std : : vector < express : : Base > > ( const std : : string & ) const ;
2026-07-19 12:58:23 +03:00
template std : : vector < std : : vector < int64_t > > IFC_PARSE_API express : : Entity : : get_value < std : : vector < std : : vector < int64_t > > > ( const std : : string & ) const ;
2026-01-04 10:40:02 +01:00
template std : : vector < std : : vector < double > > IFC_PARSE_API express : : Entity : : get_value < std : : vector < std : : vector < double > > > ( const std : : string & ) const ;
template std : : vector < std : : vector < express : : Base > > IFC_PARSE_API express : : Entity : : get_value < std : : vector < std : : vector < express : : Base > > > ( const std : : string & ) const ;
2026-07-19 12:58:23 +03:00
template int64_t IFC_PARSE_API express : : Entity : : get_value < int64_t > ( const std : : string & , const int64_t & ) const ;
2026-01-04 10:40:02 +01:00
template bool IFC_PARSE_API express : : Entity : : get_value < bool > ( const std : : string & , const bool & ) const ;
template boost : : logic : : tribool IFC_PARSE_API express : : Entity : : get_value < boost : : logic : : tribool > ( const std : : string & , const boost : : logic : : tribool & ) const ;
template double IFC_PARSE_API express : : Entity : : get_value < double > ( const std : : string & , const double & ) const ;
template std : : string IFC_PARSE_API express : : Entity : : get_value < std : : string > ( const std : : string & , const std : : string & ) const ;
template express : : Base IFC_PARSE_API express : : Entity : : get_value < express : : Base > ( const std : : string & , const express : : Base & ) const ;
template boost : : dynamic_bitset < > IFC_PARSE_API express : : Entity : : get_value < boost : : dynamic_bitset < > > ( const std : : string & , const boost : : dynamic_bitset < > & ) const ;
2026-03-31 15:32:36 +02:00
template enumeration_reference IFC_PARSE_API express : : Entity : : get_value < enumeration_reference > ( const std : : string & , const enumeration_reference & ) const ;
2026-07-19 12:58:23 +03:00
template std : : vector < int64_t > IFC_PARSE_API express : : Entity : : get_value < std : : vector < int64_t > > ( const std : : string & , const std : : vector < int64_t > & ) const ;
2026-01-04 10:40:02 +01:00
template std : : vector < double > IFC_PARSE_API express : : Entity : : get_value < std : : vector < double > > ( const std : : string & , const std : : vector < double > & ) const ;
template std : : vector < std : : string > IFC_PARSE_API express : : Entity : : get_value < std : : vector < std : : string > > ( const std : : string & , const std : : vector < std : : string > & ) const ;
template std : : vector < boost : : dynamic_bitset < > > IFC_PARSE_API express : : Entity : : get_value < std : : vector < boost : : dynamic_bitset < > > > ( const std : : string & , const std : : vector < boost : : dynamic_bitset < > > & ) const ;
template std : : vector < express : : Base > IFC_PARSE_API express : : Entity : : get_value < std : : vector < express : : Base > > ( const std : : string & , const std : : vector < express : : Base > & ) const ;
2026-07-19 12:58:23 +03:00
template std : : vector < std : : vector < int64_t > > IFC_PARSE_API express : : Entity : : get_value < std : : vector < std : : vector < int64_t > > > ( const std : : string & , const std : : vector < std : : vector < int64_t > > & ) const ;
2026-01-04 10:40:02 +01:00
template std : : vector < std : : vector < double > > IFC_PARSE_API express : : Entity : : get_value < std : : vector < std : : vector < double > > > ( const std : : string & , const std : : vector < std : : vector < double > > & ) const ;
template std : : vector < std : : vector < express : : Base > > IFC_PARSE_API express : : Entity : : get_value < std : : vector < std : : vector < express : : Base > > > ( const std : : string & , const std : : vector < std : : vector < express : : Base > > & ) const ;