Files

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

731 lines
31 KiB
C++
Raw Permalink Normal View History

2023-09-17 12:30:17 +02: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/>. *
* *
********************************************************************************/
2023-09-17 12:30:17 +02:00
2018-09-02 13:55:36 +02:00
#define BOOST_RESULT_OF_USE_DECLTYPE
#ifdef WITH_IFCXML
2018-08-29 12:56:35 +02:00
#include "IfcFile.h"
#include "IfcLogger.h"
2018-08-29 12:56:35 +02:00
#include <boost/algorithm/string.hpp>
2018-08-29 12:56:35 +02:00
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/algorithm/copy.hpp>
2024-08-23 20:29:07 +02:00
#include <boost/any.hpp>
2023-09-17 12:30:17 +02:00
#include <libxml/parser.h>
2018-08-29 12:56:35 +02:00
2024-08-23 20:29:07 +02:00
namespace {
// Base case: when there are no more types left to check.
template <typename Fn>
void visit_any_impl(Fn& fn, const boost::any& a) {
}
// Recursive case: Check the first type in the pack.
template <typename Fn, typename T, typename... Types>
void visit_any_impl(Fn& fn, const boost::any& a) {
if (a.type() == typeid(T)) {
Fn(boost::any_cast<T>(a));
} else {
visit_any_impl<Types...>(a);
}
}
// Helper to prepend a type to a tuple
template <typename Fn, typename U>
void visit_any(Fn fn, const boost::any& a);
template <typename Fn, typename... Types>
void visit_any(Fn fn, const boost::any & a) {
visit_any_impl<Fn, Types...>(fn, a);
};
}
2018-08-29 12:56:35 +02:00
// For debug printing on release builds
// #undef NDEBUG
// ifcXML is quite radically different for ifc2x3 and ifc4. ifc2x3 follows
// iso 10303 part 28 and puts all attribute values in XML text nodes. ifc4
// has attributes in actual XML attributes and may include inverse attributes
// to make trees more compact.
enum ifcxml_dialect {
2023-09-17 12:30:17 +02:00
ifcxml_dialect_ifc2x3,
ifcxml_dialect_ifc4,
ifcxml_dialect_unknown
2018-08-29 12:56:35 +02:00
};
// "Dereferences" a named_attribute. For example:
// IfcCompoundPlaneAngleMeasure -> LIST [3:4] OF INTEGER
void follow_named(const IfcParse::parameter_type*& pt) {
while (pt->as_named_type() != nullptr) {
if (pt->as_named_type()->declared_type()->as_type_declaration() != nullptr) {
2023-09-17 12:30:17 +02:00
pt = pt->as_named_type()->declared_type()->as_type_declaration()->declared_type();
} else {
break;
}
}
2018-08-29 12:56:35 +02:00
}
// The ifcXML parser uses SAX so we need to keep a stack of where we are in
// the file. These different kinds of nodes could be subclasses but aren't
// for ease in pushing to std::vector.
class stack_node {
2023-09-17 12:30:17 +02:00
public:
enum node_type {
stack_empty,
node_instance,
node_instance_attribute,
node_aggregate,
node_aggregate_element,
node_inverse,
node_select,
node_header,
node_header_entry
};
2024-08-23 20:29:07 +02:00
// to be coerced into the correct type later on
std::vector<boost::any> aggregate_elements;
2023-09-17 12:30:17 +02:00
protected:
node_type type_;
IfcUtil::IfcBaseClass* inst_;
int idx_;
const IfcParse::inverse_attribute* inv_;
std::string tagname_;
std::string id_in_file_;
const IfcParse::parameter_type* aggregate_elem_type_;
stack_node()
: type_(stack_empty),
inst_(nullptr),
idx_(-1),
inv_(nullptr),
aggregate_elem_type_(nullptr) {}
public:
2024-08-23 20:29:07 +02:00
static stack_node instance(const std::string& id, IfcUtil::IfcBaseClass* inst) {
stack_node node;
node.type_ = node_instance;
node.inst_ = inst;
2024-08-23 20:29:07 +02:00
node.id_in_file_ = id;
return node;
2023-09-17 12:30:17 +02:00
}
static stack_node instance_attribute(IfcUtil::IfcBaseClass* inst, int idx) {
stack_node node;
node.type_ = node_instance_attribute;
node.inst_ = inst;
node.idx_ = idx;
return node;
2023-09-17 12:30:17 +02:00
}
static stack_node aggregate(IfcUtil::IfcBaseClass* inst, int idx) {
stack_node node;
node.type_ = node_aggregate;
node.inst_ = inst;
node.idx_ = idx;
return node;
2023-09-17 12:30:17 +02:00
}
static stack_node aggregate_element(const IfcParse::parameter_type* aggregate_elem_type, int idx) {
stack_node node;
node.type_ = node_aggregate_element;
node.idx_ = idx;
node.aggregate_elem_type_ = aggregate_elem_type;
return node;
2023-09-17 12:30:17 +02:00
}
static stack_node inverse(IfcUtil::IfcBaseClass* inst, const IfcParse::inverse_attribute* inv) {
stack_node node;
node.type_ = node_inverse;
node.inst_ = inst;
node.inv_ = inv;
return node;
2023-09-17 12:30:17 +02:00
}
static stack_node select(IfcUtil::IfcBaseClass* inst, int idx) {
stack_node node;
node.type_ = node_select;
node.inst_ = inst;
node.idx_ = idx;
return node;
2023-09-17 12:30:17 +02:00
}
static stack_node header() {
stack_node node;
node.type_ = node_header;
return node;
2023-09-17 12:30:17 +02:00
};
static stack_node header_entry(const std::string& tagname) {
stack_node node;
node.type_ = node_header_entry;
node.tagname_ = tagname;
return node;
2023-09-17 12:30:17 +02:00
};
node_type ntype() const { return type_; }
IfcUtil::IfcBaseClass* inst() const { return inst_; }
int idx() const { return idx_; }
const IfcParse::inverse_attribute* inv_attr() const { return inv_; }
const std::string& tagname() const { return tagname_; }
2024-08-23 20:29:07 +02:00
const std::string& id() const { return id_in_file_; }
2023-09-17 12:30:17 +02:00
const IfcParse::parameter_type* aggregate_elem_type() const { return aggregate_elem_type_; }
std::string repr() const {
std::stringstream stream;
2023-09-17 12:30:17 +02:00
static const char* const node_type_names[] = {"empty", "inst", "attr", "aggr", "agelem", "inv", "sel", "head", "hdentry"};
stream << "[" << node_type_names[type_] << "] ";
if (inst_ != nullptr) {
stream << inst_->declaration().name() << " ";
2023-09-17 12:30:17 +02:00
}
if (type_ == node_aggregate) {
stream << "{" << aggregate_elements.size() << " elems} ";
2023-09-17 12:30:17 +02:00
}
if (idx_ != -1) {
stream << idx_ << " ";
2023-09-17 12:30:17 +02:00
}
return stream.str();
2023-09-17 12:30:17 +02:00
}
2018-08-29 12:56:35 +02:00
};
struct ifcxml_parse_state {
2026-06-11 21:04:44 +02:00
explicit ifcxml_parse_state(Logger& logger)
: file(nullptr)
, dialect(ifcxml_dialect_unknown)
, logger(logger)
{}
2023-09-17 12:30:17 +02:00
IfcParse::IfcFile* file;
std::vector<stack_node> stack;
std::map<std::string, int> idmap;
2024-08-23 20:29:07 +02:00
std::vector<std::tuple<IfcUtil::IfcBaseEntity*, size_t, std::string>> forward_references;
2023-09-17 12:30:17 +02:00
ifcxml_dialect dialect;
2026-06-11 21:04:44 +02:00
Logger& logger;
2018-08-29 12:56:35 +02:00
};
// ifc4 allows for aggregates to be concatenated using whitespace.
template <typename T>
std::vector<T> split(const std::string& value) {
2023-09-17 12:30:17 +02:00
std::vector<std::string> strs;
boost::split(
strs, value, [](char character) { return character == ' '; }, boost::token_compress_on);
2023-09-17 12:30:17 +02:00
std::vector<T> r(strs.size());
boost::copy(strs | boost::adaptors::transformed([](const std::string& s) {
return boost::lexical_cast<T>(s);
}),
r.begin());
return r;
2018-08-29 12:56:35 +02:00
}
2026-06-11 21:04:44 +02:00
boost::any parse_attribute_value(const IfcParse::parameter_type* ty, const std::string& value, Logger& logger) {
2024-08-23 20:29:07 +02:00
boost::any any;
2023-09-17 12:30:17 +02:00
auto cpp_type = IfcUtil::from_parameter_type(ty);
if (cpp_type == IfcUtil::Argument_STRING) {
2024-08-23 20:29:07 +02:00
any = value;
2023-09-17 12:30:17 +02:00
} else if (cpp_type == IfcUtil::Argument_ENUMERATION) {
const auto* enum_type = ty->as_named_type()->declared_type()->as_enumeration_type();
2023-09-17 12:30:17 +02:00
std::vector<std::string>::const_iterator iter = std::find(
2023-09-17 12:30:17 +02:00
enum_type->enumeration_items().begin(),
enum_type->enumeration_items().end(),
boost::to_upper_copy(value));
2024-08-23 20:29:07 +02:00
any = EnumerationReference(enum_type, std::distance(enum_type->enumeration_items().begin(), iter));
2023-09-17 12:30:17 +02:00
} else if (cpp_type == IfcUtil::Argument_INT) {
2024-08-23 20:29:07 +02:00
any = boost::lexical_cast<int>(value);
2023-09-17 12:30:17 +02:00
} else if (cpp_type == IfcUtil::Argument_DOUBLE) {
2024-08-23 20:29:07 +02:00
any = boost::lexical_cast<double>(value);
2023-09-17 12:30:17 +02:00
} else if (cpp_type == IfcUtil::Argument_BOOL) {
2024-08-23 20:29:07 +02:00
any = boost::to_lower_copy(value) == "true";
2023-09-17 12:30:17 +02:00
} else if (cpp_type == IfcUtil::Argument_AGGREGATE_OF_INT) {
2024-08-23 20:29:07 +02:00
any = split<int>(value);
2023-09-17 12:30:17 +02:00
} else if (cpp_type == IfcUtil::Argument_AGGREGATE_OF_DOUBLE) {
2024-08-23 20:29:07 +02:00
any = split<double>(value);
2023-09-17 12:30:17 +02:00
}
2024-08-23 20:29:07 +02:00
if (any.empty()) {
2026-06-11 21:04:44 +02:00
logger.Error("SYN", 29, "Attribute '" + value + "' not successfully parsed");
2023-09-17 12:30:17 +02:00
}
2024-08-23 20:29:07 +02:00
return any;
2018-08-29 12:56:35 +02:00
}
static void end_element(void* user, const xmlChar* tag) {
2023-09-17 12:30:17 +02:00
ifcxml_parse_state* state = (ifcxml_parse_state*)user;
if (state->file == nullptr) {
return;
}
if (!state->stack.empty() && state->stack.back().ntype() == stack_node::node_aggregate) {
const auto& back = state->stack.back();
auto& elems = state->stack.back().aggregate_elements;
2024-08-23 20:29:07 +02:00
/*
auto* list = new IfcParse::ArgumentList(elems.size());
2023-09-17 12:30:17 +02:00
size_t i = 0;
for (auto& elem : elems) {
list->arguments()[i++] = elem;
2023-09-17 12:30:17 +02:00
}
2024-08-23 20:29:07 +02:00
*/
// @todo
2025-02-27 22:07:31 +01:00
// back.inst()->set_attribute_value(back.idx(), elems);
2023-09-17 12:30:17 +02:00
}
if (state->dialect == ifcxml_dialect_ifc2x3 && state->stack.back().ntype() == stack_node::node_instance) {
if (state->stack.back().inst() != nullptr) {
2024-08-23 20:29:07 +02:00
state->idmap[state->stack.back().id()] = state->file->addEntity(state->stack.back().inst())->id();
2023-09-17 12:30:17 +02:00
}
}
const std::string tagname = (char*)tag;
// ignore uos ex:iso_10303_28 (ifc2x3) and ifc:ifcXML (ifc4)
if (tagname != "uos" && tagname != "ex:iso_10303_28" && tagname != "ifc:ifcXML" && tagname != "ifcXML") {
if (state->stack.empty()) {
2026-06-11 21:04:44 +02:00
state->logger.Error("SYN", 30, "Mismatch in parse stack due to previous errors");
2023-09-17 12:30:17 +02:00
} else {
state->stack.pop_back();
}
}
2018-08-29 12:56:35 +02:00
}
static void process_characters(void* user, const xmlChar* character, int len) {
2023-09-17 12:30:17 +02:00
ifcxml_parse_state* state = (ifcxml_parse_state*)user;
if (state->file == nullptr) {
return;
}
std::string txt((char*)character, len);
2023-09-17 12:30:17 +02:00
stack_node::node_type state_type = stack_node::stack_empty;
if (!state->stack.empty()) {
state_type = state->stack.back().ntype();
}
if (!state->stack.empty() && state->stack.back().inst() != nullptr && (state->stack.back().inst()->declaration().as_type_declaration() != nullptr)) {
const auto* pt = state->stack.back().inst()->declaration().as_type_declaration()->declared_type();
2024-08-23 20:29:07 +02:00
boost::any val;
2023-09-17 12:30:17 +02:00
try {
2026-06-11 21:04:44 +02:00
val = parse_attribute_value(pt, txt, state->logger);
2023-09-17 12:30:17 +02:00
} catch (const std::exception& e) {
2026-06-11 21:04:44 +02:00
state->logger.Error("SYN", 31, e, state->stack.back().inst());
2023-09-17 12:30:17 +02:00
}
2024-08-23 20:29:07 +02:00
if (!val.empty()) {
2023-09-17 12:30:17 +02:00
// type declaration always at idx 0
2024-08-23 20:29:07 +02:00
visit_any([&state](auto& v) {
2025-02-27 22:07:31 +01:00
state->stack.back().inst()->set_attribute_value(0, v);
2024-08-23 20:29:07 +02:00
}, val);
2023-09-17 12:30:17 +02:00
}
} else if (state_type == stack_node::node_header_entry) {
const std::string tagname = boost::replace_all_copy(state->stack.back().tagname(), "ex:", "");
auto& header = state->file->header();
if (tagname == "name") {
2025-02-27 22:07:31 +01:00
header.file_name()->setname(txt);
2023-09-17 12:30:17 +02:00
} else if (tagname == "time_stamp") {
2025-02-27 22:07:31 +01:00
header.file_name()->settime_stamp(txt);
2023-09-17 12:30:17 +02:00
} else if (tagname == "author") {
2025-02-27 22:07:31 +01:00
header.file_name()->setauthor({txt});
2023-09-17 12:30:17 +02:00
} else if (tagname == "organization") {
2025-02-27 22:07:31 +01:00
header.file_name()->setorganization({txt});
2023-09-17 12:30:17 +02:00
} else if (tagname == "preprocessor_version") {
2025-02-27 22:07:31 +01:00
header.file_name()->setpreprocessor_version(txt);
2023-09-17 12:30:17 +02:00
} else if (tagname == "originating_system") {
2025-02-27 22:07:31 +01:00
header.file_name()->setoriginating_system(txt);
2023-09-17 12:30:17 +02:00
} else if (tagname == "authorization") {
header.file_name()->setauthorization(txt);
2023-09-17 12:30:17 +02:00
} else if (tagname == "documentation") {
2025-02-27 22:07:31 +01:00
header.file_description()->setdescription({txt});
2023-09-17 12:30:17 +02:00
} else {
2026-06-11 21:04:44 +02:00
state->logger.Error("SYN", 32, "Unrecognized header entry " + tagname);
2023-09-17 12:30:17 +02:00
}
} else if (state_type == stack_node::node_instance_attribute) {
const auto* pt = state->stack.back().inst()->declaration().as_entity()->attribute_by_index(state->stack.back().idx())->type_of_attribute();
2023-09-17 12:30:17 +02:00
auto cpp_type = IfcUtil::from_parameter_type(pt);
if (cpp_type != IfcUtil::Argument_ENTITY_INSTANCE) {
2026-06-11 21:04:44 +02:00
auto val = parse_attribute_value(pt, txt, state->logger);
2024-08-23 20:29:07 +02:00
if (!val.empty()) {
visit_any([&state](auto& v) {
state->stack.back().inst()->set_attribute_value(state->stack.back().idx(), v);
}, val);
2023-09-17 12:30:17 +02:00
}
}
} else if (state_type == stack_node::node_aggregate_element) {
const auto* pt = state->stack.back().aggregate_elem_type();
2026-06-11 21:04:44 +02:00
auto val = parse_attribute_value(pt, txt, state->logger);
2024-08-23 20:29:07 +02:00
if (!val.empty()) {
2023-09-17 12:30:17 +02:00
(*(state->stack.rbegin() + 1)).aggregate_elements.push_back(val);
}
}
2018-08-29 12:56:35 +02:00
}
static void start_element(void* user, const xmlChar* tag, const xmlChar** attrs) {
2023-09-17 12:30:17 +02:00
ifcxml_parse_state* state = (ifcxml_parse_state*)user;
std::string tagname = (char*)tag;
2018-08-29 12:56:35 +02:00
#ifndef NDEBUG
2023-09-17 12:30:17 +02:00
std::cout << "stack:" << std::endl;
{
int i = 1;
for (auto& node : state->stack) {
std::cout << " " << (i++) << ":" << node.repr() << std::endl;
2023-09-17 12:30:17 +02:00
}
}
std::cout << std::string(state->stack.size(), ' ') << "<" << tagname << ">";
2018-08-29 12:56:35 +02:00
#endif
2023-09-17 12:30:17 +02:00
std::vector<std::pair<std::string, std::string>> attributes;
2018-08-29 12:56:35 +02:00
if (attrs != nullptr) {
2023-09-17 12:30:17 +02:00
std::string attrname;
int i = 0;
while (attrs[i] != NULL) {
if ((i % 2) != 0) {
2023-09-17 12:30:17 +02:00
const std::string value = (char*)attrs[i];
2018-08-29 12:56:35 +02:00
#ifndef NDEBUG
2023-09-17 12:30:17 +02:00
std::cout << " " << attrname << "='" << value << "'";
2018-08-29 12:56:35 +02:00
#endif
2023-09-17 12:30:17 +02:00
attributes.push_back(std::make_pair(attrname, value));
if ((tagname == "ifc:ifcXML" || tagname == "ifcXML") && attrname == "xsi:schemaLocation" &&
(boost::starts_with(value, "http://www.buildingsmart-tech.org/ifcXML/IFC4") ||
boost::starts_with(value, "http://www.buildingsmart-tech.org/ifc/IFC4"))) {
// We're expecting a schemaLocation like "http://www.buildingsmart-tech.org/ifcXML/IFC4/Add2 IFC4_ADD2_TC1.xsd"
// With token compression this is split into:
// [0] http:
// [1] www.buildingsmart-tech.org
// [2] ifcXML
// [3] IFC4
// [4] Add2
// [5] IFC4_ADD2_TC1.xsd
// The hosstname will likely change though.
auto it = boost::algorithm::make_split_iterator(value, boost::algorithm::token_finder(boost::algorithm::is_any_of("/ "), boost::algorithm::token_compress_on));
2023-09-17 12:30:17 +02:00
decltype(it) end;
for (int tok = 0; it != end && tok < 3; ++it, ++tok) {
}
if (it != end) {
std::string schema_name(&it->front(), it->size());
boost::to_upper(schema_name);
2026-06-11 21:04:44 +02:00
state->file = new IfcParse::IfcFile(IfcParse::schema_by_name(schema_name), IfcParse::FT_AUTODETECT, "", state->logger);
2023-09-17 12:30:17 +02:00
state->dialect = ifcxml_dialect_ifc4;
}
goto end;
} else if (tagname == "ex:iso_10303_28" && attrname == "xsi:schemaLocation" && boost::starts_with(value, "http://www.iai-tech.org/ifcXML/IFC2x3")) {
2026-06-11 21:04:44 +02:00
state->file = new IfcParse::IfcFile(IfcParse::schema_by_name("IFC2X3"), IfcParse::FT_AUTODETECT, "", state->logger);
2023-09-17 12:30:17 +02:00
state->dialect = ifcxml_dialect_ifc2x3;
goto end;
}
} else {
attrname = (char*)attrs[i];
}
i++;
}
}
if (state->file == nullptr) {
return;
}
{
// ifcXML id attributes are commonly numeric identifiers prefixed with 'i' (as
// XML identifiers need to start with a alphabetic character). This convention
// is not always followed, so a mapping is kept from XML string attribute to
// numeric index into the IfcParse::IfcFile.
2024-08-23 20:29:07 +02:00
std::string id;
2023-09-17 12:30:17 +02:00
// Create an attribute value from an instance. Potentially NULL in case it is a
// forward reference to an instance not yet encountered.
auto instance_to_attribute = [&state](const std::variant<std::string, IfcUtil::IfcBaseClass*>& inst_or_ref, size_t attribute_index, IfcUtil::IfcBaseClass*& inst) {
if (inst_or_ref.index() == 0) {
2023-09-17 12:30:17 +02:00
inst = nullptr;
// This attribute is NULL initially and after parsing the complete
// file populated in a subsequent step.
state->forward_references.push_back(std::make_tuple(inst->as<IfcUtil::IfcBaseEntity>(), attribute_index, std::get<std::string>(inst_or_ref)));
2023-09-17 12:30:17 +02:00
} else {
inst = std::get<IfcUtil::IfcBaseClass*>(inst_or_ref);
2024-08-23 20:29:07 +02:00
inst->set_attribute_value(attribute_index, inst);
2023-09-17 12:30:17 +02:00
}
};
// Create or reference an instance from the file and set attributes based on XML attributes.
2024-08-23 20:29:07 +02:00
auto create_instance = [&state, &attributes](const IfcParse::declaration* decl) {
2023-09-17 12:30:17 +02:00
boost::optional<std::string> id;
std::variant<std::string, IfcUtil::IfcBaseClass*> rv;
2023-09-17 12:30:17 +02:00
for (auto& pair : attributes) {
if (pair.first == "id" || pair.first == "href" || pair.first == "ref") {
2024-08-23 20:29:07 +02:00
id = id = pair.second;
2023-09-17 12:30:17 +02:00
if (pair.first == "href" || pair.first == "ref") {
if (state->idmap.find(pair.second) == state->idmap.end()) {
rv = pair.second;
return rv;
}
rv = state->file->instance_by_id(state->idmap[pair.second]);
return rv;
2023-09-17 12:30:17 +02:00
}
} else if (pair.first == "xsi:type") {
decl = state->file->schema()->declaration_by_name(pair.second)->as_entity();
}
}
auto untyped = IfcEntityInstanceData(in_memory_attribute_storage(decl->as_entity() != nullptr ? decl->as_entity()->attribute_count() : 1));
2023-09-17 12:30:17 +02:00
const IfcParse::entity* entity = decl->as_entity();
if (entity != nullptr) {
2023-09-17 12:30:17 +02:00
for (auto& pair : attributes) {
if (pair.first == "id" || pair.first == "xsi:type" || pair.first == "pos") {
continue;
}
auto idx = entity->attribute_index(pair.first);
if (idx != -1) {
const auto* attr = entity->attribute_by_index(idx);
2026-06-11 21:04:44 +02:00
auto val = parse_attribute_value(attr->type_of_attribute(), pair.second, state->logger);
2024-08-23 20:29:07 +02:00
if (!val.empty()) {
visit_any([&untyped, idx](auto& v) {
untyped.set_attribute_value(idx, v);
2024-08-23 20:29:07 +02:00
}, val);
2023-09-17 12:30:17 +02:00
}
} else {
2026-06-11 21:04:44 +02:00
state->logger.Error("SYN", 33, "Unknown attribute '" + pair.first + "' on entity '" + entity->name() + "' with value '" + pair.second + "'");
2023-09-17 12:30:17 +02:00
}
}
}
2024-08-23 20:29:07 +02:00
IfcUtil::IfcBaseClass* newinst = state->file->schema()->instantiate(decl, std::move(untyped));
2023-09-17 12:30:17 +02:00
if (state->dialect == ifcxml_dialect_ifc4) {
// In IFC2X3 not added directly because attrs such as GlobalId are in
// subsequent child nodes
newinst = state->file->addEntity(newinst);
if (id) {
2024-08-23 20:29:07 +02:00
state->idmap[*id] = newinst->id();
2023-09-17 12:30:17 +02:00
}
}
rv = newinst;
return rv;
};
stack_node::node_type state_type = stack_node::stack_empty;
if (!state->stack.empty()) {
state_type = state->stack.back().ntype();
}
const std::string tagname_copy = boost::replace_all_copy(tagname, "-wrapper", "");
if (state_type == stack_node::node_select) {
const IfcParse::declaration* decl = state->file->schema()->declaration_by_name(tagname_copy);
2024-08-23 20:29:07 +02:00
// Argument* attr;
2023-09-17 12:30:17 +02:00
IfcUtil::IfcBaseClass* inst;
auto inst_ = create_instance(decl);
2024-08-23 20:29:07 +02:00
instance_to_attribute(inst_, state->stack.back().idx(), inst);
2025-02-27 22:07:31 +01:00
// state->stack.back().inst()->set_attribute_value(state->stack.back().idx(), attr);
2024-08-23 20:29:07 +02:00
state->stack.push_back(stack_node::instance(id, inst));
2023-09-17 12:30:17 +02:00
} else if (state_type == stack_node::node_aggregate) {
const IfcParse::parameter_type* attribute_type = state->stack.back().inst()->declaration().as_entity()->attribute_by_index(state->stack.back().idx())->type_of_attribute();
follow_named(attribute_type);
const IfcParse::parameter_type* element_type = attribute_type->as_aggregation_type()->type_of_element();
follow_named(element_type);
int aggrpos = -1;
/*
2018-08-29 12:56:35 +02:00
auto it = std::find_if(attributes.begin(), attributes.end(), [](const std::pair<std::string, std::string>& p) {
return p.first == "pos";
});
boost::lexical_cast<int>(it->second);
*/
if (element_type->as_simple_type() != nullptr) {
2023-09-17 12:30:17 +02:00
state->stack.push_back(stack_node::aggregate_element(element_type, aggrpos));
} else {
const IfcParse::declaration* decl = nullptr;
try {
decl = state->file->schema()->declaration_by_name(tagname_copy);
} catch (const std::exception& e) {
2026-06-11 21:04:44 +02:00
state->logger.Error("SYN", 34, e);
2023-09-17 12:30:17 +02:00
}
if (decl != nullptr) {
2023-09-17 12:30:17 +02:00
auto inst_or_ref = create_instance(decl);
IfcUtil::IfcBaseClass* inst;
2024-08-23 20:29:07 +02:00
// Argument* attr;
// @todo
// instance_to_attribute(inst_or_ref, attr, inst);
state->stack.back().aggregate_elements.push_back(boost::any{});
state->stack.push_back(stack_node::instance(id, inst));
2023-09-17 12:30:17 +02:00
}
}
} else if (state_type == stack_node::node_instance) {
const IfcParse::entity* current = state->stack.back().inst()->declaration().as_entity();
if (current == nullptr) {
2026-06-11 21:04:44 +02:00
state->logger.Error("SYN", 35, "'" + state->stack.back().inst()->declaration().name() + "' is not an entity, unable to set attribute '" + tagname + "'");
2023-09-17 12:30:17 +02:00
// We need to push something on the stack. Likely there has been some extra indirection that is not understood.
state->stack.push_back(state->stack.back());
} else {
auto idx = current->attribute_index(tagname);
if (idx == -1) {
auto inverses = current->all_inverse_attributes();
auto found = std::find_if(inverses.begin(), inverses.end(), [&tagname](const IfcParse::inverse_attribute* attr) {
return attr->name() == tagname;
});
if (found == inverses.end()) {
2026-06-11 21:04:44 +02:00
state->logger.Error("SYN", 36, "Unknown attribute " + tagname);
2023-09-17 12:30:17 +02:00
state->stack.push_back(state->stack.back());
} else {
if ((*found)->bound1() == 0 && (*found)->bound2() == 1) {
auto inst_or_ref = create_instance((*found)->entity_reference());
IfcUtil::IfcBaseClass* inst;
2024-08-23 20:29:07 +02:00
instance_to_attribute(inst_or_ref, 0, inst);
2023-09-17 12:30:17 +02:00
if (inst != nullptr) {
int idx = (*found)->entity_reference()->attribute_index(
(*found)->attribute_reference());
2025-02-27 22:07:31 +01:00
inst->set_attribute_value(idx, state->stack.back().inst());
2024-08-23 20:29:07 +02:00
state->stack.push_back(stack_node::instance(id, inst));
2023-09-17 12:30:17 +02:00
} else {
2026-06-11 21:04:44 +02:00
state->logger.Error("SYN", 37, "Unknown attribute " + tagname);
2023-09-17 12:30:17 +02:00
state->stack.push_back(state->stack.back());
}
} else {
state->stack.push_back(stack_node::inverse(state->stack.back().inst(), *found));
}
}
} else {
const IfcParse::parameter_type* attribute_type = current->attribute_by_index(idx)->type_of_attribute();
if (state->dialect == ifcxml_dialect_ifc2x3) {
follow_named(attribute_type);
if (attribute_type->as_aggregation_type() != nullptr) {
2023-09-17 12:30:17 +02:00
state->stack.push_back(stack_node::aggregate(state->stack.back().inst(), idx));
} else {
state->stack.push_back(stack_node::instance_attribute(state->stack.back().inst(), idx));
}
} else {
if (IfcUtil::from_parameter_type(attribute_type) == IfcUtil::Argument_ENTITY_INSTANCE) {
if (const auto* entity = attribute_type->as_named_type()->declared_type()->as_entity()) {
2023-09-17 12:30:17 +02:00
auto inst_or_reference = create_instance(entity);
2024-08-23 20:29:07 +02:00
IfcUtil::IfcBaseClass* inst;
instance_to_attribute(inst_or_reference, idx, inst);
// @todo
state->stack.back().inst();
state->stack.push_back(stack_node::instance(id, std::get<IfcUtil::IfcBaseClass*>(inst_or_reference)));
} else if (attribute_type->as_named_type()->declared_type()->as_select_type() != nullptr) {
2023-09-17 12:30:17 +02:00
// Select types cause an additional indirection, so the current stack node is simply repeated
state->stack.push_back(stack_node::select(state->stack.back().inst(), idx));
}
} else if (attribute_type->as_aggregation_type() != nullptr) {
2023-09-17 12:30:17 +02:00
state->stack.push_back(stack_node::aggregate(state->stack.back().inst(), idx));
}
}
}
}
} else if (state->file != nullptr) {
2023-09-17 12:30:17 +02:00
if (state_type == stack_node::node_header) {
state->stack.push_back(stack_node::header_entry(tagname));
} else if (tagname == "ex:iso_10303_28_header" || tagname == "header") {
state->stack.push_back(stack_node::header());
} else if (tagname == "uos") {
// intentially empty, ignored in end_element() as well
} else {
const IfcParse::declaration* decl = nullptr;
try {
decl = state->file->schema()->declaration_by_name(tagname);
} catch (const std::exception& e) {
2026-06-11 21:04:44 +02:00
state->logger.Error("SYN", 38, e);
2023-09-17 12:30:17 +02:00
}
if (decl == nullptr) {
2023-09-17 12:30:17 +02:00
goto end;
}
const IfcParse::entity* entity = decl->as_entity();
if ((entity == nullptr) && state_type != stack_node::node_instance_attribute) {
2026-06-11 21:04:44 +02:00
state->logger.Error("SYN", 39, "Not an entity definition " + tagname);
2023-09-17 12:30:17 +02:00
goto end;
}
auto inst_or_ref = create_instance(decl);
IfcUtil::IfcBaseClass* inst;
2024-08-23 20:29:07 +02:00
instance_to_attribute(inst_or_ref, state->stack.back().idx(), inst);
2023-09-17 12:30:17 +02:00
if (state_type == stack_node::node_inverse) {
int idx = state->stack.back().inv_attr()->entity_reference()->attribute_index(
state->stack.back().inv_attr()->attribute_reference());
if (inst != nullptr) {
2025-02-27 22:07:31 +01:00
inst->set_attribute_value(idx, state->stack.back().inst());
2023-09-17 12:30:17 +02:00
} else {
2026-06-11 21:04:44 +02:00
state->logger.Error("SYN", 40, "Internal error, inverse attribute not processed");
2023-09-17 12:30:17 +02:00
}
} else if (state_type == stack_node::node_instance_attribute) {
2025-02-27 22:07:31 +01:00
state->stack.back().inst()->set_attribute_value(state->stack.back().idx(), inst);
2023-09-17 12:30:17 +02:00
}
if (entity == nullptr) {
// Type declaration, immediately populate attr 0
state->stack.push_back(stack_node::instance_attribute(inst, 0));
} else {
2024-08-23 20:29:07 +02:00
state->stack.push_back(stack_node::instance(id, inst));
2023-09-17 12:30:17 +02:00
}
}
}
}
2018-08-29 12:56:35 +02:00
end:
#ifndef NDEBUG
2023-09-17 12:30:17 +02:00
std::cout << std::endl;
2018-08-29 12:56:35 +02:00
#endif
2023-09-17 12:30:17 +02:00
return;
2018-08-29 12:56:35 +02:00
}
2026-06-11 21:04:44 +02:00
IFC_PARSE_API IfcParse::IfcFile* IfcParse::parse_ifcxml(const std::string& filename, Logger& logger) {
2024-08-23 20:29:07 +02:00
throw std::runtime_error("IFC-XML import temporarily disabled");
2026-06-11 21:04:44 +02:00
ifcxml_parse_state state(logger);
2023-09-17 12:30:17 +02:00
xmlSAXHandler handler;
memset(&handler, 0, sizeof(xmlSAXHandler));
handler.startElement = start_element;
handler.endElement = end_element;
handler.characters = process_characters;
xmlSAXUserParseFile(&handler, &state, filename.c_str());
for (const auto& pair : state.forward_references) {
2024-08-23 20:29:07 +02:00
/*
2023-09-17 12:30:17 +02:00
auto it = state.idmap.find(pair.second);
if (it == state.idmap.end()) {
Logger::Error("Instance with id '" + pair.second + "' not encountered");
} else {
pair.first->set(state.file->instance_by_id(it->second));
}
2024-08-23 20:29:07 +02:00
*/
2023-09-17 12:30:17 +02:00
}
if (state.file != nullptr) {
2024-08-23 20:29:07 +02:00
// state.file->parsing_complete() = true;
2023-09-17 12:30:17 +02:00
state.file->build_inverses();
}
return state.file;
2018-08-29 12:56:35 +02:00
}
2023-09-17 12:30:17 +02:00
#endif // WITH_IFCXML