Completed
Push — master ( 57e7bf...8a74a7 )
by P.R.
01:36
created

Node.is_document_root()   A

Complexity

Conditions 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %
Metric Value
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:
0 ignored issues
show
best-practice introduced by
Too many public methods (21/20)
Loading history...
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
0 ignored issues
show
Coding Style Naming introduced by
The name id does not conform to the attribute naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
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):
1 ignored issue
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
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):
2 ignored issues
show
Unused Code introduced by
The argument parent_hierarchy_level seems to be unused.
Loading history...
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
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):
1 ignored issue
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
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):
1 ignored issue
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
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):
1 ignored issue
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
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):
1 ignored issue
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
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
                # Appending in NodeStore labels list.
303
                if node.argument not in node_store.labels:
304
                    node_store.labels[node.argument] = self.argument
305
                else:
306
                    raise NameError('Duplicate label', node.argument)
307
308
                # Removing node from child nodes.
309
                self.child_nodes.remove(node.id)
310
311
            node.parse_labels()
312
313
            out_scope(node)
314
315
    # ------------------------------------------------------------------------------------------------------------------
316
    def set_id_heading_node(self):
317
        """
318
        Sets id to heading node. (Argument of first label)
319
        """
320
        node = in_scope(self.labels[0])
321
        self._options['id'] = node.argument
322
        out_scope(node)
323
324
    # ------------------------------------------------------------------------------------------------------------------
325
    def change_ref_argument(self):
326
        """
327
        Changes reference argument on number of depending heading node.
328
        """
329
        for node_id in self.child_nodes:
330
            node = in_scope(node_id)
331
332
            if node.argument in node_store.labels and node.get_command() == 'ref':
333
                node.set_option_value('href', '#{0}'.format(node.argument))
334
                node.argument = node_store.labels[node.argument]
335
336
            node.change_ref_argument()
337
338
            out_scope(node)
339
340
# ----------------------------------------------------------------------------------------------------------------------
341