diff --git a/src/ifcopenshell-python/ifcopenshell/express/rule_compiler.py b/src/ifcopenshell-python/ifcopenshell/express/rule_compiler.py index 38fa867778..21c85132f8 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/rule_compiler.py +++ b/src/ifcopenshell-python/ifcopenshell/express/rule_compiler.py @@ -939,10 +939,27 @@ def typeof(inst): # If V evaluates to indeterminate (?), an empty set is returned. return express_set([]) schema_name = inst.is_a(True).split('.')[0].lower() + schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name) + # Per ISO 10303-11 12.4.3, TYPEOF() also includes every select data type + # that references (transitively) any of the value's types as a member of + # its select list. Cache, per schema, the reverse map type name -> select + # types directly referencing it. + select_parents = getattr(typeof, '_select_parents', None) + if select_parents is None: + select_parents = typeof._select_parents = {} + schema_selects = select_parents.get(schema_name) + if schema_selects is None: + schema_selects = {} + for decl in schema.declarations(): + if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.select_type): + sel_name = decl.name().lower() + for member in decl.select_list(): + schema_selects.setdefault(member.name().lower(), []).append(sel_name) + select_parents[schema_name] = schema_selects def inner(): - decl = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name).declaration_by_name(inst.is_a()) + decl = schema.declaration_by_name(inst.is_a()) while decl: - yield '.'.join((schema_name, decl.name().lower())) + yield decl.name().lower() if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.entity): decl = decl.supertype() else: @@ -951,7 +968,15 @@ def typeof(inst): decl = decl.declared_type() if not isinstance(decl, ifcopenshell.ifcopenshell_wrapper.type_declaration): break - return express_set(inner()) + names = set(inner()) + queue = list(names) + while queue: + name = queue.pop() + for sel_name in schema_selects.get(name, ()): + if sel_name not in names: + names.add(sel_name) + queue.append(sel_name) + return express_set('.'.join((schema_name, name)) for name in names) class indeterminate_type: def __bool__(self): diff --git a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC2X3.py b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC2X3.py index bdcf863e48..83968563b8 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC2X3.py +++ b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC2X3.py @@ -96,11 +96,24 @@ def typeof(inst): if not inst: return express_set([]) schema_name = inst.is_a(True).split('.')[0].lower() + schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name) + select_parents = getattr(typeof, '_select_parents', None) + if select_parents is None: + select_parents = typeof._select_parents = {} + schema_selects = select_parents.get(schema_name) + if schema_selects is None: + schema_selects = {} + for decl in schema.declarations(): + if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.select_type): + sel_name = decl.name().lower() + for member in decl.select_list(): + schema_selects.setdefault(member.name().lower(), []).append(sel_name) + select_parents[schema_name] = schema_selects def inner(): - decl = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name).declaration_by_name(inst.is_a()) + decl = schema.declaration_by_name(inst.is_a()) while decl: - yield '.'.join((schema_name, decl.name().lower())) + yield decl.name().lower() if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.entity): decl = decl.supertype() else: @@ -109,7 +122,15 @@ def typeof(inst): decl = decl.declared_type() if not isinstance(decl, ifcopenshell.ifcopenshell_wrapper.type_declaration): break - return express_set(inner()) + names = set(inner()) + queue = list(names) + while queue: + name = queue.pop() + for sel_name in schema_selects.get(name, ()): + if sel_name not in names: + names.add(sel_name) + queue.append(sel_name) + return express_set('.'.join((schema_name, name)) for name in names) class indeterminate_type: diff --git a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4.py b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4.py index 7b931af2e9..2014111f77 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4.py +++ b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4.py @@ -96,11 +96,24 @@ def typeof(inst): if not inst: return express_set([]) schema_name = inst.is_a(True).split('.')[0].lower() + schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name) + select_parents = getattr(typeof, '_select_parents', None) + if select_parents is None: + select_parents = typeof._select_parents = {} + schema_selects = select_parents.get(schema_name) + if schema_selects is None: + schema_selects = {} + for decl in schema.declarations(): + if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.select_type): + sel_name = decl.name().lower() + for member in decl.select_list(): + schema_selects.setdefault(member.name().lower(), []).append(sel_name) + select_parents[schema_name] = schema_selects def inner(): - decl = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name).declaration_by_name(inst.is_a()) + decl = schema.declaration_by_name(inst.is_a()) while decl: - yield '.'.join((schema_name, decl.name().lower())) + yield decl.name().lower() if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.entity): decl = decl.supertype() else: @@ -109,7 +122,15 @@ def typeof(inst): decl = decl.declared_type() if not isinstance(decl, ifcopenshell.ifcopenshell_wrapper.type_declaration): break - return express_set(inner()) + names = set(inner()) + queue = list(names) + while queue: + name = queue.pop() + for sel_name in schema_selects.get(name, ()): + if sel_name not in names: + names.add(sel_name) + queue.append(sel_name) + return express_set('.'.join((schema_name, name)) for name in names) class indeterminate_type: diff --git a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X1.py b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X1.py index b07e9a2234..99c161a33d 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X1.py +++ b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X1.py @@ -96,11 +96,24 @@ def typeof(inst): if not inst: return express_set([]) schema_name = inst.is_a(True).split('.')[0].lower() + schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name) + select_parents = getattr(typeof, '_select_parents', None) + if select_parents is None: + select_parents = typeof._select_parents = {} + schema_selects = select_parents.get(schema_name) + if schema_selects is None: + schema_selects = {} + for decl in schema.declarations(): + if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.select_type): + sel_name = decl.name().lower() + for member in decl.select_list(): + schema_selects.setdefault(member.name().lower(), []).append(sel_name) + select_parents[schema_name] = schema_selects def inner(): - decl = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name).declaration_by_name(inst.is_a()) + decl = schema.declaration_by_name(inst.is_a()) while decl: - yield '.'.join((schema_name, decl.name().lower())) + yield decl.name().lower() if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.entity): decl = decl.supertype() else: @@ -109,7 +122,15 @@ def typeof(inst): decl = decl.declared_type() if not isinstance(decl, ifcopenshell.ifcopenshell_wrapper.type_declaration): break - return express_set(inner()) + names = set(inner()) + queue = list(names) + while queue: + name = queue.pop() + for sel_name in schema_selects.get(name, ()): + if sel_name not in names: + names.add(sel_name) + queue.append(sel_name) + return express_set('.'.join((schema_name, name)) for name in names) class indeterminate_type: diff --git a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X2.py b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X2.py index 07f6a261e7..6bd1f6039c 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X2.py +++ b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X2.py @@ -96,11 +96,24 @@ def typeof(inst): if not inst: return express_set([]) schema_name = inst.is_a(True).split('.')[0].lower() + schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name) + select_parents = getattr(typeof, '_select_parents', None) + if select_parents is None: + select_parents = typeof._select_parents = {} + schema_selects = select_parents.get(schema_name) + if schema_selects is None: + schema_selects = {} + for decl in schema.declarations(): + if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.select_type): + sel_name = decl.name().lower() + for member in decl.select_list(): + schema_selects.setdefault(member.name().lower(), []).append(sel_name) + select_parents[schema_name] = schema_selects def inner(): - decl = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name).declaration_by_name(inst.is_a()) + decl = schema.declaration_by_name(inst.is_a()) while decl: - yield '.'.join((schema_name, decl.name().lower())) + yield decl.name().lower() if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.entity): decl = decl.supertype() else: @@ -109,7 +122,15 @@ def typeof(inst): decl = decl.declared_type() if not isinstance(decl, ifcopenshell.ifcopenshell_wrapper.type_declaration): break - return express_set(inner()) + names = set(inner()) + queue = list(names) + while queue: + name = queue.pop() + for sel_name in schema_selects.get(name, ()): + if sel_name not in names: + names.add(sel_name) + queue.append(sel_name) + return express_set('.'.join((schema_name, name)) for name in names) class indeterminate_type: diff --git a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3.py b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3.py index 63389eba70..1e389997a6 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3.py +++ b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3.py @@ -96,11 +96,24 @@ def typeof(inst): if not inst: return express_set([]) schema_name = inst.is_a(True).split('.')[0].lower() + schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name) + select_parents = getattr(typeof, '_select_parents', None) + if select_parents is None: + select_parents = typeof._select_parents = {} + schema_selects = select_parents.get(schema_name) + if schema_selects is None: + schema_selects = {} + for decl in schema.declarations(): + if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.select_type): + sel_name = decl.name().lower() + for member in decl.select_list(): + schema_selects.setdefault(member.name().lower(), []).append(sel_name) + select_parents[schema_name] = schema_selects def inner(): - decl = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name).declaration_by_name(inst.is_a()) + decl = schema.declaration_by_name(inst.is_a()) while decl: - yield '.'.join((schema_name, decl.name().lower())) + yield decl.name().lower() if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.entity): decl = decl.supertype() else: @@ -109,7 +122,15 @@ def typeof(inst): decl = decl.declared_type() if not isinstance(decl, ifcopenshell.ifcopenshell_wrapper.type_declaration): break - return express_set(inner()) + names = set(inner()) + queue = list(names) + while queue: + name = queue.pop() + for sel_name in schema_selects.get(name, ()): + if sel_name not in names: + names.add(sel_name) + queue.append(sel_name) + return express_set('.'.join((schema_name, name)) for name in names) class indeterminate_type: diff --git a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_ADD1.py b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_ADD1.py index 67bba82f2b..459edbcfce 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_ADD1.py +++ b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_ADD1.py @@ -96,11 +96,24 @@ def typeof(inst): if not inst: return express_set([]) schema_name = inst.is_a(True).split('.')[0].lower() + schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name) + select_parents = getattr(typeof, '_select_parents', None) + if select_parents is None: + select_parents = typeof._select_parents = {} + schema_selects = select_parents.get(schema_name) + if schema_selects is None: + schema_selects = {} + for decl in schema.declarations(): + if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.select_type): + sel_name = decl.name().lower() + for member in decl.select_list(): + schema_selects.setdefault(member.name().lower(), []).append(sel_name) + select_parents[schema_name] = schema_selects def inner(): - decl = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name).declaration_by_name(inst.is_a()) + decl = schema.declaration_by_name(inst.is_a()) while decl: - yield '.'.join((schema_name, decl.name().lower())) + yield decl.name().lower() if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.entity): decl = decl.supertype() else: @@ -109,7 +122,15 @@ def typeof(inst): decl = decl.declared_type() if not isinstance(decl, ifcopenshell.ifcopenshell_wrapper.type_declaration): break - return express_set(inner()) + names = set(inner()) + queue = list(names) + while queue: + name = queue.pop() + for sel_name in schema_selects.get(name, ()): + if sel_name not in names: + names.add(sel_name) + queue.append(sel_name) + return express_set('.'.join((schema_name, name)) for name in names) class indeterminate_type: diff --git a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_ADD2.py b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_ADD2.py index 1645ddf8f5..69629f6868 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_ADD2.py +++ b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_ADD2.py @@ -96,11 +96,24 @@ def typeof(inst): if not inst: return express_set([]) schema_name = inst.is_a(True).split('.')[0].lower() + schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name) + select_parents = getattr(typeof, '_select_parents', None) + if select_parents is None: + select_parents = typeof._select_parents = {} + schema_selects = select_parents.get(schema_name) + if schema_selects is None: + schema_selects = {} + for decl in schema.declarations(): + if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.select_type): + sel_name = decl.name().lower() + for member in decl.select_list(): + schema_selects.setdefault(member.name().lower(), []).append(sel_name) + select_parents[schema_name] = schema_selects def inner(): - decl = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name).declaration_by_name(inst.is_a()) + decl = schema.declaration_by_name(inst.is_a()) while decl: - yield '.'.join((schema_name, decl.name().lower())) + yield decl.name().lower() if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.entity): decl = decl.supertype() else: @@ -109,7 +122,15 @@ def typeof(inst): decl = decl.declared_type() if not isinstance(decl, ifcopenshell.ifcopenshell_wrapper.type_declaration): break - return express_set(inner()) + names = set(inner()) + queue = list(names) + while queue: + name = queue.pop() + for sel_name in schema_selects.get(name, ()): + if sel_name not in names: + names.add(sel_name) + queue.append(sel_name) + return express_set('.'.join((schema_name, name)) for name in names) class indeterminate_type: diff --git a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_RC1.py b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_RC1.py index ae0ee5bce1..c0b7a33a9a 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_RC1.py +++ b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_RC1.py @@ -96,11 +96,24 @@ def typeof(inst): if not inst: return express_set([]) schema_name = inst.is_a(True).split('.')[0].lower() + schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name) + select_parents = getattr(typeof, '_select_parents', None) + if select_parents is None: + select_parents = typeof._select_parents = {} + schema_selects = select_parents.get(schema_name) + if schema_selects is None: + schema_selects = {} + for decl in schema.declarations(): + if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.select_type): + sel_name = decl.name().lower() + for member in decl.select_list(): + schema_selects.setdefault(member.name().lower(), []).append(sel_name) + select_parents[schema_name] = schema_selects def inner(): - decl = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name).declaration_by_name(inst.is_a()) + decl = schema.declaration_by_name(inst.is_a()) while decl: - yield '.'.join((schema_name, decl.name().lower())) + yield decl.name().lower() if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.entity): decl = decl.supertype() else: @@ -109,7 +122,15 @@ def typeof(inst): decl = decl.declared_type() if not isinstance(decl, ifcopenshell.ifcopenshell_wrapper.type_declaration): break - return express_set(inner()) + names = set(inner()) + queue = list(names) + while queue: + name = queue.pop() + for sel_name in schema_selects.get(name, ()): + if sel_name not in names: + names.add(sel_name) + queue.append(sel_name) + return express_set('.'.join((schema_name, name)) for name in names) class indeterminate_type: diff --git a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_RC2.py b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_RC2.py index 36329b4af0..193599a6d3 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_RC2.py +++ b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_RC2.py @@ -96,11 +96,24 @@ def typeof(inst): if not inst: return express_set([]) schema_name = inst.is_a(True).split('.')[0].lower() + schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name) + select_parents = getattr(typeof, '_select_parents', None) + if select_parents is None: + select_parents = typeof._select_parents = {} + schema_selects = select_parents.get(schema_name) + if schema_selects is None: + schema_selects = {} + for decl in schema.declarations(): + if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.select_type): + sel_name = decl.name().lower() + for member in decl.select_list(): + schema_selects.setdefault(member.name().lower(), []).append(sel_name) + select_parents[schema_name] = schema_selects def inner(): - decl = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name).declaration_by_name(inst.is_a()) + decl = schema.declaration_by_name(inst.is_a()) while decl: - yield '.'.join((schema_name, decl.name().lower())) + yield decl.name().lower() if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.entity): decl = decl.supertype() else: @@ -109,7 +122,15 @@ def typeof(inst): decl = decl.declared_type() if not isinstance(decl, ifcopenshell.ifcopenshell_wrapper.type_declaration): break - return express_set(inner()) + names = set(inner()) + queue = list(names) + while queue: + name = queue.pop() + for sel_name in schema_selects.get(name, ()): + if sel_name not in names: + names.add(sel_name) + queue.append(sel_name) + return express_set('.'.join((schema_name, name)) for name in names) class indeterminate_type: diff --git a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_RC3.py b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_RC3.py index f04cb77e97..1cd320f354 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_RC3.py +++ b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_RC3.py @@ -96,11 +96,24 @@ def typeof(inst): if not inst: return express_set([]) schema_name = inst.is_a(True).split('.')[0].lower() + schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name) + select_parents = getattr(typeof, '_select_parents', None) + if select_parents is None: + select_parents = typeof._select_parents = {} + schema_selects = select_parents.get(schema_name) + if schema_selects is None: + schema_selects = {} + for decl in schema.declarations(): + if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.select_type): + sel_name = decl.name().lower() + for member in decl.select_list(): + schema_selects.setdefault(member.name().lower(), []).append(sel_name) + select_parents[schema_name] = schema_selects def inner(): - decl = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name).declaration_by_name(inst.is_a()) + decl = schema.declaration_by_name(inst.is_a()) while decl: - yield '.'.join((schema_name, decl.name().lower())) + yield decl.name().lower() if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.entity): decl = decl.supertype() else: @@ -109,7 +122,15 @@ def typeof(inst): decl = decl.declared_type() if not isinstance(decl, ifcopenshell.ifcopenshell_wrapper.type_declaration): break - return express_set(inner()) + names = set(inner()) + queue = list(names) + while queue: + name = queue.pop() + for sel_name in schema_selects.get(name, ()): + if sel_name not in names: + names.add(sel_name) + queue.append(sel_name) + return express_set('.'.join((schema_name, name)) for name in names) class indeterminate_type: diff --git a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_RC4.py b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_RC4.py index 89afa919b8..683aaeb93e 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_RC4.py +++ b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_RC4.py @@ -96,11 +96,24 @@ def typeof(inst): if not inst: return express_set([]) schema_name = inst.is_a(True).split('.')[0].lower() + schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name) + select_parents = getattr(typeof, '_select_parents', None) + if select_parents is None: + select_parents = typeof._select_parents = {} + schema_selects = select_parents.get(schema_name) + if schema_selects is None: + schema_selects = {} + for decl in schema.declarations(): + if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.select_type): + sel_name = decl.name().lower() + for member in decl.select_list(): + schema_selects.setdefault(member.name().lower(), []).append(sel_name) + select_parents[schema_name] = schema_selects def inner(): - decl = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name).declaration_by_name(inst.is_a()) + decl = schema.declaration_by_name(inst.is_a()) while decl: - yield '.'.join((schema_name, decl.name().lower())) + yield decl.name().lower() if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.entity): decl = decl.supertype() else: @@ -109,7 +122,15 @@ def typeof(inst): decl = decl.declared_type() if not isinstance(decl, ifcopenshell.ifcopenshell_wrapper.type_declaration): break - return express_set(inner()) + names = set(inner()) + queue = list(names) + while queue: + name = queue.pop() + for sel_name in schema_selects.get(name, ()): + if sel_name not in names: + names.add(sel_name) + queue.append(sel_name) + return express_set('.'.join((schema_name, name)) for name in names) class indeterminate_type: diff --git a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_TC1.py b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_TC1.py index c9d106e350..4276e7eed9 100644 --- a/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_TC1.py +++ b/src/ifcopenshell-python/ifcopenshell/express/rules/IFC4X3_TC1.py @@ -96,11 +96,24 @@ def typeof(inst): if not inst: return express_set([]) schema_name = inst.is_a(True).split('.')[0].lower() + schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name) + select_parents = getattr(typeof, '_select_parents', None) + if select_parents is None: + select_parents = typeof._select_parents = {} + schema_selects = select_parents.get(schema_name) + if schema_selects is None: + schema_selects = {} + for decl in schema.declarations(): + if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.select_type): + sel_name = decl.name().lower() + for member in decl.select_list(): + schema_selects.setdefault(member.name().lower(), []).append(sel_name) + select_parents[schema_name] = schema_selects def inner(): - decl = ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema_name).declaration_by_name(inst.is_a()) + decl = schema.declaration_by_name(inst.is_a()) while decl: - yield '.'.join((schema_name, decl.name().lower())) + yield decl.name().lower() if isinstance(decl, ifcopenshell.ifcopenshell_wrapper.entity): decl = decl.supertype() else: @@ -109,7 +122,15 @@ def typeof(inst): decl = decl.declared_type() if not isinstance(decl, ifcopenshell.ifcopenshell_wrapper.type_declaration): break - return express_set(inner()) + names = set(inner()) + queue = list(names) + while queue: + name = queue.pop() + for sel_name in schema_selects.get(name, ()): + if sel_name not in names: + names.add(sel_name) + queue.append(sel_name) + return express_set('.'.join((schema_name, name)) for name in names) class indeterminate_type: