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

NavigationBuilder::getRootContainer()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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