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

NavigationBuilder::isUsed()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Everlution\Navigation\Nested\Builder;
6
7
use Everlution\Navigation\Nested\AdvancedNavigationInterface;
8
use Everlution\Navigation\Nested\Container\ContainerNotFoundException;
9
use Everlution\Navigation\Nested\Container\NestableContainerInterface;
10
use Everlution\Navigation\Builder\MatcherInterface;
11
use Everlution\Navigation\Builder\NoCurrentItemFoundException;
12
use Everlution\Navigation\ContainerInterface;
13
14
/**
15
 * Class NavigationBuilder
16
 *
17
 * @author Martin Lutter <[email protected]>
18
 */
19
class NavigationBuilder
20
{
21
    /** @var \Everlution\Navigation\Nested\AdvancedNavigationInterface */
22
    private $navigation;
23
    /** @var ParentNode */
24
    private $root;
25
    /** @var ContainerInterface[] */
26
    private $stack = [];
27
    /** @var ContainerInterface[] */
28
    private $used = [];
29
    /** @var ParentNode */
30
    private $current;
31
    /** @var MatcherInterface */
32
    private $matcher;
33
34
    public function __construct(AdvancedNavigationInterface $container, MatcherInterface $matcher)
35
    {
36
        $this->navigation = $container;
37
        $this->matcher = $matcher;
38
        $this->build($matcher);
39
    }
40
41
    /**
42
     * @return ParentNode
43
     *
44
     * @throws NoCurrentItemFoundException
45
     */
46
    public function getCurrent(): ParentNode
47
    {
48
        if (!$this->current) {
49
            throw new NoCurrentItemFoundException();
50
        }
51
52
        return $this->current;
53
    }
54
55
    /**
56
     * @param MatcherInterface $matcher
57
     */
58
    private function build(MatcherInterface $matcher)
59
    {
60
        $this->root = new ParentNode($this->navigation->getRoot());
61
        $this->setCurrentNode($matcher, $this->root);
62
        $this->addToUsed($this->root);
63
64
        foreach ($this->navigation->getNavigationContainers() as $container) {
65
            $this->walkToRootContainer($container);
66
            $this->addContainersFromStack();
67
        }
68
    }
69
70
    /**
71
     * @param NestableContainerInterface $item
72
     * @return NestableContainerInterface
73
     * @throws ContainerNotFoundException
74
     */
75
    private function getParent(NestableContainerInterface $item): NestableContainerInterface
76
    {
77
        return $this->navigation->get($item->getParentContainer());
78
    }
79
80
    /**
81
     * @param NestableContainerInterface $container
82
     * @return NestableContainerInterface
83
     */
84
    private function walkToRootContainer(NestableContainerInterface $container): NestableContainerInterface
85
    {
86
        $this->stack[] = $container;
87
88
        try {
89
            return $this->walkToRootContainer($this->getParent($container));
90
        } catch (ContainerNotFoundException $e) {
91
            return $container;
92
        }
93
    }
94
95
    private function addContainersFromStack(): void
96
    {
97
        $root = $this->root;
98
        while ($container = array_pop($this->stack)) {
99
            if (false === $this->isUsed($container)) {
100
                $this->addContainer($root, $container);
101
            }
102
103
            $root = $root->get(get_class($container));
104
        }
105
    }
106
107
    /**
108
     * @param ParentNode $root
109
     * @param NestableContainerInterface $container
110
     */
111
    private function addContainer(ParentNode $root, NestableContainerInterface $container): void
112
    {
113
        $parentNode = new ParentNode($container);
114
115
        $root->addChild($parentNode);
116
        $this->addToUsed($parentNode);
117
        $this->setCurrentNode($this->matcher, $parentNode);
118
    }
119
120
    /**
121
     * @param MatcherInterface $matcher
122
     * @param ParentNode $node
123
     */
124
    private function setCurrentNode(MatcherInterface $matcher, ParentNode $node): void
125
    {
126
        if (!$this->current && $matcher->isCurrent($node->getContainer())) {
127
            $this->current = $node;
128
        }
129
    }
130
131
    /**
132
     * @param ContainerInterface $container
133
     * @return bool
134
     */
135
    private function isUsed(ContainerInterface $container): bool
136
    {
137
        $name = get_class($container);
138
139
        return isset($this->used[$name]);
140
    }
141
142
    /**
143
     * @param ParentNode $node
144
     */
145
    private function addToUsed(ParentNode $node): void
146
    {
147
        $name = get_class($node->getContainer());
148
        $this->used[$name] = $node;
149
    }
150
}
151