StrictDoc Documentation
strictdoc/backend/sdoc/models/document_grammar.py
Source file coverage
Path:
strictdoc/backend/sdoc/models/document_grammar.py
Lines:
405
Non-empty lines:
367
Non-empty lines covered with requirements:
367 / 367 (100.0%)
Functions:
14
Functions covered by requirements:
14 / 14 (100.0%)
1
"""
2
@relation(SDOC-SRS-19, scope=file)
3
"""
4
 
5
from typing import Dict, List, Optional, Set
6
 
7
from strictdoc.backend.sdoc.models.grammar_element import (
8
    GrammarElement,
9
    GrammarElementFieldString,
10
    GrammarElementFieldType,
11
)
12
from strictdoc.backend.sdoc.models.model import (
13
    RequirementFieldName,
14
    SDocDocumentIF,
15
    SDocGrammarIF,
16
)
17
 
18
 
19
class DocumentGrammar(SDocGrammarIF):
20
    def __init__(
21
        self,
22
        parent: Optional[SDocDocumentIF],
23
        elements: List[GrammarElement],
24
        import_from_file: Optional[str] = None,
25
    ) -> None:
26
        self.parent: Optional[SDocDocumentIF] = parent
27
        self.elements: List[GrammarElement] = elements
28
 
29
        self.registered_elements: Set[str] = set()
30
        self.elements_by_type: Dict[str, GrammarElement] = {}
31
 
32
        self.update_with_elements(elements)
33
 
34
        # The textX parser passes an empty string instead of None when
35
        # import_from_file is not provided in input.
36
        if import_from_file is not None and len(import_from_file) == 0:
37
            import_from_file = None
38
        self.import_from_file: Optional[str] = import_from_file
39
 
40
        self.is_default = False
41
 
42
        self.ng_line_start: Optional[int] = None
43
        self.ng_col_start: Optional[int] = None
44
 
45
    @staticmethod
46
    def create_default(
47
        parent: Optional[SDocDocumentIF],
48
        enable_mid: bool = False,
49
        include_child_relation: bool = False,
50
    ) -> "DocumentGrammar":
51
        """
52
        @relation(SDOC-SRS-93, scope=function)
53
        """
54
 
55
        text_element: GrammarElement = (
56
            DocumentGrammar.create_default_text_element(enable_mid=enable_mid)
57
        )
58
 
59
        # @relation(SDOC-SRS-132, scope=range_start)
60
        fields: List[GrammarElementFieldType] = []
61
 
62
        if enable_mid:
63
            fields.append(
64
                GrammarElementFieldString(
65
                    parent=None,
66
                    title=RequirementFieldName.MID,
67
                    human_title=None,
68
                    required="False",
69
                )
70
            )
71
 
72
        fields.extend(
73
            [
74
                GrammarElementFieldString(
75
                    parent=None,
76
                    title=RequirementFieldName.UID,
77
                    human_title=None,
78
                    required="False",
79
                ),
80
                GrammarElementFieldString(
81
                    parent=None,
82
                    title=RequirementFieldName.LEVEL,
83
                    human_title=None,
84
                    required="False",
85
                ),
86
                GrammarElementFieldString(
87
                    parent=None,
88
                    title=RequirementFieldName.STATUS,
89
                    human_title=None,
90
                    required="False",
91
                ),
92
                GrammarElementFieldString(
93
                    parent=None,
94
                    title=RequirementFieldName.TAGS,
95
                    human_title=None,
96
                    required="False",
97
                ),
98
                GrammarElementFieldString(
99
                    parent=None,
100
                    title=RequirementFieldName.TITLE,
101
                    human_title=None,
102
                    required="False",
103
                ),
104
                GrammarElementFieldString(
105
                    parent=None,
106
                    title=RequirementFieldName.STATEMENT,
107
                    human_title=None,
108
                    required="False",
109
                ),
110
                GrammarElementFieldString(
111
                    parent=None,
112
                    title=RequirementFieldName.RATIONALE,
113
                    human_title=None,
114
                    required="False",
115
                ),
116
                GrammarElementFieldString(
117
                    parent=None,
118
                    title=RequirementFieldName.COMMENT,
119
                    human_title=None,
120
                    required="False",
121
                ),
122
            ]
123
        )
