notations_builder   A
last analyzed

Complexity

Total Complexity 38

Size/Duplication

Total Lines 228
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 38
eloc 149
dl 0
loc 228
rs 9.36
c 0
b 0
f 0

7 Functions

Rating   Name   Duplication   Size   Complexity  
A generate_choices() 0 2 1
A generate_node_choices() 0 15 3
A resolve_inheritance() 0 8 3
A inherit() 0 14 3
F generate_graphml_keys() 0 113 17
B extend() 0 17 7
A notations() 0 30 4
1
'''
2
    A builder that creates several python config files from central JSON
3
    config files, the so-called 'notations' files. Each notation file
4
    describes all the relevant semantical rules for the particular graph type.
5
    The Python derivates are generated in a way that they can be smoothly used
6
    for correctness and consistency checks.
7
'''
8
9
from SCons.Script import *
10
import json
11
import pprint
12
13
14
def generate_graphml_keys(notations):
15
    '''
16
        Derive the GraphML preamble from our notations file. This is cool,
17
        because our GraphML import / export magically matches to the notations file.
18
19
        GraphML allows to define extensions as part of the document, by having
20
        <key> elements that describe the extensions. Later <data> elements
21
        can refer to these keys.
22
23
        Our GraphML export behaves nicely and always adds this preamble, so that
24
        other GraphML tools can render the values nicely. For this reason,
25
        the 'graphml_keys' list is generated. It is just an ugly collection of XML
26
        definitions, which means that Marcus violates his own understand of
27
        beautiful code ...
28
29
        Our GraphML import does not rely on the existence of this preamble, but
30
        simply wants to know if a given GraphML <data> element refers to a valid key.
31
        For this reason, the 'graphml_graph_data' and 'graphml_node_data' list is generated.
32
        It tells the import code if a graph / node element in the input XML is allowed
33
        to have a particulary named data element.
34
    '''
35
    graphml_keys = {}
36
    graphml_graph_data = {}
37
    graphml_node_data = {}
38
39
    def generate_key_xml(name, kind, default, for_what='node'):
40
        return \
41
            '        <key id="%s" for="%s" attr.name="%s" attr.type="%s">\n' \
42
            '            <default>%s</default>\n' \
43
            '        </key>' % (name, for_what, name, kind, default,)
44
45
    for notation in notations:
46
        notation_kind = notation['kind']
47
        graphml_graph_data[notation_kind] = set(['kind'])
48
        graphml_node_data[notation_kind] = set(['id', 'kind', 'x', 'y'])
49
        properties = set()
50
        all_keys = [
51
            generate_key_xml('id', 'string', '0'),
52
            generate_key_xml('kind', 'string', 'node'),
53
            generate_key_xml('x', 'long', '0'),
54
            generate_key_xml('y', 'long', '0'),
55
            generate_key_xml('kind', 'string', 'faulttree', 'graph')
56
        ]
57
58
        for node_kind, node in notation['nodes'].items():
59
            for property_name, propertie in node.get('properties', {}).items():
60
                if property_name in properties:
61
                    continue
62
                else:
63
                    properties.add(property_name)
64
65
                property_default = propertie.get('default', '')
66
                property_kind = propertie['kind']
67
                key = None
68
                keys = None
69
70
                if property_kind in {'text', 'textfield'}:
71
                    key = generate_key_xml(
72
                        property_name, 'string', property_default if propertie != 'name' else 'Node')
73
                    graphml_node_data[notation_kind].add(property_name)
74
                elif property_kind == 'compound':
75
                    parts_index = property_default[0]
76
                    compound_parts = propertie['parts']
77
78
                    keys = [
79
                        generate_key_xml(
80
                            property_name, 'string', property_default[1]),
81
                        generate_key_xml(
82
                            property_name + 'Kind', 'string', compound_parts[parts_index]['partName']),
83
                    ]
84
                    graphml_node_data[notation_kind].add(property_name)
85
                elif property_kind == 'bool':
86
                    key = generate_key_xml(
87
                        property_name, 'boolean', 'true' if property_default else 'false')
88
                    graphml_node_data[notation_kind].add(property_name)
89
                elif property_kind == 'choice':
90
                    index = propertie['values'].index(property_default)
91
                    property_default = property_default[index]
92
                    key = generate_key_xml(
93
                        property_name, 'string', property_default)
94
                    graphml_node_data[notation_kind].add(property_name)
95
                elif property_kind == 'epsilon':
