/********************************************************************************
* *
* 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);
}