Files
2026-05-31 10:17:09 +07:00

445 lines
20 KiB
Python

# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Thomas Krijnen <thomas@aecgeeks.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 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 IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import codegen
import templates
from schema import OrderedCaseInsensitiveDict
from header import USE_VIRTUAL_INHERITANCE
class Implementation(codegen.Base):
def __init__(self, mapping):
enumeration_functions = []
entity_implementations = []
schema_entity_statements = []
schema_name = mapping.schema.name.capitalize()
schema_name_upper = mapping.schema.name.upper()
stringify = lambda s: '"%s"' % s
cat = lambda vs: "".join(vs)
catc = lambda vs: ", ".join(vs)
catnl = lambda vs: "\n".join(vs)
cator = lambda vs: " || ".join(vs)
nl = lambda s: "%s\n" % s if len(s) else s
write = lambda str, **kwargs: enumeration_functions.append(str % kwargs)
collections_by_type = (
("entity", mapping.schema.entities),
("type_declaration", mapping.schema.simpletypes),
("select_type", mapping.schema.selects),
("enumeration_type", mapping.schema.enumerations),
)
self.names = []
for _, collection in collections_by_type:
self.names.extend(collection.keys())
self.names.sort(key=str.lower)
for name, enum in mapping.schema.enumerations.items():
short_name = name[:-4] if name.endswith("Enum") else name
context = locals()
write(
templates.enumeration_function,
max_id=len(enum.values),
name=name,
schema_name=schema_name,
schema_name_upper=schema_name_upper,
index_in_schema=self.names.index(str(name)),
values=catc(map(stringify, enum.values)),
from_string_statements=catnl(
templates.enum_from_string_stmt % dict(context, **locals()) for value in enum.values
),
)
if USE_VIRTUAL_INHERITANCE:
for name, enum in mapping.schema.selects.items():
write(
templates.select_function,
name=name,
schema_name=schema_name,
schema_name_upper=schema_name_upper,
index_in_schema=self.names.index(str(name)),
)
write = lambda str, **kwargs: entity_implementations.append(str % kwargs)
for name, type in mapping.schema.entities.items():
parent_type_test = (
""
if not type.supertypes or len(type.supertypes) != 1
else templates.parent_type_test % (type.supertypes[0])
)
constructor_arguments = mapping.get_assignable_arguments(type, include_derived=True)
constructor_arguments_str = catc(
"%(full_type)s v%(index)d_%(name)s" % a for a in constructor_arguments if not a["is_derived"]
)
attributes = []
constructor_implementations = []
write_attr = lambda str, **kwargs: attributes.append(str % kwargs)
for arg in constructor_arguments:
if not arg["is_inherited"] and not arg["is_derived"]:
def find_template(arg):
simple = mapping.schema.is_simpletype(arg["list_instance_type"])
select = arg["list_instance_type"] == "IfcUtil::IfcBaseClass"
express = (
mapping.flatten_type_string(arg["list_instance_type"]) in mapping.express_to_cpp_typemapping
)
if arg["is_enum"]:
return templates.get_attr_stmt_enum
elif arg["is_nested"] and arg["is_templated_list"]:
return templates.get_attr_stmt_nested_array
elif arg["is_templated_list"] and not (select or simple or express):
return templates.get_attr_stmt_array
elif arg["non_optional_type"].endswith("*"):
return templates.get_attr_stmt_entity
else:
return templates.get_attr_stmt
null_check = ""
if arg["is_optional"]:
attr_check = "if(get_attribute_value(%d).isNull()) { return %%s; }" % (arg["index"] - 1,)
if "boost::optional" in arg["full_type"]:
null_check = attr_check % "boost::none"
else:
null_check = attr_check % "nullptr"
tmpl = find_template(arg)
write_attr(
templates.const_function,
class_name=name,
name=arg["name"],
arguments="",
schema_name=schema_name,
schema_name_upper=schema_name_upper,
return_type=arg["full_type"],
body=tmpl
% {
"index": arg["index"] - 1,
"type": arg["full_type"].replace("::Value", ""),
"non_optional_type": arg["non_optional_type"].replace("::Value", ""),
"non_optional_type_no_pointer": arg["non_optional_type"]
.replace("::Value", "")
.replace("*", ""),
"list_instance_type": arg["list_instance_type"],
"null_check": null_check,
},
)
def find_template(arg):
simple = mapping.schema.is_simpletype(arg["list_instance_type"])
select = arg["list_instance_type"] == "IfcUtil::IfcBaseClass"
express = arg["list_instance_type"] in mapping.express_to_cpp_typemapping
if arg["is_enum"]:
return templates.set_attr_stmt_enum
elif arg["is_templated_list"] and not (select or simple or express):
return templates.set_attr_stmt_array
elif arg["full_type"].endswith("*"):
return templates.set_attr_instance
else:
return templates.set_attr_stmt
tmpl = find_template(arg)
write_attr(
templates.function,
class_name=name,
name="set%s" % arg["name"],
arguments="%s v" % arg["full_type"],
return_type="void",
schema_name=schema_name,
schema_name_upper=schema_name_upper,
body=tmpl
% {
"index": arg["index"] - 1,
"type": arg["full_type"].replace("::Value", ""),
"non_optional_type": arg["non_optional_type"].replace("::Value", ""),
"star_if_optional": "*" if "boost::optional" in arg["full_type"] else "",
"check_optional_set_begin": "if (v) {" if "boost::optional" in arg["full_type"] else "",
"check_optional_set_else": (
"} else {" if "boost::optional" in arg["full_type"] else "if constexpr (false)"
),
"check_optional_set_end": "}" if "boost::optional" in arg["full_type"] else "",
},
)
if arg["is_derived"]:
constructor_implementations.append(templates.constructor_stmt_derived % {"index": arg["index"] - 1})
else:
is_optional_non_naked_ptr = arg["is_optional"] and not arg["non_optional_type"].endswith("*")
arg_name = "v%(index)d_%(name)s" % arg
deref_name = ("*%s" % arg_name) if is_optional_non_naked_ptr else arg_name
tmpl = (
templates.constructor_stmt_array
if arg["is_templated_list"]
else (
templates.constructor_stmt_enum
if arg["is_enum"]
else (
templates.constructor_stmt_instance
if arg["full_type"].endswith("*")
else templates.constructor_stmt
)
)
)
impl = tmpl % {
"name": deref_name,
"index": arg["index"] - 1,
"type": arg["non_optional_type"].replace("::Value", ""),
}
if is_optional_non_naked_ptr:
impl = templates.constructor_stmt_optional % {
"name": arg_name,
"index": arg["index"] - 1,
"stmt": impl,
}
constructor_implementations.append(impl)
def get_attribute_index(entity, attr_name):
related_entity = mapping.schema.entities[entity]
return [
a["name"].lower() for a in mapping.get_assignable_arguments(related_entity, include_derived=True)
].index(attr_name.lower())
inverse = [
templates.const_function
% {
"class_name": name,
"schema_name": schema_name,
"schema_name_upper": schema_name_upper,
"name": i.name,
"arguments": "",
"return_type": "::%s::%s::list::ptr" % (schema_name, i.entity),
"body": templates.get_inverse
% {
"type": i.entity,
"type_index": self.names.index(i.entity),
"index": get_attribute_index(i.entity, i.attribute),
"schema_name": schema_name,
"schema_name_upper": schema_name_upper,
},
}
for i in type.inverse
]
superclass = (
"%s(std::move(e))" % type.supertypes[0]
if len(type.supertypes) == 1
else "IfcUtil::IfcBaseEntity(std::move(e))"
)
superclass_num_attrs = (
"%s(IfcEntityInstanceData(in_memory_attribute_storage(%%d)))" % type.supertypes[0]
if len(type.supertypes) == 1
else "IfcUtil::IfcBaseEntity(IfcEntityInstanceData(in_memory_attribute_storage(%d)))"
) % len(constructor_arguments)
write(
templates.entity_implementation,
name=name,
parent_type_test=parent_type_test,
constructor_arguments=constructor_arguments_str,
constructor_implementation=cat(constructor_implementations),
attributes=nl(catnl(attributes)),
inverse=nl(catnl(inverse)),
superclass=superclass,
superclass_num_attrs=superclass_num_attrs,
schema_name=schema_name,
schema_name_upper=schema_name_upper,
index_in_schema=self.names.index(str(name)),
)
selectable_simple_types = sorted(
set(sum([b.values for a, b in mapping.schema.selects.items()], []))
& set(map(str, mapping.schema.types.keys()))
)
schema_entity_statements += [
templates.schema_entity_stmt % locals() for name, type in mapping.schema.simpletypes.items()
]
schema_entity_statements += [
templates.schema_entity_stmt % locals() for name, type in mapping.schema.enumerations.items()
]
schema_entity_statements += [
templates.schema_entity_stmt % locals() for name, type in mapping.schema.entities.items()
]
enumerable_types = sorted(
set(
[name for name, type in mapping.schema.types.items()]
+ [name for name, type in mapping.schema.entities.items()]
)
)
max_len = max(map(len, enumerable_types))
type_name_strings = catc(map(stringify, enumerable_types))
string_map_statements = [
templates.string_map_statement
% {"uppercase_name": name.upper(), "name": name, "padding": " " * (max_len - len(name))}
for name in enumerable_types
]
enumeration_index_by_str = OrderedCaseInsensitiveDict((j, i) for i, j in enumerate(enumerable_types))
def get_parent_id(s):
e = mapping.schema.entities.get(s)
if e and e.supertypes:
return enumeration_index_by_str[e.supertypes[0]]
else:
return -1
parent_type_statements = ",".join(map(str, map(get_parent_id, enumerable_types)))
max_id = len(enumerable_types)
simple_type_statements = cator("v == Type::%s" % name for name in selectable_simple_types)
simple_type_impl = []
for class_name, type in mapping.schema.simpletypes.items():
type_str = mapping.make_type_string(mapping.flatten_type_string(type))
attr_type = mapping.make_argument_type(type)
superclass = mapping.simple_type_parent(class_name) or "IfcUtil::IfcBaseType"
simpletype_impl_is = (
templates.simpletype_impl_is_with_supertype
if superclass
else templates.simpletype_impl_is_without_supertype
)
constructor = templates.constructor_single_initlist # if superclass else templates.constructor
simpletype_impl_cast = (
templates.simpletype_impl_cast_templated
if mapping.is_templated_list(type)
else templates.simpletype_impl_cast
)
simpletype_impl_constructor = (
templates.simpletype_impl_constructor_templated
if mapping.is_templated_list(type)
else templates.simpletype_impl_constructor
)
def compose(params, schema_name=schema_name, schema_name_upper=schema_name_upper):
class_name, attr_type, superclass, superclass_init, name, tmpl, return_type, args, body = params
index_in_schema = self.names.index(str(class_name))
underlying_type = mapping.list_instance_type(type)
arguments = ",".join(args)
body = body % locals()
return tmpl % locals()
simple_type_impl.append(templates.simpletype_impl_comment % {"name": class_name})
simple_type_impl.extend(
map(
compose,
map(
lambda x: (class_name, attr_type, superclass) + x,
(
(
"",
"Class",
templates.function,
"const IfcParse::type_declaration&",
(),
templates.simpletype_impl_class,
),
(
"",
"declaration",
templates.const_function,
"const IfcParse::type_declaration&",
(),
templates.simpletype_impl_declaration,
),
(
"std::move(e)",
"",
constructor,
"",
("IfcEntityInstanceData&& e",),
"",
),
(
(
"",
"",
constructor,
"",
("%s v" % type_str,),
(
"set_attribute_value(0, v%s);"
% ("->generalize()" if mapping.is_templated_list(type) else "")
),
)
if mapping.simple_type_parent(class_name) is None
else ("v", "", constructor, "", ("%s v" % type_str,), "")
),
("", "", templates.cast_function, type_str, (), simpletype_impl_cast),
),
),
)
)
simple_type_impl.append("")
"""
external_definitions = (
[("extern entity* %s_%%s_type;" % schema_name_upper) % n for n in mapping.schema.entities.keys()]
+ [
("extern type_declaration* %s_%%s_type;" % schema_name_upper) % n
for n in mapping.schema.simpletypes.keys()
]
+ [
("extern enumeration_type* %s_%%s_type;" % schema_name_upper) % n
for n in mapping.schema.enumerations.keys()
]
+ [
("extern select_type* %s_%%s_type;" % schema_name_upper) % n
for n in mapping.schema.selects.keys()
]
)
"""
external_definitions = ["extern declaration* %s_types[%d];" % (schema_name_upper, len(self.names))]
self.str = templates.implementation % {
"schema_name_upper": schema_name_upper,
"schema_name": schema_name,
"max_id": max_id,
"enumeration_functions": cat(enumeration_functions),
"schema_entity_statements": catnl(schema_entity_statements),
"type_name_strings": type_name_strings,
"string_map_statements": catnl(string_map_statements),
"simple_type_statement": simple_type_statements,
"parent_type_statements": parent_type_statements,
"entity_implementations": catnl(entity_implementations),
"simple_type_impl": catnl(simple_type_impl),
"external_definitions": catnl(external_definitions),
}
self.schema_name = mapping.schema.name.capitalize()
self.file_name = "%s.cpp" % self.schema_name
def __repr__(self):
return self.str
Generator = Implementation