Completed
Push — master ( 687beb...e8fc5f )
by P.R.
01:57
created

Node.is_hierarchy_root()   A

Complexity

Conditions 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 7
rs 9.4285
cc 1
1
"""
2
SDoc
3
4
Copyright 2016 Set Based IT Consultancy
5
6
Licence MIT
7
"""
8
# ----------------------------------------------------------------------------------------------------------------------
9
import abc
10
from sdoc.sdoc2 import in_scope, out_scope, node_store
11
12
13
class Node:
14
    """
15
    Abstract class for SDoc2 nodes.
16
    """
17
18
    # ------------------------------------------------------------------------------------------------------------------
19
    def __init__(self, name, options=None, argument=''):
20
        """
21
        Object constructor.
22
23
        :param str name: The (command) name of this node.
24
        :param dict[str,str] options: The options of this node.
25
        :param str argument: The argument of this node (inline commands only).
26
        """
27
        self.id = 0
28
        """
29
        The ID of this SDoc2 node.
30
31
        :type: int
32
        """
33
34
        self.name = name
35
        """
36
        The (command) name of this node.
37
38
        :type: str
39
        """
40
41
        self._argument = argument
42
        """
43
        The argument of this node (inline commands only).
44
45
        :type: str
46
        """
47
48
        self._options = options if options else {}
49
        """
50
        The options of this node.
51
52
        :type: dict[str,int|str]
53
        """
54
55
        self.child_nodes = []
56
        """
57
        The ID's of the SDoc2 child nodes of this SDoc2 node.
58
59
        :type: list[int]
60
        """
61
62
        self.position = None
63
        """
64
        The position where this node is defined.
65
66
        :type: None|sdoc.sdoc2.Position.Position
67
        """
68
69
        self.labels = []
70
        """
71
        The list of labels in the node.
72
73
        :type:
74
        """
75
76
    # ------------------------------------------------------------------------------------------------------------------
77
    @property
78
    def argument(self):
79
        """
80
        Getter for argument.
81
82
        :rtype: str
83
        """
84
        return self._argument
85
86
    # ------------------------------------------------------------------------------------------------------------------
87
    @argument.setter
88
    def argument(self, new_argument):
89
        """
90
        Setter for argument.
91
92
        :param str new_argument: The new argument.
93
        """
94
        self._argument = new_argument
95
96
    # ------------------------------------------------------------------------------------------------------------------
97
    def print_info(self, level):
98
        """
99
        Temp function for development.
100
101
        :param int level: the level of block commands.
102
        """
103
        print("{0!s}{1:4d} {2!s}".format(' ' * 4 * level, self.id, self.name))
104
        for node_id in self.child_nodes:
105
            node = in_scope(node_id)
106
107
            node.print_info(level + 1)
108
109
            out_scope(node)
110
111
    # ------------------------------------------------------------------------------------------------------------------
112
    def get_hierarchy_name(self):
113
        """
114
        Returns the hierarchy name if this node is a part of a hierarchy. Otherwise returns False.
115
116
        :rtype: str|bool
117
        """
118
        return False
119
120
    # ------------------------------------------------------------------------------------------------------------------
121
    @abc.abstractmethod
122
    def get_command(self):
123
        """
124
        Returns command of this node.
125
126
        :rtype: str
127
        """
128
        raise NotImplementedError()
129
130
    # ------------------------------------------------------------------------------------------------------------------
131
    def get_hierarchy_level(self, parent_hierarchy_level=-1):
132
        """
133
        Returns the hierarchy level if this node is a part of a hierarchy.
134
135
        :param int parent_hierarchy_level: The hierarchy level of the parent node in the same hierarchy.
136
137
        :rtype: int
138
        """
139
        raise RuntimeError("This method MUST only be called when a node is a part of an hierarchy.")
140
141
    # ------------------------------------------------------------------------------------------------------------------
142
    def get_option_value(self, option_name):
143
        """
144
        Returns the value of an option. Returns None if the option is not set.
145
146
        :param str option_name: The name of the option.
147
148
        :rtype: str
149
        """
150
        return self._options[option_name] if option_name in self._options else None
151
152
    # ------------------------------------------------------------------------------------------------------------------
153
    def set_option_value(self, option, value):
154
        """
155
        Sets value for option.
156
157
        :param str option: The name of an option
158
        :param mixed value: The value of an option
159
        """
160
        self._options[option] = value
161
162
    # ------------------------------------------------------------------------------------------------------------------
163
    @abc.abstractmethod
164
    def is_block_command(self):
165
        """
166
        Returns True if this node is created by a block command. Otherwise returns False.
167
168
        :rtype: bool
169
        """
170
        raise NotImplementedError()
171
172
    # ------------------------------------------------------------------------------------------------------------------
173
    def is_document_root(self):
174
        """
175
        Returns True if this node is a document root node. Otherwise returns False.
176
177
        :rtype: bool
178
        """
179
        return False
180
181
    # ------------------------------------------------------------------------------------------------------------------
182
    def is_hierarchy_root(self):
183
        """
184
        Returns True if this node can be the root of a hierarchy. Otherwise returns False.
185
186
        :rtype: bool
187
        """
188
        return False
189
190
    # ------------------------------------------------------------------------------------------------------------------
191
    @abc.abstractmethod
192
    def is_inline_command(self):
193
        """
194
        Returns True if this node is created by a inline command. Otherwise returns False.
195
196
        :rtype: bool
197
        """
