Passed
Pull Request — master (#6)
by
unknown
02:23
created

NavigationBuilder::addContainersFromStack()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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