/******************************************************************************** * * * 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 * * GNU Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public License * * along with this program. If not, see . * * * ********************************************************************************/ // This file was generated with the assistance of an AI coding tool. #include "pset.h" #include "../ifcparse/exception.h" #include "../ifcparse/file.h" #include "../ifcparse/instance_data.h" #include "schema_dispatch.i" #include #include namespace { template struct is_optional : std::false_type {}; template struct is_optional> : std::true_type {}; template struct is_ifc4_or_higher : std::false_type {}; template struct is_ifc4_or_higher> : std::true_type {}; template struct has_predefined_property_set : std::false_type {}; template struct has_predefined_property_set> : std::true_type {}; template struct has_set_point_value : std::false_type {}; template struct has_set_point_value().SetPointValue())>> : std::true_type {}; template struct has_curve_interpolation : std::false_type {}; template struct has_curve_interpolation().CurveInterpolation())>> : std::true_type {}; std::string schema_name(const express::base& instance) { return instance.declaration().schema()->name(); } [[noreturn]] void unsupported_schema(const std::string& name) { throw ifcopenshell::exception("No helper implementation was built for schema " + name); } property_value from_attribute(const ifcopenshell::attribute_value& value); template property_list scalar_list(const std::vector& values) { property_list result; result.reserve(values.size()); for (const auto& value : values) { result.emplace_back(value); } return result; } property_value from_attribute(const ifcopenshell::attribute_value& value) { if (value.isNull()) { return {}; } switch (value.type()) { case ifcopenshell::Argument_INT: return static_cast(value); case ifcopenshell::Argument_BOOL: return static_cast(value); case ifcopenshell::Argument_LOGICAL: { const boost::logic::tribool logical = value; if (boost::logic::indeterminate(logical)) { return "UNKNOWN"; } return static_cast(logical); } case ifcopenshell::Argument_DOUBLE: return static_cast(value); case ifcopenshell::Argument_STRING: return static_cast(value); case ifcopenshell::Argument_ENUMERATION: { const ifcopenshell::enumeration_reference enumeration = value; return enumeration.value() ? enumeration.value() : ""; } case ifcopenshell::Argument_BINARY: { const boost::dynamic_bitset<> bits = value; std::string result; boost::to_string(bits, result); return result; } case ifcopenshell::Argument_ENTITY_INSTANCE: { const express::base instance = value; if (instance && !instance.declaration().as_entity()) { return from_attribute(instance.get_attribute_value(0)); } return instance; } case ifcopenshell::Argument_AGGREGATE_OF_INT: return scalar_list(static_cast>(value)); case ifcopenshell::Argument_AGGREGATE_OF_DOUBLE: return scalar_list(static_cast>(value)); case ifcopenshell::Argument_AGGREGATE_OF_STRING: return scalar_list(static_cast>(value)); case ifcopenshell::Argument_AGGREGATE_OF_ENTITY_INSTANCE: { property_list result; for (const auto& instance : static_cast>(value)) { if (instance && !instance.declaration().as_entity()) { result.push_back(from_attribute(instance.get_attribute_value(0))); } else { result.emplace_back(instance); } } return result; } case ifcopenshell::Argument_EMPTY_AGGREGATE: return property_list{}; default: return {}; } } template property_value from_select(const Select& value) { if (!value) { return {}; } return from_attribute(value.concrete().get_attribute_value(0)); } template property_value from_select_values(const Values& maybe_values) { if constexpr (is_optional::value) { if (!maybe_values || maybe_values->empty()) { return {}; } return from_select_values(*maybe_values); } else { if (maybe_values.empty()) { return {}; } property_list result; result.reserve(maybe_values.size()); for (const auto& value : maybe_values) { result.push_back(from_select(value)); } return result; } } property_value verbose_value(const express::base& instance, property_value value, const std::optional& value_type = std::nullopt) { property_map result = { {"id", instance.id()}, {"class", instance.declaration().name()}, {"value", std::move(value)}, }; if (value_type) { result["value_type"] = *value_type; } return result; } template property_map get_properties_s(const std::vector& properties, bool verbose); template property_map get_quantities_s(const std::vector& quantities, bool verbose); template std::optional property_value_s(const typename Schema::IfcProperty& property, bool verbose) { property_value result; std::optional value_type; if (auto single = property.template as()) { const auto nominal = single.NominalValue(); result = from_select(nominal); if (nominal) { value_type = nominal.concrete().declaration().name(); } } else if (auto enumerated = property.template as()) { result = from_select_values(enumerated.EnumerationValues()); } else if (auto list = property.template as()) { result = from_select_values(list.ListValues()); } else if (auto bounded = property.template as()) { property_map data = { {"id", bounded.id()}, {"type", bounded.declaration().name()}, {"UpperBoundValue", from_select(bounded.UpperBoundValue())}, {"LowerBoundValue", from_select(bounded.LowerBoundValue())}, }; if constexpr (has_set_point_value::value) { data["SetPointValue"] = from_select(bounded.SetPointValue()); } result = std::move(data); } else if (auto table = property.template as()) { property_map data = { {"id", table.id()}, {"type", table.declaration().name()}, {"DefiningValues", from_select_values(table.DefiningValues())}, {"DefinedValues", from_select_values(table.DefinedValues())}, }; if (const auto expression = table.Expression()) { data["Expression"] = *expression; } if (const auto defining_unit = table.DefiningUnit()) { data["DefiningUnit"] = defining_unit.concrete(); } if (const auto defined_unit = table.DefinedUnit()) { data["DefinedUnit"] = defined_unit.concrete(); } if constexpr (has_curve_interpolation::value) { if (const auto interpolation = table.CurveInterpolation()) { data["CurveInterpolation"] = Schema::IfcCurveInterpolationEnum::ToString(*interpolation); } } result = std::move(data); } else if (auto complex = property.template as()) { result = property_map{ {"id", complex.id()}, {"type", complex.declaration().name()}, {"UsageName", complex.UsageName()}, {"properties", get_properties_s(complex.HasProperties(), verbose)}, }; } else { return std::nullopt; } if (verbose) { return verbose_value(property, std::move(result), value_type); } return result; } template std::optional get_property_s(const std::vector& properties, const std::string& name, bool verbose) { for (const auto& property : properties) { if (property.Name() == name) { return property_value_s(property, verbose); } } return std::nullopt; } template property_map get_properties_s(const std::vector& properties, bool verbose) { property_map result; for (const auto& property : properties) { if (auto value = property_value_s(property, verbose)) { result[property.Name()] = std::move(*value); } } return result; } template std::optional quantity_value_s(const typename Schema::IfcPhysicalQuantity& quantity, bool verbose) { property_value result; if (quantity.template as()) { result = from_attribute(quantity.get_attribute_value(3)); } else if (auto complex = quantity.template as()) { result = property_map{ {"id", complex.id()}, {"type", complex.declaration().name()}, {"Discrimination", complex.Discrimination()}, {"properties", get_quantities_s(complex.HasQuantities(), verbose)}, }; } else { return std::nullopt; } if (verbose) { return verbose_value(quantity, std::move(result)); } return result; } template std::optional get_quantity_s( const std::vector& quantities, const std::string& name, bool verbose) { for (const auto& quantity : quantities) { if (quantity.Name() == name) { return quantity_value_s(quantity, verbose); } } return std::nullopt; } template property_map get_quantities_s(const std::vector& quantities, bool verbose) { property_map result; for (const auto& quantity : quantities) { if (auto value = quantity_value_s(quantity, verbose)) { result[quantity.Name()] = std::move(*value); } } return result; } template std::optional predefined_properties_s( const typename Schema::IfcPreDefinedPropertySet& definition, const std::optional& property_name) { property_map result; const auto* declaration = definition.declaration().as_entity(); for (std::size_t index = 4; index < declaration->attribute_count(); ++index) { const auto* attribute = declaration->attribute_by_index(index); if (property_name && attribute->name() != *property_name) { continue; } const auto value = definition.get_attribute_value(index); if (value.isNull()) { continue; } if (property_name) { return from_attribute(value); } result[attribute->name()] = from_attribute(value); } if (property_name) { return std::nullopt; } result["id"] = definition.id(); return result; } template std::optional get_property_definition_s( const express::base& definition, const std::optional& property_name, bool verbose) { if (!definition) { return std::nullopt; } if (auto quantity = definition.template as()) { if (property_name) { return get_quantity_s(quantity.Quantities(), *property_name, verbose); } auto result = get_quantities_s(quantity.Quantities(), verbose); result["id"] = definition.id(); return result; } if (auto pset = definition.template as()) { if (property_name) { return get_property_s(pset.HasProperties(), *property_name, verbose); } auto result = get_properties_s(pset.HasProperties(), verbose); result["id"] = definition.id(); return result; } if constexpr (is_ifc4_or_higher::value) { if (auto extended = definition.template as()) { if (property_name) { return get_property_s(extended.Properties(), *property_name, verbose); } auto result = get_properties_s(extended.Properties(), verbose); result["id"] = definition.id(); return result; } } else { if (auto extended = definition.template as()) { if (property_name) { return get_property_s(extended.ExtendedProperties(), *property_name, verbose); } auto result = get_properties_s(extended.ExtendedProperties(), verbose); result["id"] = definition.id(); return result; } } if constexpr (has_predefined_property_set::value) { if (auto predefined = definition.template as()) { return predefined_properties_s(predefined, property_name); } } return std::nullopt; } template express::base concrete_definition(const Definition& definition) { if constexpr (std::is_base_of_v) { return definition.concrete(); } else { return definition; } } template bool definition_is_pset_s(const express::base& definition) { if (definition.template as()) { return true; } if constexpr (has_predefined_property_set::value) { if (definition.template as()) { return true; } } if constexpr (!is_ifc4_or_higher::value) { if (definition.template as()) { return true; } } return false; } template std::optional definition_name_s(const express::base& definition) { if (auto pset = definition.template as()) { return pset.Name(); } if (auto quantity = definition.template as()) { return quantity.Name(); } if constexpr (is_ifc4_or_higher::value) { if (auto extended = definition.template as()) { return extended.Name(); } } else { if (auto extended = definition.template as()) { return extended.Name(); } } if constexpr (has_predefined_property_set::value) { if (auto predefined = definition.template as()) { return predefined.Name(); } } return std::nullopt; } template void merge_definition_s(element_properties& result, const express::base& definition, bool psets_only, bool qtos_only, bool verbose) { const bool is_quantity = static_cast(definition.template as()); if (psets_only && !definition_is_pset_s(definition)) { return; } if (qtos_only && !is_quantity) { return; } const auto name = definition_name_s(definition); if (!name) { return; } auto values = get_property_definition_s(definition, std::nullopt, verbose); if (!values) { return; } const auto* properties = values->template get_if(); if (!properties) { return; } auto& destination = result[*name]; for (const auto& [property_name, value] : *properties) { destination.insert_or_assign(property_name, value); } } template typename Schema::IfcTypeObject get_type_s(const typename Schema::IfcObject& object) { if constexpr (is_ifc4_or_higher::value) { const auto relationships = object.IsTypedBy(); if (!relationships.empty()) { return relationships.front().RelatingType(); } } else { for (const auto& relationship : object.IsDefinedBy()) { if (auto by_type = relationship.template as()) { return by_type.RelatingType(); } } } return {}; } template element_properties get_psets_s(const express::base& element, bool psets_only, bool qtos_only, bool should_inherit, bool verbose) { element_properties result; if (auto type = element.template as()) { if (const auto definitions = type.HasPropertySets()) { for (const auto& definition : *definitions) { merge_definition_s(result, definition, psets_only, qtos_only, verbose); } } return result; } if constexpr (is_ifc4_or_higher::value) { if (auto material = element.template as()) { if (qtos_only) { return result; } for (const auto& definition : material.HasProperties()) { merge_definition_s(result, definition, false, false, verbose); } return result; } if (auto profile = element.template as()) { if (qtos_only) { return result; } for (const auto& definition : profile.HasProperties()) { merge_definition_s(result, definition, false, false, verbose); } return result; } } else { if (auto material = element.template as()) { if (qtos_only) { return result; } for (const auto& definition : element.file()->template instances_by_type()) { if (definition.Material() == material) { merge_definition_s(result, definition, false, false, verbose); } } return result; } if (element.template as()) { return result; } } if (auto object = element.template as()) { if (should_inherit) { if (const auto type = get_type_s(object)) { result = get_psets_s(type, psets_only, qtos_only, false, verbose); } } for (const auto& relationship : object.IsDefinedBy()) { if (auto by_properties = relationship.template as()) { merge_definition_s(result, concrete_definition(by_properties.RelatingPropertyDefinition()), psets_only, qtos_only, verbose); } } } return result; } template std::optional get_inherited_property_s(const express::base& element, const std::string& pset_name, const std::string& property_name, bool psets_only, bool qtos_only, bool verbose) { const auto object = element.template as(); if (!object) { return std::nullopt; } const auto type = get_type_s(object); if (!type) { return std::nullopt; } const auto psets = get_psets_s(type, psets_only, qtos_only, false, verbose); const auto pset = psets.find(pset_name); if (pset == psets.end()) { return std::nullopt; } const auto property = pset->second.find(property_name); if (property == pset->second.end()) { return std::nullopt; } return property->second; } template std::vector cast_entities(const std::vector& values) { std::vector result; result.reserve(values.size()); for (const auto& value : values) { if (auto typed = value.template as()) { result.push_back(typed); } } return result; } void print_value(std::ostream& stream, const property_value& value) { std::visit( [&stream](const auto& item) { using T = std::decay_t; if constexpr (std::is_same_v) { stream << "null"; } else if constexpr (std::is_same_v) { stream << (item ? "true" : "false"); } else if constexpr (std::is_same_v) { if (item) { item.to_string(stream); } else { stream << "null"; } } else if constexpr (std::is_same_v) { stream << '['; bool first = true; for (const auto& child : item) { if (!first) { stream << ", "; } first = false; print_value(stream, child); } stream << ']'; } else if constexpr (std::is_same_v) { stream << '{'; bool first = true; for (const auto& [name, child] : item) { if (!first) { stream << ", "; } first = false; stream << name << ": "; print_value(stream, child); } stream << '}'; } else { stream << item; } }, value.value); } } // namespace std::ostream& operator<<(std::ostream& stream, const property_value& value) { print_value(stream, value); return stream; } element_properties get_psets(const express::base& element, bool psets_only, bool qtos_only, bool should_inherit, bool verbose) { if (!element) { return {}; } const auto name = schema_name(element); #define IFCOPENSHELL_DISPATCH(Schema, Identifier) \ if (name == Identifier) { \ return get_psets_s(element, psets_only, qtos_only, should_inherit, verbose); \ } IFCOPENSHELL_HELPER_FOR_EACH_SCHEMA(IFCOPENSHELL_DISPATCH) #undef IFCOPENSHELL_DISPATCH unsupported_schema(name); } std::optional get_pset(const express::base& element, const std::string& name, const std::optional& property_name, bool psets_only, bool qtos_only, bool should_inherit, bool verbose) { auto psets = get_psets(element, psets_only, qtos_only, should_inherit, verbose); const auto set = psets.find(name); if (set == psets.end()) { return std::nullopt; } if (!property_name) { return set->second; } const auto property = set->second.find(*property_name); if (property == set->second.end()) { return std::nullopt; } if (property->second.is_null() && should_inherit) { const auto schema = schema_name(element); #define IFCOPENSHELL_DISPATCH(Schema, Identifier) \ if (schema == Identifier) { \ if (auto inherited = get_inherited_property_s(element, name, *property_name, psets_only, qtos_only, verbose)) { \ return inherited; \ } \ } IFCOPENSHELL_HELPER_FOR_EACH_SCHEMA(IFCOPENSHELL_DISPATCH) #undef IFCOPENSHELL_DISPATCH } return property->second; } std::optional get_property_definition( const express::base& definition, const std::optional& property_name, bool verbose) { if (!definition) { return std::nullopt; } const auto name = schema_name(definition); #define IFCOPENSHELL_DISPATCH(Schema, Identifier) \ if (name == Identifier) \ return get_property_definition_s(definition, property_name, verbose); IFCOPENSHELL_HELPER_FOR_EACH_SCHEMA(IFCOPENSHELL_DISPATCH) #undef IFCOPENSHELL_DISPATCH unsupported_schema(name); } std::optional get_quantity(const std::vector& quantities, const std::string& name, bool verbose) { if (quantities.empty()) { return std::nullopt; } const auto schema = schema_name(quantities.front()); #define IFCOPENSHELL_DISPATCH(Schema, Identifier) \ if (schema == Identifier) { \ return get_quantity_s(cast_entities(quantities), \ name, \ verbose); \ } IFCOPENSHELL_HELPER_FOR_EACH_SCHEMA(IFCOPENSHELL_DISPATCH) #undef IFCOPENSHELL_DISPATCH unsupported_schema(schema); } property_map get_quantities(const std::vector& quantities, bool verbose) { if (quantities.empty()) { return {}; } const auto schema = schema_name(quantities.front()); #define IFCOPENSHELL_DISPATCH(Schema, Identifier) \ if (schema == Identifier) { \ return get_quantities_s(cast_entities(quantities), \ verbose); \ } IFCOPENSHELL_HELPER_FOR_EACH_SCHEMA(IFCOPENSHELL_DISPATCH) #undef IFCOPENSHELL_DISPATCH unsupported_schema(schema); } std::optional get_property(const std::vector& properties, const std::string& name, bool verbose) { if (properties.empty()) { return std::nullopt; } const auto schema = schema_name(properties.front()); #define IFCOPENSHELL_DISPATCH(Schema, Identifier) \ if (schema == Identifier) { \ return get_property_s(cast_entities(properties), \ name, \ verbose); \ } IFCOPENSHELL_HELPER_FOR_EACH_SCHEMA(IFCOPENSHELL_DISPATCH) #undef IFCOPENSHELL_DISPATCH unsupported_schema(schema); } property_map get_properties(const std::vector& properties, bool verbose) { if (properties.empty()) { return {}; } const auto schema = schema_name(properties.front()); #define IFCOPENSHELL_DISPATCH(Schema, Identifier) \ if (schema == Identifier) { \ return get_properties_s(cast_entities(properties), \ verbose); \ } IFCOPENSHELL_HELPER_FOR_EACH_SCHEMA(IFCOPENSHELL_DISPATCH) #undef IFCOPENSHELL_DISPATCH unsupported_schema(schema); }