Completed
Push — master ( f38e95...1522e3 )
by Martin
03:50
created

AttributesProcessor   A

Complexity

Total Complexity 40

Size/Duplication

Total Lines 112
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 97.87%

Importance

Changes 0
Metric Value
wmc 40
lcom 1
cbo 9
dl 0
loc 112
ccs 46
cts 47
cp 0.9787
rs 9.2
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
C processDocument() 0 38 17
C findTargetAndDirection() 0 39 13
A getPrevious() 0 10 4
A getNext() 0 10 4
A isAttributesNode() 0 4 2

How to fix   Complexity   

Complex Class

Complex classes like AttributesProcessor often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AttributesProcessor, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This is part of the webuni/commonmark-attributes-extension package.
7
 *
8
 * (c) Martin Hasoň <[email protected]>
9
 * (c) Webuni s.r.o. <[email protected]>
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace Webuni\CommonMark\AttributesExtension;
16
17
use League\CommonMark\Block\Element\AbstractBlock;
18
use League\CommonMark\Block\Element\Document;
19
use League\CommonMark\Block\Element\FencedCode;
20
use League\CommonMark\Block\Element\ListBlock;
21
use League\CommonMark\Block\Element\ListItem;
22
use League\CommonMark\Inline\Element\AbstractInline;
23
use League\CommonMark\Node\Node;
24
25
final class AttributesProcessor
26
{
27
    private const DIRECTION_PREFIX = 'prefix';
28 5
29
    private const DIRECTION_SUFFIX = 'suffix';
30 5
31 5
    public function processDocument(Document $document): void
32 5
    {
33
        $walker = $document->walker();
34 5
        while ($event = $walker->next()) {
35 5
            $node = $event->getNode();
36
            if (!$node instanceof AttributesInline && ($event->isEntering() || !$node instanceof Attributes)) {
37
                continue;
38 5
            }
39
40 5
            list($target, $direction) = $this->findTargetAndDirection($node);
41 5
42 1
            if ($target instanceof Node) {
43
                $parent = $target->parent();
44
                if ($parent instanceof ListItem && $parent->parent() instanceof ListBlock && $parent->parent()->isTight()) {
45 5
                    $target = $parent;
46 4
                }
47
48 3
                if (self::DIRECTION_SUFFIX === $direction) {
49
                    $attributes = AttributesUtils::merge($target, $node->getAttributes());
50
                } else {
51 5
                    $attributes = AttributesUtils::merge($node->getAttributes(), $target);
52
                }
53
54 5
                if ($target instanceof AbstractBlock || $target instanceof AbstractInline) {
55 3
                    $target->data['attributes'] = $attributes;
56
                }
57
            }
58 5
59
            if ($node instanceof AbstractBlock && $node->endsWithBlankLine() && $node->next() && $node->previous()) {
60 5
                $previous = $node->previous();
61
                if ($previous instanceof AbstractBlock) {
62 5
                    $previous->setLastLineBlank(true);
63
                }
64 5
            }
65 5
66 5
            $node->detach();
67 5
        }
68 5
    }
69 5
70
    private function findTargetAndDirection(Node $node): array
71 5
    {
72 1
        $target = null;
73 1
        $direction = null;
74
        $previous = $next = $node;
75 1
        while (true) {
76
            $previous = $this->getPrevious($previous);
77
            $next = $this->getNext($next);
78 5
79 4
            if (null === $previous && null === $next) {
80 4
                if (!$node->parent() instanceof FencedCode) {
81
                    $target = $node->parent();
82 4
                    $direction = self::DIRECTION_SUFFIX;
83
                }
84
85 3
                break;
86 3
            }
87 3
88
            if ($node instanceof AttributesInline && (null === $previous || ($previous instanceof AbstractInline && $node->isBlock()))) {
89 3
                continue;
90
            }
91
92
            if (null !== $previous && !$this->isAttributesNode($previous)) {
93 5
                $target = $previous;
94
                $direction = self::DIRECTION_SUFFIX;
95
96 5
                break;
97
            }
98 5
99
            if (null !== $next && !$this->isAttributesNode($next)) {
100 5
                $target = $next;
101 2
                $direction = self::DIRECTION_PREFIX;
102
103
                break;
104 5
            }
105
        }
106
107 5
        return [$target, $direction];
108
    }
109 5
110 5
    private function getPrevious(Node $node = null): ?Node
111
    {
112
        $previous = $node instanceof Node ? $node->previous() : null;
113
114
        if ($previous instanceof AbstractBlock && $previous->endsWithBlankLine()) {
115
            $previous = null;
116
        }
117
118
        return $previous;
119
    }
120
121
    private function getNext(Node $node = null): ?Node
122
    {
123
        $next = $node instanceof Node ? $node->next() : null;
124
125
        if ($node instanceof AbstractBlock && $node->endsWithBlankLine()) {
126
            $next = null;
127
        }
128
129
        return $next;
130
    }
131
132
    private function isAttributesNode(Node $node): bool
133
    {
134
        return $node instanceof Attributes || $node instanceof AttributesInline;
135
    }
136
}
137