mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +00:00
Fix #2745. You can now quote and escape complex pset names in the selector.
This commit is contained in:
@@ -35,9 +35,10 @@ class Selector:
|
||||
guid_selector: "#" /[0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$]{22}/
|
||||
class_selector: "." WORD filter ?
|
||||
filter: "[" filter_key (comparison filter_value)? "]"
|
||||
filter_key: WORD | pset_or_qto
|
||||
filter_key: WORD | ESCAPED_STRING | keys_quoted | keys_simple
|
||||
filter_value: ESCAPED_STRING | SIGNED_FLOAT | SIGNED_INT | BOOLEAN | NULL
|
||||
pset_or_qto: /[^\\W][^.=<>]*[^\\W]/ "." /[^\\W][^.=<>]*[^\\W]/
|
||||
keys_quoted: ESCAPED_STRING "." ESCAPED_STRING
|
||||
keys_simple: /[^\\W][^.=<>]*[^\\W]/ "." /[^\\W][^.=<>]*[^\\W]/
|
||||
lfunction: and | or
|
||||
inverse_relationship: types | decomposed_by | bounded_by | grouped_by
|
||||
types: "*"
|
||||
@@ -180,7 +181,13 @@ class Selector:
|
||||
results = []
|
||||
key = filter_rule.children[0].children[0]
|
||||
if not isinstance(key, str):
|
||||
key = key.children[0] + "." + key.children[1]
|
||||
k0 = key.children[0]
|
||||
if k0.type == "ESCAPED_STRING":
|
||||
k0 = k0[1:-1].replace("\\\"", '"')
|
||||
k1 = key.children[1]
|
||||
if k1.type == "ESCAPED_STRING":
|
||||
k1 = k1[1:-1].replace("\\\"", '"')
|
||||
key = [k0, k1]
|
||||
comparison = value = None
|
||||
if len(filter_rule.children) > 1:
|
||||
comparison = filter_rule.children[1].children[0].data
|
||||
@@ -210,56 +217,29 @@ class Selector:
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def get_element_value(cls, element, key, value=None):
|
||||
if "." in key and key.split(".")[0] == "type":
|
||||
try:
|
||||
element = ifcopenshell.util.element.get_type(element)
|
||||
if not element:
|
||||
return None
|
||||
except:
|
||||
return
|
||||
key = ".".join(key.split(".")[1:])
|
||||
elif "." in key and key.split(".")[0] == "material":
|
||||
try:
|
||||
material_definition = ifcopenshell.util.element.get_material(
|
||||
element, should_skip_usage=True
|
||||
)
|
||||
if not material_definition:
|
||||
return None
|
||||
key = ".".join(key.split(".")[1:])
|
||||
if value:
|
||||
materials = [
|
||||
inst
|
||||
for inst in cls.file.traverse(material_definition)
|
||||
if inst.is_a("IfcMaterial")
|
||||
]
|
||||
for material in materials or []:
|
||||
info = material.get_info()
|
||||
if key in info and info[key] == value:
|
||||
element = material
|
||||
element = material_definition
|
||||
if not element:
|
||||
return None
|
||||
except:
|
||||
return
|
||||
elif "." in key and key.split(".")[0] == "container":
|
||||
try:
|
||||
element = ifcopenshell.util.element.get_container(element)
|
||||
if not element:
|
||||
return None
|
||||
except:
|
||||
return
|
||||
key = ".".join(key.split(".")[1:])
|
||||
info = element.get_info()
|
||||
if key in info:
|
||||
return info[key]
|
||||
elif "." in key:
|
||||
key_components = key.split(".")
|
||||
pset_name = key_components[0]
|
||||
prop = ".".join(key_components[1:])
|
||||
psets = ifcopenshell.util.element.get_psets(element)
|
||||
if pset_name in psets and prop in psets[pset_name]:
|
||||
return psets[pset_name][prop]
|
||||
def get_element_value(cls, element, keys, value=None):
|
||||
if not isinstance(keys, (tuple, list)):
|
||||
keys = [keys]
|
||||
value = element
|
||||
for key in keys:
|
||||
if key == "type":
|
||||
value = ifcopenshell.util.element.get_type(element)
|
||||
elif key == "material":
|
||||
value = ifcopenshell.util.element.get_material(element, should_skip_usage=True)
|
||||
elif key == "container":
|
||||
value = ifcopenshell.util.element.get_container(element)
|
||||
elif isinstance(value, ifcopenshell.entity_instance):
|
||||
result = value.get_info().get(key, None)
|
||||
if result is not None:
|
||||
return result
|
||||
result = ifcopenshell.util.element.get_pset(value, key)
|
||||
if result is None:
|
||||
value = None
|
||||
else:
|
||||
value = result
|
||||
elif isinstance(value, dict): # Such as from the result of a prior get_pset
|
||||
return value.get(key, None)
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def filter_element(cls, element, element_value, comparison, value):
|
||||
|
||||
@@ -145,6 +145,18 @@ class TestSelector(test.bootstrap.IFC4):
|
||||
self.file, '.IfcElement[a !%$§&/()?|*-+,€~#@µ^°a.a !%$§&/()?|*-+,€~#@µ^°a="Bar"]'
|
||||
) == [element]
|
||||
|
||||
def test_selecting_a_property_which_includes_a_dot(self):
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="a.b")
|
||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"c.d": "Bar"})
|
||||
assert subject.Selector.parse(self.file, '.IfcElement["a.b"."c.d"="Bar"]') == [element]
|
||||
|
||||
def test_selecting_a_property_which_includes_an_escaped_quote(self):
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name='"a.b"')
|
||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={'"c.d"': "Bar"})
|
||||
assert subject.Selector.parse(self.file, r'.IfcElement["\"a.b\""."\"c.d\""="Bar"]') == [element]
|
||||
|
||||
def test_comparing_if_value_is_in_a_list(self):
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
element.Name = "Foobar"
|
||||
|
||||
Reference in New Issue
Block a user