124
 
125
        requirement_element = GrammarElement(
126
            parent=None,
127
            tag="REQUIREMENT",
128
            property_is_composite="",
129
            property_prefix="",
130
            property_view_style="",
131
            fields=fields,
132
            relations=[],
133
        )
134
        # @relation(SDOC-SRS-132, scope=range_end)
135
 
136
        requirement_element.relations = GrammarElement.create_default_relations(
137
            requirement_element,
138
            include_child=include_child_relation,
139
        )
140
 
141
        elements: List[GrammarElement] = []
142
 
143
        section_element: GrammarElement = (
144
            DocumentGrammar.create_default_section_element(
145
                enable_mid=enable_mid
146
            )
147
        )
148
        elements.append(section_element)
149
 
150
        elements.append(text_element)
151
        elements.append(requirement_element)
152
 
153
        grammar = DocumentGrammar(
154
            parent=parent, elements=elements, import_from_file=None
155
        )
156
        grammar.is_default = True
157
        text_element.parent = grammar
158
        requirement_element.parent = grammar
159
 
160
        return grammar
161
 
162
    @staticmethod
163
    def create_for_test_report(parent: SDocDocumentIF) -> "DocumentGrammar":
164
        section_element: GrammarElement = (
165
            DocumentGrammar.create_default_section_element(
166
                parent=None, enable_mid=parent.config.enable_mid == True
167
            )
168
        )
169
        text_element: GrammarElement = (
170
            DocumentGrammar.create_default_text_element(
171
                parent=None, enable_mid=parent.config.enable_mid == True
172
            )
173
        )
174
 
175
        fields: List[GrammarElementFieldType] = [
176
            GrammarElementFieldString(
177
                parent=None,
178
                title=RequirementFieldName.UID,
179
                human_title=None,
180
                required="False",
181
            ),
182
            GrammarElementFieldString(
183
                parent=None,
184
                title="TEST_PATH",
185
                human_title=None,
186
                required="False",
187
            ),
188
            GrammarElementFieldString(
189
                parent=None,
190
                title="TEST_FUNCTION",
191
                human_title=None,
192
                required="False",
193
            ),
194
            GrammarElementFieldString(
195
                parent=None,
196
                title="DURATION",
197
                human_title=None,
198
                required="False",
199
            ),
200
            GrammarElementFieldString(
201
                parent=None,
202
                title=RequirementFieldName.STATUS,
203
                human_title=None,
204
                required="True",
205
            ),
206
            GrammarElementFieldString(
207
                parent=None,
208
                title=RequirementFieldName.TITLE,
209
                human_title=None,
210
                required="True",
211
            ),
212
            GrammarElementFieldString(
213
                parent=None,
214
                title=RequirementFieldName.STATEMENT,
215
                human_title=None,
216
                required="False",
217
            ),
218
        ]
219
        requirement_element = GrammarElement(
220
            parent=None,
221
            tag="TEST_RESULT",
222
            property_is_composite="",
223
            property_prefix="",
224
            property_view_style="",
225
            fields=fields,
226
            relations=[],
227
        )
228
 
229
        requirement_element.relations = GrammarElement.create_default_relations(
230
            requirement_element
231
        )
232
 
233
        elements: List[GrammarElement] = [
234
            section_element,
235
            text_element,
236
            requirement_element,
237
        ]
238
        grammar = DocumentGrammar(
239
            parent=parent, elements=elements, import_from_file=None
240
        )
241
        grammar.is_default = True
242
        section_element.parent = grammar
243
        text_element.parent = grammar
244
        requirement_element.parent = grammar
245
 
246
        return grammar
247
 
248
    def get_element_by_mid(self, element_mid: str) -> GrammarElement:
249
        for element_ in self.elements:
250
            if element_.mid == element_mid:
251
                return element_
252
        raise AssertionError(
253
            f"Could not find a grammar element with MID: {element_mid}"
254
        )
255
 
256
    def dump_fields(self, node_type: str) -> str:
257
        return ", ".join(
258
            list(
259
                map(
260
                    lambda g: g.title,
261
                    self.elements_by_type[node_type].fields,
262
                )
263
            )
264
        )
265
 
266
    def has_text_element(self) -> bool:
267
        for element_ in self.elements:
