Passed
Pull Request — master (#6)
by
unknown
01:51
created

NavigationBuilder::build()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 6
nc 3
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Everlution\Navigation\Advanced\Builder;
6
7
use Everlution\Navigation\Advanced\Item\AdvancedNavigationInterface;
8
use Everlution\Navigation\Advanced\Item\NestableInterface;
9
use Everlution\Navigation\Builder\MatcherInterface;
10
use Everlution\Navigation\Builder\NoCurrentItemFoundException;
11
use Everlution\Navigation\ContainerInterface;
12
13
/**
14
 * Class NavigationBuilder.
15
 *
16
 * @author Ivan Barlog <[email protected]>
17
 */
18
class NavigationBuilder
19
{
20
    /** @var AdvancedNavigationInterface */
21
    private $container;
22
    /** @var ParentNode */
23
    private $root;
24
    /** @var ContainerInterface[] */
25
    private $stack = [];
26
    /** @var ContainerInterface[] */
27
    private $used = [];
28
    /** @var ParentNode */
29
    private $current;
30
    /** @var MatcherInterface */
31
    private $matcher;
32
33
    public function __construct(AdvancedNavigationInterface $container, MatcherInterface $matcher)
34
    {
35
        $this->container = $container;
36
        $this->matcher = $matcher;
37
        $this->build($matcher);
38
    }
39
40
    /**
41
     * @return ParentNode
42
     *
43
     * @throws NoCurrentItemFoundException
44
     */
45
    public function getCurrent(): ParentNode
46
    {
47
        if (!$this->current) {
48
            throw new NoCurrentItemFoundException();
49
        }
50
51
        return $this->current;
52
    }
53
54
    private function build(MatcherInterface $matcher)
55
    {
56
        $this->root = new ParentNode($this->getRootContainer($this->container->getRoot()));
57
        $this->setCurrentNode($matcher, $this->root);
58
59
        /** @var ContainerInterface $container */
60
        foreach ($this->container->getContainers() as $container) {
61
            if ($container instanceof NestableInterface) {
62
                $this->walkToRootContainer($container);
63
                $this->addContainersFromStack();
64
            }
65
        }
66
    }
67
68
    private function getParent(NestableInterface $item): ContainerInterface
69
    {
70
        return $this->container->get($item->getParentNavigation());
71
    }
72
73
    private function walkToRootContainer(ContainerInterface $container): ContainerInterface
74
    {
75
        $this->stack[] = $container;
76
77
        if (!$container instanceof NestableInterface) {
78
            return $container;
79
        }
80
81
        return $this->walkToRootContainer($this->getParent($container));
82
    }
83
84
    private function getRootContainer(ContainerInterface $container): ContainerInterface
85
    {
86
        if (!$container instanceof NestableInterface) {
87
            return $container;
88
        }
89
90
        return $this->getRootContainer($this->getParent($container));
91
    }
92
93
    private function addContainersFromStack(): void
94
    {
95
        $root = $this->root;
96
        while ($container = array_pop($this->stack)) {
97
            $this->addContainer($root, $container);
98
            $root = $root->get(get_class($container));
99
        }
100
    }
101
102
    private function addContainer(ParentNode $root, ContainerInterface $container): void
103
    {
104
        $name = get_class($container);
105
        if (array_key_exists($name, $this->used)) {
106
            return;
107
        }
108
109
        $parentNode = new ParentNode($container);
110
        $parentNode->setParent($root);
111
        $this->used[$name] = $parentNode;
112
        $root->addChild($parentNode);
113
        $this->setCurrentNode($this->matcher, $parentNode);
114
    }
115
116
    private function setCurrentNode(MatcherInterface $matcher, ParentNode $node): void
117
    {
118
        if (!$this->current && $matcher->isCurrent($node->getContainer())) {
119
            $this->current = $node;
120
        }
121
    }
122
}
123