PreloadedGraphRepository::isSuitableEdge()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 2
1
<?php
2
3
namespace Smoren\GraphTools\Store;
4
5
use Smoren\GraphTools\Conditions\Interfaces\FilterConditionInterface;
6
use Smoren\GraphTools\Exceptions\RepositoryException;
7
use Smoren\GraphTools\Models\Interfaces\EdgeInterface;
8
use Smoren\GraphTools\Models\Interfaces\VertexInterface;
9
use Smoren\GraphTools\Store\Interfaces\GraphRepositoryInterface;
10
use Smoren\GraphTools\Structs\Interfaces\TraverseStepIteratorInterface;
11
use Smoren\GraphTools\Structs\TraverseStepItem;
12
use Smoren\GraphTools\Structs\TraverseStepIterator;
13
14
/**
15
 * Graph repository implementation with data storage in RAM
16
 * @author Smoren <[email protected]>
17
 */
18
class PreloadedGraphRepository implements GraphRepositoryInterface
19
{
20
    /**
21
     * @var array<string, VertexInterface> vertexes map by id
22
     */
23
    protected array $vertexMap = [];
24
    /**
25
     * @var array<string, EdgeInterface> edges map by id
26
     */
27
    protected array $edgesMap = [];
28
    /**
29
     * @var array<string, array<string, string[]>> links map: array<vertexFromId array<edgeId, [edgeType, vertexToId]>>
30
     */
31
    protected array $edgesDirectMap = [];
32
    /**
33
     * @var array<string, array<string, string[]>> links map: array<vertexToId array<edgeId, [edgeType, vertexFromId]>>
34
     */
35
    protected array $edgesReverseMap = [];
36
37
    /**
38
     * SimpleGraphRepository constructor
39
     * @param array<VertexInterface> $vertexes vertexes list
40
     * @param array<EdgeInterface> $edges edges list
41
     */
42
    public function __construct(
43
        array $vertexes,
44
        array $edges
45
    ) {
46
        foreach($vertexes as $vertex) {
47
            $this->vertexMap[$vertex->getId()] = $vertex;
48
        }
49
50
        foreach($edges as $edge) {
51
            $this->edgesMap[$edge->getId()] = $edge;
52
53
            $this->setToMap(
54
                $this->edgesDirectMap,
55
                [$edge->getFromId(), $edge->getId()],
56
                [$edge->getType(), $edge->getToId()]
57
            );
58
59
            $this->setToMap(
60
                $this->edgesReverseMap,
61
                [$edge->getToId(), $edge->getId()],
62
                [$edge->getType(), $edge->getFromId()]
63
            );
64
        }
65
    }
66
67
    /**
68
     * @inheritDoc
69
     * @throws RepositoryException
70
     */
71
    public function getVertexById(string $id): VertexInterface
72
    {
73
        if(!isset($this->vertexMap[$id])) {
74
            throw new RepositoryException(
75
                "vertex with id '{$id}' not exist",
76
                RepositoryException::VERTEX_NOT_FOUND
77
            );
78
        }
79
        return $this->vertexMap[$id];
80
    }
81
82
    /**
83
     * @inheritDoc
84
     * @throws RepositoryException
85
     */
86
    public function getEdgeById(string $id): EdgeInterface
87
    {
88
        if(!isset($this->edgesMap[$id])) {
89
            throw new RepositoryException(
90
                "edge with id '{$id}' not exist",
91
                RepositoryException::EDGE_NOT_FOUND
92
            );
93
        }
94
        return $this->edgesMap[$id];
95
    }
96
97
    /**
98
     * @inheritDoc
99
     * @throws RepositoryException
100
     */
101
    public function getNextVertexes(
102
        VertexInterface $vertex,
103
        ?FilterConditionInterface $condition = null
104
    ): TraverseStepIteratorInterface {
105
        return $this->getLinkedVertexesFromMap(
106
            $this->edgesDirectMap,
107
            $vertex,
108
            $condition
109
        );
110
    }
111
112
    /**
113
     * @inheritDoc
114
     * @throws RepositoryException
115
     */
116
    public function getPrevVertexes(
117
        VertexInterface $vertex,
118
        ?FilterConditionInterface $condition = null
119
    ): TraverseStepIteratorInterface {
120
        return $this->getLinkedVertexesFromMap(
121
            $this->edgesReverseMap,
122
            $vertex,
123
            $condition
124
        );
125
    }
126
127
    /**
128
     * Get next vertexes from given map by current vertex
129
     * @param array<string, array<string, string[]>> $source source map
130
     * @param VertexInterface $vertex current vertex
131
     * @param FilterConditionInterface|null $condition filter condition
132
     * @return TraverseStepIteratorInterface next vertexes iterator
133
     * @throws RepositoryException
134
     */
135
    protected function getLinkedVertexesFromMap(
136
        array $source,
137
        VertexInterface $vertex,
138
        ?FilterConditionInterface $condition
139
    ): TraverseStepIteratorInterface {
140
        $result = [];
141
        foreach($source[$vertex->getId()] ?? [] as $edgeId => [$edgeType, $targetId]) {
142
            $edge = $this->getEdgeById($edgeId);
143
            if($this->isSuitableEdge($edge, $condition)) {
144
                $target = $this->getVertexById($targetId);
145
                if($this->isSuitableVertex($target, $condition)) {
146
                    $result[] = new TraverseStepItem($edge, $target);
147
                }
148
            }
149
        }
150
        return new TraverseStepIterator($result);
151
    }
152
153
    /**
154
     * Returns true if given vertex matches filter condition
155
     * @param VertexInterface $vertex vertex to check
156
     * @param FilterConditionInterface|null $condition filter condition
157
     * @return bool
158
     */
159
    protected function isSuitableVertex(VertexInterface $vertex, ?FilterConditionInterface $condition): bool
160
    {
161
        return ($condition === null) || $condition->isSuitableVertex($vertex);
162
    }
163
164
    /**
165
     * Returns true if given edge matches filter condition
166
     * @param EdgeInterface $edge edge to check
167
     * @param FilterConditionInterface|null $condition filter condition
168
     * @return bool
169
     */
170
    protected function isSuitableEdge(EdgeInterface $edge, ?FilterConditionInterface $condition): bool
171
    {
172
        return ($condition === null) || $condition->isSuitableEdge($edge);
173
    }
174
175
    /**
176
     * @param array<string, mixed> $map
177
     * @param array<string> $path
178
     * @param array<string> $value
179
     * @return void
180
     */
181
    protected function setToMap(array &$map, array $path, array $value): void
182
    {
183
        foreach ($path as $key) {
184
            /** @var array<string, mixed> $map */
185
            $map = &$map[$key];
186
        }
187
        $map = $value;
188
    }
189
}
190