445 lines
20 KiB
Python
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
|