diff --git a/src/ifcopenshell-python/ifcopenshell/validate.py b/src/ifcopenshell-python/ifcopenshell/validate.py index 5de6ae2f77..60a6090a6c 100644 --- a/src/ifcopenshell-python/ifcopenshell/validate.py +++ b/src/ifcopenshell-python/ifcopenshell/validate.py @@ -256,11 +256,12 @@ def assert_valid( return True -def log_internal_cpp_errors(filename: str, logger: Logger) -> None: +def log_internal_cpp_errors(f: ifcopenshell.file, filename: str, logger: Logger) -> None: import re import bisect chr_offset_re = re.compile(r"at offset (\d+)\s*") + for_instance_re = re.compile(r"\s*for instance #(\d+)\s*") log = ifcopenshell.get_log() msgs = list(map(json.loads, filter(None, log.split("\n")))) @@ -286,6 +287,23 @@ def log_internal_cpp_errors(filename: str, logger: Logger) -> None: else: logger.error("For instance:\n %s\n%s", line, m) + instance_messages = [for_instance_re.findall(m["message"]) for m in msgs] + if instance_messages: + for instid, msg in zip(instance_messages, msgs): + if instid: + m = for_instance_re.sub("", msg["message"]) + try: + inst = f[int(instid[0])] + except: + inst = None + if hasattr(logger, "set_state"): + logger.set_state("instance", inst) + logger.set_state("attribute", None) + logger.error(m) + elif inst: + logger.error("For instance:\n %s\n%s", inst, m) + else: + logger.error(m) entity_attribute_map: dict[tuple[str, str], tuple[entity_type, tuple[attribute]]] = {} @@ -368,7 +386,7 @@ def validate(f: Union[ifcopenshell.file, str], logger: Logger, express_rules=Fal logger.error(f"Unsupported schema: {schema_name}") return - log_internal_cpp_errors(filename, logger) + log_internal_cpp_errors(f, filename, logger) schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(f.schema_identifier) used_guids: dict[str, ifcopenshell.entity_instance] = dict() @@ -490,7 +508,7 @@ def validate(f: Union[ifcopenshell.file, str], logger: Logger, express_rules=Fal # Re capturing the log when validate() is finished # iterating over every instance so that all attribute counts # are verified. - log_internal_cpp_errors(filename, logger) + log_internal_cpp_errors(f, filename, logger) # Restore the original value for 'use_attribute_value_derived' ifcopenshell.ifcopenshell_wrapper.set_feature("use_attribute_value_derived", attribute_value_derived_org) diff --git a/src/ifcparse/IfcFile.cpp b/src/ifcparse/IfcFile.cpp index 6228884120..c28f215f6c 100644 --- a/src/ifcparse/IfcFile.cpp +++ b/src/ifcparse/IfcFile.cpp @@ -180,7 +180,7 @@ namespace { } } -IfcEntityInstanceData IfcParse::parse_context::construct(int name, unresolved_references& references_to_resolve, const IfcParse::declaration* decl) { +IfcEntityInstanceData IfcParse::parse_context::construct(int name, unresolved_references& references_to_resolve, const IfcParse::declaration* decl, boost::optional expected_size) { std::vector parameter_types; if ((decl != nullptr) && (decl->as_type_declaration() != nullptr)) { @@ -197,8 +197,11 @@ IfcEntityInstanceData IfcParse::parse_context::construct(int name, unresolved_re ); } - if ((decl != nullptr) && (tokens_.size() != parameter_types.size())) { - // warning + if (((decl != nullptr) && (tokens_.size() != parameter_types.size())) || + expected_size && *expected_size != tokens_.size()) + { + size_t expected = expected_size ? *expected_size : parameter_types.size(); + Logger::Warning("Expected " + std::to_string(expected) + " attribute values, found " + std::to_string(tokens_.size()) + " for instance #" + std::to_string(name)); } if (tokens_.empty()) { diff --git a/src/ifcparse/IfcFile.h b/src/ifcparse/IfcFile.h index 79357b4241..4d663eeb08 100644 --- a/src/ifcparse/IfcFile.h +++ b/src/ifcparse/IfcFile.h @@ -91,7 +91,7 @@ struct parse_context { void push(IfcUtil::IfcBaseClass* inst); - IfcEntityInstanceData construct(int name, unresolved_references& references_to_resolve, const IfcParse::declaration* decl); + IfcEntityInstanceData construct(int name, unresolved_references& references_to_resolve, const IfcParse::declaration* decl, boost::optional expected_size); }; /// This class provides several static convenience functions and variables diff --git a/src/ifcparse/IfcParse.cpp b/src/ifcparse/IfcParse.cpp index 403809b5ac..8826ea9c4c 100644 --- a/src/ifcparse/IfcParse.cpp +++ b/src/ifcparse/IfcParse.cpp @@ -719,7 +719,7 @@ void IfcParse::IfcFile::load(unsigned entity_instance_name, const IfcParse::enti tokens->Next(); load(0, nullptr, ps, -1); const auto *decl = schema_->declaration_by_name(TokenFunc::asStringRef(next)); - auto* simple_type_instance = schema_->instantiate(decl, ps.construct(-1, references_to_resolve, decl)); + auto* simple_type_instance = schema_->instantiate(decl, ps.construct(-1, references_to_resolve, decl, boost::none)); //@todo decide addEntity(((IfcUtil::IfcBaseClass*)*entity)); context.push(simple_type_instance); simple_type_instance->file_ = this; @@ -748,7 +748,7 @@ IfcEntityInstanceData IfcParse::read(unsigned int i, IfcFile* f) { parse_context pc; f->tokens->Next(); f->load(i, ty->as_entity(), pc, -1); - return IfcEntityInstanceData(pc.construct(i, f->references_to_resolve, ty)); + return IfcEntityInstanceData(pc.construct(i, f->references_to_resolve, ty, boost::none)); } void IfcParse::IfcFile::try_read_semicolon() const { @@ -1300,7 +1300,7 @@ void IfcFile::initialize_(IfcParse::IfcSpfStream* s) { parse_context ps; tokens->Next(); load(current_id, entity_type->as_entity(), ps, -1); - instance = schema_->instantiate(entity_type, ps.construct(current_id, references_to_resolve, entity_type)); + instance = schema_->instantiate(entity_type, ps.construct(current_id, references_to_resolve, entity_type, boost::none)); instance->file_ = this; instance->id_ = current_id; diff --git a/src/ifcparse/IfcSpfHeader.cpp b/src/ifcparse/IfcSpfHeader.cpp index aaebe0a4c1..6fb7c0bce0 100644 --- a/src/ifcparse/IfcSpfHeader.cpp +++ b/src/ifcparse/IfcSpfHeader.cpp @@ -41,12 +41,7 @@ namespace { parse_context pc; f->tokens->Next(); f->load(-1, nullptr, pc, -1); - return pc.construct(-1, f->references_to_resolve, nullptr); - /*std::ostringstream oss; - ent.toString(oss); - auto osss = oss.str(); - std::wcout << osss.c_str() << std::endl;*/ - // return ent; + return pc.construct(-1, f->references_to_resolve, nullptr, s); } }