diff --git a/src/blenderbim/blenderbim/bim/module/drawing/operator.py b/src/blenderbim/blenderbim/bim/module/drawing/operator.py index 111bed06d5..a6fe33cd35 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/operator.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/operator.py @@ -612,7 +612,7 @@ class CreateDrawing(bpy.types.Operator): classes.append("material-{}".format(re.sub("[^0-9a-zA-Z]+", "", self.get_material_name(material)))) classes.append("globalid-{}".format(element.GlobalId)) for attribute in svg_writer.annotations["attributes"]: - result = self.selector.get_element_value(element, attribute) + result = ifcopenshell.util.selector.get_element_value(element, attribute) if result: classes.append( "{}-{}".format(re.sub("[^0-9a-zA-Z]+", "", attribute), re.sub("[^0-9a-zA-Z]+", "", result)) @@ -960,7 +960,6 @@ class ActivateDrawingStyle(bpy.types.Operator): exec(f"{path} = {value}") def set_query(self, context): - self.selector = ifcopenshell.util.selector.Selector() self.include_global_ids = [] self.exclude_global_ids = [] for ifc_file in context.scene.DocProperties.ifc_files: @@ -969,10 +968,10 @@ class ActivateDrawingStyle(bpy.types.Operator): except: continue if self.drawing_style.include_query: - results = self.selector.parse(ifc, self.drawing_style.include_query) + results = ifcopenshell.util.selector.Selector.parse(ifc, self.drawing_style.include_query) self.include_global_ids.extend([e.GlobalId for e in results]) if self.drawing_style.exclude_query: - results = self.selector.parse(ifc, self.drawing_style.exclude_query) + results = ifcopenshell.util.selector.Selector.parse(ifc, self.drawing_style.exclude_query) self.exclude_global_ids.extend([e.GlobalId for e in results]) if self.drawing_style.include_query: self.parse_filter_query("INCLUDE", context) diff --git a/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py b/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py index 5c2840f161..a988d0fd21 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/svgwriter.py @@ -337,7 +337,7 @@ class SvgWriter: if custom_classes: classes.extend(custom_classes.split()) for key in self.metadata: - value = ifcopenshell.util.selector.Selector.get_element_value(element, key) + value = ifcopenshell.util.selector.get_element_value(element, key) if value: classes.append(self.canonicalise_class_name(key) + "-" + self.canonicalise_class_name(str(value))) return classes @@ -671,11 +671,10 @@ class SvgWriter: literal = text_literal.Literal product = tool.Drawing.get_assigned_product(element) - selector = ifcopenshell.util.selector.Selector variables = {} if product != None: for variable in re.findall("{{.*?}}", literal): - literal = literal.replace(variable, str(selector.get_element_value(product, variable[2:-2]) or "")) + literal = literal.replace(variable, str(ifcopenshell.util.selector.get_element_value(product, variable[2:-2]) or "")) for line_number, text_line in enumerate(literal.replace("\\n", "\n").split("\n")): self.svg.add( diff --git a/src/blenderbim/blenderbim/tool/drawing.py b/src/blenderbim/blenderbim/tool/drawing.py index 8124d91759..4f9c8e754f 100644 --- a/src/blenderbim/blenderbim/tool/drawing.py +++ b/src/blenderbim/blenderbim/tool/drawing.py @@ -526,10 +526,9 @@ class Drawing(blenderbim.core.tool.Drawing): value = element.Literal product = cls.get_assigned_product(tool.Ifc.get_entity(obj)) if product: - selector = ifcopenshell.util.selector.Selector() variables = {} for variable in re.findall("{{.*?}}", value): - value = value.replace(variable, str(selector.get_element_value(product, variable[2:-2]) or "")) + value = value.replace(variable, str(ifcopenshell.util.selector.get_element_value(product, variable[2:-2]) or "")) obj.BIMTextProperties.value = value # TODO below this point is highly experimental prototype code with no tests diff --git a/src/ifccsv/ifccsv.py b/src/ifccsv/ifccsv.py index 08d0d9ef65..8fc4adb5a1 100755 --- a/src/ifccsv/ifccsv.py +++ b/src/ifccsv/ifccsv.py @@ -144,7 +144,6 @@ class IfcCsv: self.attributes = [] self.output = "" self.format = "csv" - self.selector = ifcopenshell.util.selector.Selector() self.delimiter = "," def export(self, ifc_file, elements): @@ -162,7 +161,7 @@ class IfcCsv: del self.attributes[index] for attribute in self.attributes: - result.append(self.selector.get_element_value(element, attribute)) + result.append(ifcopenshell.util.selector.get_element_value(element, attribute)) self.results.append(result) self.headers = ["GlobalId"] diff --git a/src/ifcopenshell-python/ifcopenshell/util/selector.py b/src/ifcopenshell-python/ifcopenshell/util/selector.py index 56c5df4899..89aa2bd269 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/selector.py +++ b/src/ifcopenshell-python/ifcopenshell/util/selector.py @@ -23,6 +23,31 @@ import ifcopenshell.util.fm import ifcopenshell.util.element +def get_element_value(element, query): + l = lark.Lark( + """start: WORD | ESCAPED_STRING | keys_regex | keys_quoted | keys_simple + keys_regex: "r" ESCAPED_STRING "." ESCAPED_STRING + keys_quoted: ESCAPED_STRING "." ESCAPED_STRING + keys_simple: /[^\\W][^.=<>]*[^\\W]/ "." /[^\\W][^.=<>]*[^\\W]/ + + // Embed common.lark for packaging + _STRING_INNER: /.*?/ + _STRING_ESC_INNER: _STRING_INNER /(? 1: comparison = filter_rule.children[1].children[0].data @@ -209,7 +224,7 @@ class Selector: elif token_type == "NULL": value = None for element in elements: - element_value = cls.get_element_value(element, keys, is_regex=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: continue if comparison and cls.filter_element( @@ -220,6 +235,21 @@ class Selector: results.append(element) return results + @classmethod + def parse_filter_query(cls, filter_query): + keys = filter_query + is_regex = False + if isinstance(keys, str): + keys = [keys] + elif keys.data == "keys_regex": + is_regex = True + keys = [keys.children[0][1:-1].replace("\\\"", '"'), keys.children[1][1:-1].replace("\\\"", '"')] + elif keys.data == "keys_quoted": + keys = [keys.children[0][1:-1].replace("\\\"", '"'), keys.children[1][1:-1].replace("\\\"", '"')] + elif keys.data == "keys_simple": + keys = [keys.children[0], keys.children[1]] + return {"keys": keys, "is_regex": is_regex} + @classmethod def get_element_value(cls, element, keys, is_regex=False): value = element diff --git a/src/ifcopenshell-python/test/util/test_selector.py b/src/ifcopenshell-python/test/util/test_selector.py index 0068845567..2991f71b7c 100644 --- a/src/ifcopenshell-python/test/util/test_selector.py +++ b/src/ifcopenshell-python/test/util/test_selector.py @@ -22,6 +22,13 @@ import ifcopenshell.api import ifcopenshell.util.selector as subject +class TestGetElementValue(test.bootstrap.IFC4): + def test_selecting_an_elements_value_using_a_query(self): + element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + element.Name = "Foobar" + assert subject.get_element_value(element, "Name") == "Foobar" + + class TestSelector(test.bootstrap.IFC4): def test_selecting_by_class(self): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")