268
            if element_.tag == "TEXT":
269
                return True
270
        return False
271
 
272
    def add_element_first(self, element: GrammarElement) -> None:
273
        self.elements.insert(0, element)
274
        self.elements_by_type[element.tag] = element
275
        self.registered_elements.add(element.tag)
276
        self.is_default = False
277
 
278
    def update_element(
279
        self, existing_element: GrammarElement, updated_element: GrammarElement
280
    ) -> None:
281
        element_index = self.elements.index(existing_element)
282
        self.elements[element_index] = updated_element
283
        self.elements_by_type[updated_element.tag] = updated_element
284
        self.is_default = False
285
 
286
    def update_with_elements(self, elements: List[GrammarElement]) -> None:
287
        # When elements are created by code, not by textX, it is convenient
288
        # if their .parent is set here automatically.
289
        for element_ in elements:
290
            element_.parent = self
291
 
292
        registered_elements: Set[str] = set()
293
        elements_by_type: Dict[str, GrammarElement] = {}
294
        for element in elements:
295
            registered_elements.add(element.tag)
296
            elements_by_type[element.tag] = element
297
 
298
        self.elements = elements
299
        self.registered_elements = registered_elements
300
        self.elements_by_type = elements_by_type
301
 
302
    @staticmethod
303
    def create_default_text_element(
304
        parent: Optional["DocumentGrammar"] = None,
305
        enable_mid: bool = False,
306
    ) -> GrammarElement:
307
        fields: List[GrammarElementFieldType] = []
308
        if enable_mid:
309
            fields.append(
310
                GrammarElementFieldString(
311
                    parent=None,
312
                    title=RequirementFieldName.MID,
313
                    human_title=None,
314
                    required="False",
315
                )
316
            )
317
        fields.extend(
318
            [
319
                GrammarElementFieldString(
320
                    parent=None,
321
                    title=RequirementFieldName.UID,
322
                    human_title=None,
323
                    required="False",
324
                ),
325
                GrammarElementFieldString(
326
                    parent=None,
327
                    title=RequirementFieldName.STATEMENT,
328
                    human_title=None,
329
                    required="True",
330
                ),
331
            ]
332
        )
333
        text_element = GrammarElement(
334
            parent=parent,
335
            tag="TEXT",
336
            property_is_composite="",
337
            property_prefix="",
338
            property_view_style="",
339
            fields=fields,
340
            relations=[],
341
        )
342
        return text_element
343
 
344
    @staticmethod
345
    def create_default_section_element(
346
        parent: Optional["DocumentGrammar"] = None, enable_mid: bool = False
347
    ) -> GrammarElement:
348
        fields: List[GrammarElementFieldType] = []
349
        if enable_mid:
350
            fields.append(
351
                GrammarElementFieldString(
352
                    parent=None,
353
                    title=RequirementFieldName.MID,
354
                    human_title=None,
355
                    required="False",
356
                )
357
            )
358
        fields.append(
359
            GrammarElementFieldString(
360
                parent=None,
361
                title=RequirementFieldName.UID,
362
                human_title=None,
363
                required="False",
364
            )
365
        )
366
        fields.append(
367
            GrammarElementFieldString(
368
                parent=None,
369
                title=RequirementFieldName.LEVEL,
370
                human_title=None,
371
                required="False",
372
            )
373
        )
374
        fields.append(
375
            GrammarElementFieldString(
376
                parent=None,
377
                title=RequirementFieldName.PREFIX,
378
                human_title=None,
379
                required="False",
380
            )
381
        )
382
        fields.append(
383
            GrammarElementFieldString(
384
                parent=None,
385
                title=RequirementFieldName.TITLE,
386
                human_title=None,
387
                required="True",
388
            ),
389
        )
390
        section_element = GrammarElement(
391
            parent=parent,
392
            tag="SECTION",
393
            property_is_composite="True",
394
            property_prefix="",
395
            property_view_style="",
396
            fields=fields,
397
            relations=[],
398
        )
399
        return section_element
400
 
401
 
402
class DocumentGrammarWrapper:
403
    def __init__(self, grammar: DocumentGrammar):
404
        assert isinstance(grammar, DocumentGrammar), grammar
405
        self.grammar: DocumentGrammar = grammar