Passed
Push — master ( 8ff2fc...2dab2b )
by P.R.
04:18 queued 10s
created

sdoc.sdoc2.node.HeadingNode   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 183
Duplicated Lines 91.8 %

Test Coverage

Coverage 86.25%

Importance

Changes 0
Metric Value
wmc 26
eloc 83
dl 168
loc 183
ccs 69
cts 80
cp 0.8625
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A HeadingNode.split_text_nodes() 20 20 4
A HeadingNode.get_hierarchy_name() 5 5 1
A HeadingNode.prepare_content_tree() 13 13 1
A HeadingNode.is_inline_command() 5 5 1
A HeadingNode.is_block_command() 5 5 1
A HeadingNode.set_numbering() 12 12 4
A HeadingNode.number() 19 19 3
A HeadingNode.__init__() 13 13 1
B HeadingNode.create_paragraphs() 39 39 7
A HeadingNode.set_toc_id() 11 11 3

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1 1
from abc import ABC
2 1
from typing import Any, Dict
3
4 1
from cleo.styles import OutputStyle
5
6 1
import sdoc
7 1
from sdoc.sdoc2 import in_scope, out_scope
8 1
from sdoc.sdoc2.helper.Enumerable import Enumerable
9 1
from sdoc.sdoc2.node.EndParagraphNode import EndParagraphNode
10 1
from sdoc.sdoc2.node.Node import Node
11 1
from sdoc.sdoc2.node.TextNode import TextNode
12 1
from sdoc.sdoc2.NodeStore import NodeStore
13
14
15 1 View Code Duplication
class HeadingNode(Node, ABC):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
16
    """
17
    Abstract class for heading nodes.
18
    """
19
20
    # ------------------------------------------------------------------------------------------------------------------
21 1
    def __init__(self, io: OutputStyle, name: str, options: Dict[str, str], argument: str):
22
        """
23
        Object constructor.
24
25
        :param OutputStyle io: The IO object.
26
        :param str name: The (command) name of this heading.
27
        :param dict[str,str] options: The options of this heading.
28
        :param str argument: The title of this heading.
29
        """
30 1
        super().__init__(io, name, options, argument)
31
32 1
        self.numbering: bool = True
33 1
        """
34
        The True the node must be numbered.
35
        """
36
37
    # ------------------------------------------------------------------------------------------------------------------
38 1
    def get_hierarchy_name(self) -> str:
39
        """
40
        Returns 'sectioning'.
41
        """
42 1
        return 'sectioning'
43
44
    # ------------------------------------------------------------------------------------------------------------------
45 1
    def is_block_command(self) -> bool:
46
        """
47
        Returns False.
48
        """
49 1
        return False
50
51
    # ------------------------------------------------------------------------------------------------------------------
52 1
    def is_inline_command(self) -> bool:
53
        """
54
        Returns True.
55
        """
56
        return True
57
58
    # ------------------------------------------------------------------------------------------------------------------
59 1
    def number(self, enumerable_numbers: Dict[str, Any]):
60
        """
61
        Sets number of heading nodes.
62
63
        :param dict[str,any] enumerable_numbers:
64
        """
65 1
        if 'heading' not in enumerable_numbers:
66 1
            enumerable_numbers['heading'] = Enumerable()
67
68 1
        enumerable_numbers['heading'].generate_numeration(self.get_hierarchy_level())
69 1
        enumerable_numbers['heading'].increment_last_level()
70 1
        enumerable_numbers['heading'].remove_starting_zeros()
71
72 1
        if 'part' in enumerable_numbers:
73
            self._options['part_number'] = enumerable_numbers['part'].get_string()
74
75 1
        self._options['number'] = enumerable_numbers['heading'].get_string()
76
77 1
        super().number(enumerable_numbers)
78
79
    # ------------------------------------------------------------------------------------------------------------------
80 1
    def set_toc_id(self) -> None:
81
        """
82
        Set ID for table of contents.
83
        """