198
        raise NotImplementedError()
199
200
    # ------------------------------------------------------------------------------------------------------------------
201
    def is_phrasing(self):
202
        """
203
        Returns True if this node is a phrasing node, i.e. is a part of a paragraph. Otherwise returns False.
204
205
        :rtype: bool
206
        """
207
        return False
208
209
    # ------------------------------------------------------------------------------------------------------------------
210
    def is_list_element(self):
211
        """
212
        Returns True if this node is a list element, e.g. an item in itemize. Otherwise returns False.
213
214
        :rtype: bool
215
        """
216
        return False
217
218
    # ------------------------------------------------------------------------------------------------------------------
219
    def append_child_node(self, child_node):
220
        """
221
        Appends a child node to the list of child nodes of the node.
222
223
        :param sdoc.sdoc2.node.Node.Node child_node: The new child node
224
        """
225
        self.child_nodes.append(child_node.id)
226
227
    # ------------------------------------------------------------------------------------------------------------------
228
    def prepare_content_tree(self):
229
        """
230
        Prepares this node for further processing.
231
        """
232
        for node_id in self.child_nodes:
233
            node = in_scope(node_id)
234
235
            node.prepare_content_tree()
236
237
            out_scope(node)
238
239
    # ------------------------------------------------------------------------------------------------------------------
240
    def number(self, numbers):
241
        """
242
        Numbers all numerable nodes such as chapters, sections, figures, and, items.
243
244
        :param numbers: The current numbers.
245
        """
246
        for node_id in self.child_nodes:
247
            node = in_scope(node_id)
248
249
            node.number(numbers)
250
251
            out_scope(node)
252
253
    # ------------------------------------------------------------------------------------------------------------------
254
    def get_enumerated_items(self):
255
        """
256
        Returns a list with a tuple with command and number of enumerated child nodes.
257
258
        Thi method is intended for unit test only.
259
260
        :rtype: list[(str,str)]
261
        """
262
        items = list()
263
264
        # First append the enumeration of this node (if any).
265
        if 'number' in self._options:
266
            items.append((self.get_command(), self._options['number'], self._argument))
267
268
        # Second append the enumeration of child nodes (if any).
269
        for node_id in self.child_nodes:
270
            node = in_scope(node_id)
271
272
            tmp = node.get_enumerated_items()
273
            if tmp:
274
                items.append(tmp)
275
276
            out_scope(node)
277
278
        return items
279
280
    # ------------------------------------------------------------------------------------------------------------------
281
    def parse_labels(self):
282
        """
283
        Parses all labels and call methods to collect labels.and for
284
        """
285
        self.modify_label_list()
286
287
        if self.labels:
288
            self.set_id_heading_node()
289
290
    # ------------------------------------------------------------------------------------------------------------------
291
    def modify_label_list(self):
292
        """
293
        Creates label list for each heading node, and for node_store. Removes label nodes from child list.
294
        """
295
        for node_id in self.child_nodes:
296
            node = in_scope(node_id)
297
298
            if node.get_command() == 'label':
299
                # Appending in Node labels list.
300
                self.labels.append(node.id)
301
302
                self.append_label_list_in_node_store(node)
303
                    if self.get_option_value('number'):
0 ignored issues
show
introduced by
unexpected indent
Loading history...
304
                        label_arg = self.get_option_value('number')
305
                        title_attribute = self.argument
306
                    else:
307
                        label_arg = self.argument
308
                        title_attribute = None
309
310
                    node_store.labels[node.argument] = {'argument': label_arg,
311
                                                        'title': title_attribute}
312
313
                # Removing node from child nodes.
314
                self.child_nodes.remove(node.id)
315
316
            node.parse_labels()
317
318
            out_scope(node)
319
320
    # ------------------------------------------------------------------------------------------------------------------
321
    def append_label_list_in_node_store(self, node):
322
        """
323
        Appending in NodeStore labels list.
324
325
        :param sdoc.sdoc2.node.Node.Node node: The current node.
326
        """
327
        if node.argument not in node_store.labels:
328
            if self.argument:
329
                node_store.labels[node.argument] = self.argument
330
331
            else:
332
                if 'number' in self._options:
333
                    node_store.labels[node.argument] = self._options['number']
334
                else:
335
                    node_store.labels[node.argument] = node.argument
336
337
        else:
338
            # @todo log definitions of both labels
339
            raise NameError('Duplicate label', node.argument)
340
341
    # ------------------------------------------------------------------------------------------------------------------
342
    def set_id_heading_node(self):
343
        """
344
        Sets id to heading node. (Argument of first label)
345
        """
346
        node = in_scope(self.labels[0])
347
        self._options['id'] = node.argument
348
        out_scope(node)
349
350
    # ------------------------------------------------------------------------------------------------------------------
351
    def change_ref_argument(self):
352
        """
353
        Changes reference argument on number of depending heading node.
354
        """
355
        for node_id in self.child_nodes:
356
            node = in_scope(node_id)
357
358
            if node.argument in node_store.labels and node.get_command() == 'ref':
359
                node.set_option_value('href', '#{0}'.format(node.argument))
360
361
                if node_store.labels[node.argument]['title']:
362
                    node.set_option_value('title', node_store.labels[node.argument]['title'])
363
364
                node.argument = node_store.labels[node.argument]['argument']
365
366
            node.change_ref_argument()
367
368
            out_scope(node)
369
370
# ----------------------------------------------------------------------------------------------------------------------
371