From df6b7a34f7bf73db59dc4158be5ef3fddd0b28bb Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Thu, 13 Mar 2025 13:09:39 +0100 Subject: [PATCH] Auto-detect filetype --- src/ifcconvert/IfcConvert.cpp | 16 ++++++-------- src/ifcparse/IfcFile.cpp | 40 +++++++++++++++++++++++++++++++++++ src/ifcparse/IfcFile.h | 16 ++++++++------ src/ifcparse/IfcParse.cpp | 20 +++++++++++++----- 4 files changed, 72 insertions(+), 20 deletions(-) diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index c2802df073..f6918fe396 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -206,7 +206,7 @@ size_t read_filters_from_file(const std::string&, inclusion_filter&, inclusion_t void parse_filter(geom_filter &, const std::vector&); std::vector setup_filters(const std::vector&, const std::string&); -bool init_input_file(const std::string& filename, IfcParse::IfcFile*& ifc_file, bool no_progress, bool mmap, bool rocksdb); +bool init_input_file(const std::string& filename, IfcParse::IfcFile*& ifc_file, bool no_progress, bool mmap); // from https://stackoverflow.com/questions/31696328/boost-program-options-using-zero-parameter-options-multiple-times struct verbosity_counter { @@ -239,7 +239,6 @@ int main(int argc, char** argv) { path_t cache_file; std::string log_format; std::string geometry_kernel; - std::string input_format; po::options_description generic_options("Command line options"); verbosity_counter vcounter; @@ -255,7 +254,6 @@ int main(int argc, char** argv) { ("yes,y", "answer 'yes' automatically to possible confirmation queries (e.g. overwriting an existing output file)") ("no-progress", "suppress possible progress bar type of prints that use carriage return") ("log-format", po::value(&log_format), "log format: plain or json") - ("input-format", po::value(&input_format), "input format: ifcspf, ifcxml, rocksdb") ("log-file", new po::typed_value(&log_file), "redirect log output to file"); po::options_description fileio_options; @@ -662,7 +660,7 @@ int main(int argc, char** argv) { if (output_extension == XML) { int exit_code = EXIT_FAILURE; try { - if (init_input_file(IfcUtil::path::to_utf8(input_filename), ifc_file, no_progress || quiet, mmap, input_format=="rocksdb")) { + if (init_input_file(IfcUtil::path::to_utf8(input_filename), ifc_file, no_progress || quiet, mmap)) { time_t start, end; time(&start); XmlSerializer s(ifc_file, IfcUtil::path::to_utf8(output_temp_filename)); @@ -682,7 +680,7 @@ int main(int argc, char** argv) { } else if (output_extension == IFC) { int exit_code = EXIT_FAILURE; try { - if (init_input_file(IfcUtil::path::to_utf8(input_filename), ifc_file, no_progress || quiet, mmap, input_format == "rocksdb")) { + if (init_input_file(IfcUtil::path::to_utf8(input_filename), ifc_file, no_progress || quiet, mmap)) { time_t start, end; time(&start); std::ofstream fs(output_filename.c_str()); @@ -708,7 +706,7 @@ int main(int argc, char** argv) { else if (output_extension == RDB) { int exit_code = EXIT_FAILURE; try { - if (init_input_file(IfcUtil::path::to_utf8(input_filename), ifc_file, no_progress || quiet, mmap, input_format == "rocksdb")) { + if (init_input_file(IfcUtil::path::to_utf8(input_filename), ifc_file, no_progress || quiet, mmap)) { time_t start, end; time(&start); RocksDbSerializer s(ifc_file, IfcUtil::path::to_utf8(output_filename)); @@ -941,7 +939,7 @@ int main(int argc, char** argv) { time_t start,end; time(&start); - if (!init_input_file(IfcUtil::path::to_utf8(input_filename), ifc_file, no_progress || quiet, mmap, input_format == "rocksdb")) { + if (!init_input_file(IfcUtil::path::to_utf8(input_filename), ifc_file, no_progress || quiet, mmap)) { write_log(!quiet); serializer.reset(); IfcUtil::path::delete_file(IfcUtil::path::to_utf8(output_temp_filename)); /**< @todo Windows Unicode support */ @@ -1274,7 +1272,7 @@ void write_log(bool header) { #include -bool init_input_file(const std::string& filename, IfcParse::IfcFile*& ifc_file, bool no_progress, bool mmap, bool rocksdb) { +bool init_input_file(const std::string& filename, IfcParse::IfcFile*& ifc_file, bool no_progress, bool mmap) { time_t start, end; // Prevent IfcFile::Init() prints by setting output to null temporarily @@ -1293,7 +1291,7 @@ bool init_input_file(const std::string& filename, IfcParse::IfcFile*& ifc_file, ifc_file = new IfcParse::IfcFile(filename, mmap); #else (void)mmap; - ifc_file = new IfcParse::IfcFile(filename, rocksdb ? IfcParse::rocksdb : IfcParse::ifcspf); + ifc_file = new IfcParse::IfcFile(filename); #endif } diff --git a/src/ifcparse/IfcFile.cpp b/src/ifcparse/IfcFile.cpp index d423542399..98dbe8af58 100644 --- a/src/ifcparse/IfcFile.cpp +++ b/src/ifcparse/IfcFile.cpp @@ -499,3 +499,43 @@ IfcParse::IfcFile::~IfcFile() { delete p.second; } } + +#include +#include + +IfcParse::filetype IfcParse::guess_file_type(const std::string& fn) { + namespace fs = std::filesystem; + + if (!fs::exists(fn)) { + // @todo this is just weird, but for consistency with earlier behaviour + // for now the only intent for this function is to auto-detect RocksDB + return FT_IFCSPF; + } + + if (fs::is_directory(fn)) { + // Typical RocksDB file to look for + auto currentFile = fs::path(fn) / "CURRENT"; + + if (!fs::exists(currentFile) || !fs::is_regular_file(currentFile)) { + return FT_UNKNOWN; + } + + std::ifstream infile(currentFile); + if (!infile) { + return FT_UNKNOWN; + } + + std::string line; + if (!std::getline(infile, line)) { + return FT_UNKNOWN; + } + + // RocksDB's CURRENT file typically contains a line like "MANIFEST-000001". + if (line.find("MANIFEST-") == 0) { + return FT_ROCKSDB; + } + } else { + // @todo just return SPF for now, but ideally this will be augmented with all other options + return FT_IFCSPF; + } +} diff --git a/src/ifcparse/IfcFile.h b/src/ifcparse/IfcFile.h index 14085046b6..c1a244f117 100644 --- a/src/ifcparse/IfcFile.h +++ b/src/ifcparse/IfcFile.h @@ -572,12 +572,16 @@ namespace impl { } enum filetype { - ifcspf, - ifcxml, - rocksdb, - autodetect + FT_IFCSPF, + FT_IFCXML, + FT_IFCZIP, + FT_ROCKSDB, + FT_UNKNOWN, + FT_AUTODETECT }; +filetype guess_file_type(const std::string& fn); + /// This class provides access to the entity instances in an IFC file /// The file takes ownership of instances added to this file and deletes them when the file is deleted. class IFC_PARSE_API IfcFile { @@ -623,13 +627,13 @@ private: #ifdef USE_MMAP IfcFile(const std::string& path, bool mmap = false); #else - IfcFile(const std::string& path, filetype ty=ifcspf); + IfcFile(const std::string& path, filetype ty=FT_AUTODETECT); #endif IfcFile(std::istream& stream, int length); IfcFile(void* data, int length); IfcFile(IfcParse::IfcSpfStream* stream); // @nb path is only used in rocksdb mode, for spf file is in-memory only until write() is called - IfcFile(const IfcParse::schema_definition* schema = IfcParse::schema_by_name("IFC4"), filetype ty = ifcspf, const std::string& path = ""); + IfcFile(const IfcParse::schema_definition* schema = IfcParse::schema_by_name("IFC4"), filetype ty = FT_AUTODETECT, const std::string& path = ""); ~IfcFile(); diff --git a/src/ifcparse/IfcParse.cpp b/src/ifcparse/IfcParse.cpp index f3b9389403..8e921ff856 100644 --- a/src/ifcparse/IfcParse.cpp +++ b/src/ifcparse/IfcParse.cpp @@ -1296,7 +1296,10 @@ IfcFile::IfcFile(const std::string& fn, bool mmap) { #else IfcFile::IfcFile(const std::string& path, filetype ty) { // @todo allow for rocksdb from path - if (ty == ifcspf) { + if (ty == FT_AUTODETECT) { + ty = guess_file_type(path); + } + if (ty == FT_IFCSPF) { IfcSpfStream s(path); storage_.emplace<1>(); // impl::in_memory_file_storage{}; // @todo assign in constructor @@ -1307,7 +1310,7 @@ IfcFile::IfcFile(const std::string& path, filetype ty) { byid_ = decltype(byid_)(&std::get(storage_).byid_); byref_excl_ = decltype(byref_excl_)(&std::get(storage_).byref_excl_); // byidentity_ = decltype(byidentity_)(&std::get(storage_).byidentity_); - } else { + } else if (ty == FT_ROCKSDB) { // @todo this can only be used for databases that already exist, because otherwise there is no way to specify the schema storage_.emplace<2>(path, this); std::get(storage_).read_schema(schema_); @@ -1315,6 +1318,8 @@ IfcFile::IfcFile(const std::string& path, filetype ty) { byid_ = decltype(byid_)(&std::get(storage_).instance_by_name_); byref_excl_ = decltype(byref_excl_)(&std::get(storage_).byref_excl_); // byidentity_ = decltype(byidentity_)(&std::get(storage_).instance_cache_); + } else { + throw std::runtime_error("Unsupported file format"); } ifcroot_type_ = schema_->declaration_by_name("IfcRoot"); } @@ -1344,21 +1349,26 @@ IfcFile::IfcFile(const IfcParse::schema_definition* schema, filetype ty, const s : schema_(schema) , ifcroot_type_(schema_->declaration_by_name("IfcRoot")) , max_id_(0) - , _header(ty == rocksdb ? this : nullptr) + , _header(ty == FT_ROCKSDB ? this : nullptr) { - if (ty == ifcspf) { + if (ty == FT_AUTODETECT) { + ty = guess_file_type(path); + } + if (ty == FT_IFCSPF) { storage_.emplace<1>(); std::get(storage_).file = this; byid_ = decltype(byid_)(&std::get(storage_).byid_); byref_excl_ = decltype(byref_excl_)(&std::get(storage_).byref_excl_); // byidentity_ = decltype(byidentity_)(&std::get(storage_).byidentity_); - } else { + } else if (ty == FT_ROCKSDB) { storage_.emplace<2>(path, this); byid_ = decltype(byid_)(&std::get(storage_).instance_by_name_); byref_excl_ = decltype(byref_excl_)(&std::get(storage_).byref_excl_); // byidentity_ = decltype(byidentity_)(&std::get(storage_).instance_cache_); + } else { + throw std::runtime_error("Unsupported file format"); } setDefaultHeaderValues(); }