mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-18 06:21:40 +00:00
Fix #3113. Support regex in selector filter values.
This commit is contained in:
@@ -62,7 +62,8 @@ class Selector:
|
|||||||
class_selector: "." WORD filter ?
|
class_selector: "." WORD filter ?
|
||||||
filter: "[" filter_key (comparison filter_value)? "]"
|
filter: "[" filter_key (comparison filter_value)? "]"
|
||||||
filter_key: WORD | ESCAPED_STRING | keys_regex | keys_quoted | keys_simple
|
filter_key: WORD | ESCAPED_STRING | keys_regex | keys_quoted | keys_simple
|
||||||
filter_value: ESCAPED_STRING | SIGNED_FLOAT | SIGNED_INT | BOOLEAN | NULL
|
filter_value: filter_regex | ESCAPED_STRING | SIGNED_FLOAT | SIGNED_INT | BOOLEAN | NULL
|
||||||
|
filter_regex: "r" ESCAPED_STRING
|
||||||
keys_regex: "r" ESCAPED_STRING ("." ESCAPED_STRING)*
|
keys_regex: "r" ESCAPED_STRING ("." ESCAPED_STRING)*
|
||||||
keys_quoted: ESCAPED_STRING ("." ESCAPED_STRING)*
|
keys_quoted: ESCAPED_STRING ("." ESCAPED_STRING)*
|
||||||
keys_simple: /[^\\W][^.=<>!%*\\]]*/ ("." /[^\\W][^.=<>!%*\\]]*/)*
|
keys_simple: /[^\\W][^.=<>!%*\\]]*/ ("." /[^\\W][^.=<>!%*\\]]*/)*
|
||||||
@@ -205,22 +206,30 @@ class Selector:
|
|||||||
comparison = filter_rule.children[1].children[0].data
|
comparison = filter_rule.children[1].children[0].data
|
||||||
if comparison == "not":
|
if comparison == "not":
|
||||||
comparison += filter_rule.children[1].children[1].data
|
comparison += filter_rule.children[1].children[1].data
|
||||||
token_type = filter_rule.children[2].children[0].type
|
filter_value = filter_rule.children[2].children[0]
|
||||||
if token_type == "ESCAPED_STRING":
|
if isinstance(filter_value, lark.Tree):
|
||||||
value = str(filter_rule.children[2].children[0][1:-1])
|
is_regex = True
|
||||||
|
token_type = filter_value.data
|
||||||
|
else:
|
||||||
|
is_regex = False
|
||||||
|
token_type = filter_value.type
|
||||||
|
if token_type == "filter_regex":
|
||||||
|
value = str(filter_value.children[0][1:-1])
|
||||||
|
elif token_type == "ESCAPED_STRING":
|
||||||
|
value = str(filter_value[1:-1])
|
||||||
elif token_type == "SIGNED_INT":
|
elif token_type == "SIGNED_INT":
|
||||||
value = int(filter_rule.children[2].children[0])
|
value = int(filter_value)
|
||||||
elif token_type == "SIGNED_FLOAT":
|
elif token_type == "SIGNED_FLOAT":
|
||||||
value = float(filter_rule.children[2].children[0])
|
value = float(filter_value)
|
||||||
elif token_type == "BOOLEAN":
|
elif token_type == "BOOLEAN":
|
||||||
value = filter_rule.children[2].children[0].lower() == "true"
|
value = filter_value.lower() == "true"
|
||||||
elif token_type == "NULL":
|
elif token_type == "NULL":
|
||||||
value = None
|
value = None
|
||||||
for element in elements:
|
for element in elements:
|
||||||
element_value = cls.get_element_value(element, filter_query["keys"], is_regex=filter_query["is_regex"])
|
element_value = cls.get_element_value(element, filter_query["keys"], is_regex=filter_query["is_regex"])
|
||||||
if element_value is None and value is not None and "not" not in comparison:
|
if element_value is None and value is not None and "not" not in comparison:
|
||||||
continue
|
continue
|
||||||
if comparison and cls.filter_element(element, element_value, comparison, value):
|
if comparison and cls.filter_element(element, element_value, comparison, value, is_regex=is_regex):
|
||||||
results.append(element)
|
results.append(element)
|
||||||
elif not comparison and element_value:
|
elif not comparison and element_value:
|
||||||
results.append(element)
|
results.append(element)
|
||||||
@@ -314,12 +323,19 @@ class Selector:
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def filter_element(cls, element, element_value, comparison, value):
|
def filter_element(cls, element, element_value, comparison, value, is_regex=False):
|
||||||
if comparison.startswith("not"):
|
if comparison.startswith("not"):
|
||||||
return not cls.filter_element(element, element_value, comparison[3:], value)
|
return not cls.filter_element(element, element_value, comparison[3:], value, is_regex=is_regex)
|
||||||
elif comparison == "equal" and isinstance(element_value, list):
|
elif comparison == "equal" and isinstance(element_value, list):
|
||||||
|
if is_regex:
|
||||||
|
for element_v in element_value:
|
||||||
|
if re.match(value, element_v):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
return value in element_value
|
return value in element_value
|
||||||
elif comparison == "equal":
|
elif comparison == "equal":
|
||||||
|
if is_regex:
|
||||||
|
return bool(re.match(value, element_value))
|
||||||
return element_value == value
|
return element_value == value
|
||||||
elif comparison == "contains" and isinstance(element_value, list):
|
elif comparison == "contains" and isinstance(element_value, list):
|
||||||
return bool([ev for ev in element_value if value in str(ev)])
|
return bool([ev for ev in element_value if value in str(ev)])
|
||||||
|
|||||||
@@ -99,6 +99,12 @@ class TestSelector(test.bootstrap.IFC4):
|
|||||||
assert subject.Selector.parse(self.file, '.IfcElement[Name="Foobar"]') == [element]
|
assert subject.Selector.parse(self.file, '.IfcElement[Name="Foobar"]') == [element]
|
||||||
assert subject.Selector.parse(self.file, '.IfcElement[Name="Foobaz"]') == []
|
assert subject.Selector.parse(self.file, '.IfcElement[Name="Foobaz"]') == []
|
||||||
|
|
||||||
|
def test_selecting_by_regex(self):
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
element.Name = "Foobar"
|
||||||
|
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSlab")
|
||||||
|
assert subject.Selector.parse(self.file, '.IfcElement[Name=r"Foo.*"]') == [element]
|
||||||
|
|
||||||
def test_selecting_by_property_existence(self):
|
def test_selecting_by_property_existence(self):
|
||||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Foo_Bar")
|
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Foo_Bar")
|
||||||
|
|||||||
Reference in New Issue
Block a user