From 75f254d16285a80a1acadea5efddd010867132c4 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Fri, 4 Oct 2019 17:24:49 +1000 Subject: [PATCH] Extract definitions and descriptions for use in plugin --- src/ifcblenderexport/extract.py | 104 ++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 src/ifcblenderexport/extract.py diff --git a/src/ifcblenderexport/extract.py b/src/ifcblenderexport/extract.py new file mode 100644 index 0000000000..b7f930f622 --- /dev/null +++ b/src/ifcblenderexport/extract.py @@ -0,0 +1,104 @@ +import xml.sax, json, copy, pathlib +from bs4 import BeautifulSoup +import sys +sys.setrecursionlimit(100) + +class IfcElementHandler(xml.sax.ContentHandler): + + def __init__(self): + self.elements = {} + self.current_element_name = None + self.enums = {} + self.current_enum_name = None + self.attribute_stack = [] + + def startElement(self, name, attrs): + if name == 'xs:element' and 'substitutionGroup' in attrs: + self.elements[attrs['name']] = { + 'description': self.get_description(attrs['name']), + 'is_abstract': True if 'abstract' in attrs else False, + 'parent': attrs['substitutionGroup'][len('ifc:'):], + 'attributes': [] + } + self.current_element_name = attrs['name'] + elif name == 'xs:simpleType' \ + and 'name' in attrs \ + and 'Enum' in attrs['name']: + self.current_enum_name = attrs['name'] + self.enums[self.current_enum_name] = [] + elif name == 'xs:enumeration' and self.current_enum_name: + self.enums[self.current_enum_name].append(attrs['value'].upper()) + elif name == 'xs:attribute' \ + and self.current_element_name \ + and 'name' in attrs \ + and 'type' in attrs: + self.elements[self.current_element_name]['attributes'].append({ + 'name': attrs['name'], + 'type': attrs['type'].replace('ifc:', ''), + }) + + def endDocument(self): + elements = {} + + for name, data in self.elements.items(): + for index, attribute in enumerate(data['attributes']): + data['attributes'][index] = self.resolve_enums(attribute) + + for name, data in self.elements.items(): + if data['is_abstract']: + continue + if self.is_an_ifcproduct(data): + self.attribute_stack = [] + self.get_parent_attributes(data) + elements[name] = copy.deepcopy(data) + elements[name]['attributes'] = copy.deepcopy(self.attribute_stack) + + self.elements = elements + + def get_description(self, name): + try: + filenames = pathlib.Path( + 'io_export_ifc/schema/ifc4-add2-tc1/ifc4-add2-tc1/html/schema/').glob( + '**/{}.htm'.format(name.lower())) + for filename in filenames: + with open(filename, 'r') as file: + soup = BeautifulSoup(file, 'html.parser') + for detail in soup.find_all('details'): + if detail.summary.string == 'Entity definition' \ + and detail.p: + return str(detail.p.text.replace('\n', ' ')) + return None + except: + return None + #print('Failed to get description for {}'.format(name)) + return None + + def resolve_enums(self, attribute): + if attribute['type'] in self.enums: + attribute['is_enum'] = True + attribute['enum_values'] = self.enums[attribute['type']] + return attribute + attribute['is_enum'] = False + attribute['enum_values'] = [] + return attribute + + def get_parent_attributes(self, data): + self.attribute_stack.extend(data['attributes']) + if data['parent'] != 'IfcProduct': # For now, we treat attributes above IfcProduct in a special way + self.get_parent_attributes(self.elements[data['parent']]) + + def is_an_ifcproduct(self, data): + if data['parent'] == 'IfcProduct': + return True + else: + for name, parent_data in self.elements.items(): + if name == data['parent']: + return self.is_an_ifcproduct(parent_data) + return False + +xsd_path = 'io_export_ifc/schema/IFC4.xsd' +handler = IfcElementHandler() +parser = xml.sax.make_parser() +parser.setContentHandler(handler) +parser.parse(xsd_path) +print(json.dumps(handler.elements, indent=4))