84
        if 'id' not in self._options:
85
            if 'part_number' in self._options:
86
                self._options['id'] = '{}:{}:{}'.format(self.name,
87
                                                        self._options['part_number'],
88
                                                        self._options['number'])
89
            else:
90
                self._options['id'] = '{}:{}'.format(self.name, self._options['number'])
91
92
    # ------------------------------------------------------------------------------------------------------------------
93 1
    def prepare_content_tree(self) -> None:
94
        """
95
        Prepares the content tree. Create paragraph nodes.
96
        """
97 1
        super().prepare_content_tree()
98
99 1
        self.set_numbering()
100
101
        # Adding the id's of split text in 'new_child_nodes1' list.
102 1
        self.split_text_nodes()
103
104
        # Creating paragraphs and add all id's in 'new_child_nodes2' list.
105 1
        self.create_paragraphs()
106
107
    # ------------------------------------------------------------------------------------------------------------------
108 1
    def set_numbering(self) -> None:
109
        """
110
        Sets the numbering status to the heading node.
111
        """
112 1
        if 'numbering' in self._options:
113
            if self._options['numbering'] == 'off':
114
                self.numbering = False
115
            elif self._options['numbering'] == 'on':
116
                self.numbering = True
117
            else:
118
                NodeStore.error("Invalid value '{}' for attribute 'numbering'. Allowed values are 'on' and 'off'.".
119
                                format(self._options['numbering']), self)
120
121
    # ------------------------------------------------------------------------------------------------------------------
122 1
    def split_text_nodes(self) -> None:
123
        """
124
        Replaces single text nodes that contains a paragraph separator (i.e. a double new line) with multiple text nodes
125
        without paragraph separator.
126
        """
127 1
        new_child_nodes = []
128
129 1
        for node_id in self.child_nodes:
130 1
            node = in_scope(node_id)
131
132 1
            if isinstance(node, TextNode):
133 1
                list_ids = node.split_by_paragraph()
134 1
                for ids in list_ids:
135 1
                    new_child_nodes.append(ids)
136
            else:
137 1
                new_child_nodes.append(node.id)
138
139 1
            out_scope(node)
140
141 1
        self.child_nodes = new_child_nodes
142
143
    # ------------------------------------------------------------------------------------------------------------------
144 1
    def create_paragraphs(self) -> None:
145
        """
146
        Create paragraph nodes.
147
148
        A paragraph consists of phrasing nodes only. Each continuous slice of phrasing child nodes is move to a
149
        paragraph node.
150
        """
151 1
        new_child_nodes = []
152 1
        paragraph_node = None
153
154 1
        for node_id in self.child_nodes:
155 1
            node = in_scope(node_id)
156
157 1
            if node.is_phrasing():
158 1
                if not paragraph_node:
159 1
                    paragraph_node = sdoc.sdoc2.node_store.create_inline_node('paragraph')
160 1
                    new_child_nodes.append(paragraph_node.id)
161
162 1
                paragraph_node.append_child_node(node)
163
            else:
164 1
                if paragraph_node:
165 1
                    paragraph_node.prune_whitespace()
166 1
                    sdoc.sdoc2.node_store.store_node(paragraph_node)
167 1
                    paragraph_node = None
168
169
                # End paragraph nodes are created temporary to separate paragraphs in a flat list of (text) node. There
170
                # role ae replaced by the content hierarchy now. So, we must no store end paragraph nodes.
171 1
                if not isinstance(node, EndParagraphNode):
172 1
                    new_child_nodes.append(node.id)
173
174 1
            out_scope(node)
175
176 1
        if paragraph_node:
177 1
            paragraph_node.prune_whitespace()
178 1
            sdoc.sdoc2.node_store.store_node(paragraph_node)
179
            # paragraph_node = None
180
181
        # Setting child nodes.
182 1
        self.child_nodes = new_child_nodes
183
184
# ----------------------------------------------------------------------------------------------------------------------
185