File size: 5,010 Bytes
985c397
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# SPDX-License-Identifier: LGPL-2.1-or-later

# ***************************************************************************
# *                                                                         *
# *   Copyright (c) 2024 Yorik van Havre <yorik@uncreated.net>              *
# *                                                                         *
# *   This file is part of FreeCAD.                                         *
# *                                                                         *
# *   FreeCAD is free software: you can redistribute it and/or modify it    *
# *   under the terms of the GNU Lesser General Public License as           *
# *   published by the Free Software Foundation, either version 2.1 of the  *
# *   License, or (at your option) any later version.                       *
# *                                                                         *
# *   FreeCAD is distributed in the hope that it will be useful, but        *
# *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
# *   Lesser General Public License for more details.                       *
# *                                                                         *
# *   You should have received a copy of the GNU Lesser General Public      *
# *   License along with FreeCAD. If not, see                               *
# *   <https://www.gnu.org/licenses/>.                                      *
# *                                                                         *
# ***************************************************************************

"""This script converts the computer iterpretable listing ifcXML XSD into a list
of non-abstract children of IfcProduct"""

import xml.sax, json, copy


class IfcElementHandler(xml.sax.ContentHandler):
    def __init__(self):
        super().__init__()
        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"]] = {
                "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 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 = "IFC4_ADD2.xsd"
handler = IfcElementHandler()
parser = xml.sax.make_parser()
parser.setContentHandler(handler)
parser.parse(xsd_path)
print(json.dumps(handler.elements, indent=4))