96
                    keys = [
97
                        generate_key_xml(
98
                            property_name, 'double', property_default[0]),
99
                        generate_key_xml(
100
                            property_name + 'Epsilon', 'double', property_default[1])
101
                    ]
102
                    graphml_node_data[notation_kind].add(property_name)
103
                elif property_kind in {'numeric', 'range'}:
104
                    kind = 'long' if propertie.get(
105
                        'step', 1) == 1 else 'double'
106
107
                    if property_name != 'missionTime':
108
                        key = generate_key_xml(
109
                            property_name, kind, property_default)
110
                        graphml_node_data[notation_kind].add(property_name)
111
                    else:
112
                        key = generate_key_xml(
113
                            property_name, kind, property_default, 'graph')
114
                        graphml_graph_data[notation_kind].add(property_name)
115
                elif property_kind == 'transfer':
116
                    key = generate_key_xml(property_name, 'string', '')
117
                    graphml_node_data[notation_kind].add(property_name)
118
119
                if key is not None:
120
                    all_keys.append(key)
121
                else:
122
                    all_keys.extend(keys)
123
124
        graphml_keys[notation_kind] = '\n'.join(all_keys)
125
126
    return graphml_keys, graphml_graph_data, graphml_node_data
127
128
129
def extend(target, source, *others, **options):
130
    all_sources = (source,) + others
131
    deep = options.get('deep', False)
132
133
    for other in all_sources:
134
        if not deep:
135
            # perform classical dict update, since nested dicts are not used
136
            target.update(other)
137
            continue
138
139
        for key, value in other.items():
140
            if key in target and isinstance(target[key], dict) and isinstance(value, dict):
141
                target[key] = extend({}, target[key], other[key], deep=True)
142
            else:
143
                target[key] = value
144
145
    return target
146
147
148
def generate_choices(notations):
149
    return [(notation['kind'], notation['name']) for notation in notations]
150
151
152
def generate_node_choices(notations):
153
    node_choices = []
154
155
    for notation in notations:
156
        nodes = notation['nodes']
157
        node_category = (notation['name'],)
158
        node_category_choices = ()
159
160
        for node_kind, node in nodes.items():
161
            node_category_choices += ((node_kind, node['nodeDisplayName']),)
162
163
        node_category += (node_category_choices,)
164
        node_choices.append(node_category)
165
166
    return node_choices
167
168
169
def inherit(node_name, node, nodes, node_cache):
170
    inherits_from = node.get('inherits')
171
172
    if not inherits_from:
173
        node_cache[node_name] = node
174
        return node
175
176
    elif inherits_from not in node_cache:
177
        inherit(inherits_from, nodes[inherits_from], nodes, node_cache)
178
179
    resolved = extend({}, node_cache[inherits_from], node, deep=True)
180
    node_cache[node_name] = resolved
181
182
    return resolved
183
184
185
def resolve_inheritance(notations):
186
    for notation in notations:
187
        notation[u'edges'] = notation.get(u'edges', {})
188
        nodes = notation['nodes']
189
        node_cache = {}
190
191
        for node_name, node in nodes.items():
192
            nodes[node_name] = inherit(node_name, node, nodes, node_cache)
193
194
195
def notations(target, source, env):
196
    '''
197
        The central build task for the Python notations file equivalents.
198
    '''
199
    notations = []
200
201
    for input_file in source:
202
        with open(str(input_file)) as handle:
203
            notations.append(json.loads(handle.read()))
204
    resolve_inheritance(notations)
205
206
    with open(str(target[0]), 'w') as out:
207
        out.write(
208
            '# DO NOT EDIT! This file is auto-generated by "setup.py build"\n')
209
        out.write('notations = ')
210
        pprint.pprint(notations, out)
211
        out.write(
212
            '\nby_kind = {notation[\'kind\']: notation for notation in notations}\n')
213
        out.write('choices = ')
214
        pprint.pprint(generate_choices(notations), out)
215
        out.write('\nnode_choices = ')
216
        pprint.pprint(generate_node_choices(notations), out)
217
        key_xml, graph_data, node_data = generate_graphml_keys(notations)
218
        out.write('\ngraphml_keys = ')
219
        pprint.pprint(key_xml, out)
220
        out.write('\ngraphml_graph_data = ')
221
        pprint.pprint(graph_data, out)
222
        out.write('\ngraphml_node_data = ')
223
        pprint.pprint(node_data, out)
224
        out.write('\n# END OF GENERATED CONTENT')
225
226
227
notationsbuilder = Builder(action=notations)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable Builder does not seem to be defined.
Loading history...
228