Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
100.00% |
1 / 1 |
|
100.00% |
17 / 17 |
CRAP | |
100.00% |
62 / 62 |
NestedGraph | |
100.00% |
1 / 1 |
|
100.00% |
17 / 17 |
33 | |
100.00% |
62 / 62 |
__construct | |
100.00% |
1 / 1 |
2 | |
100.00% |
7 / 7 |
|||
getRootGraph | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
getNodes | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
getEdges | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
createNode | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
addNode | |
100.00% |
1 / 1 |
2 | |
100.00% |
6 / 6 |
|||
getNode | |
100.00% |
1 / 1 |
2 | |
100.00% |
3 / 3 |
|||
hasNode | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
removeNode | |
100.00% |
1 / 1 |
3 | |
100.00% |
8 / 8 |
|||
createEdge | |
100.00% |
1 / 1 |
3 | |
100.00% |
3 / 3 |
|||
addEdge | |
100.00% |
1 / 1 |
3 | |
100.00% |
11 / 11 |
|||
getEdge | |
100.00% |
1 / 1 |
2 | |
100.00% |
3 / 3 |
|||
hasEdge | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
removeEdge | |
100.00% |
1 / 1 |
3 | |
100.00% |
8 / 8 |
|||
getParentNode | |
100.00% |
1 / 1 |
2 | |
100.00% |
1 / 1 |
|||
nodeAttrChanged | |
100.00% |
1 / 1 |
2 | |
100.00% |
3 / 3 |
|||
edgeAttrChanged | |
100.00% |
1 / 1 |
2 | |
100.00% |
3 / 3 |
|||
onAttrChanged | n/a |
0 / 0 |
1 | n/a |
0 / 0 |
<?php declare(strict_types = 1); | |
/* | |
* Copyright (c) 2017, Josef Kufner <josef@kufner.cz> | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*/ | |
namespace Smalldb\Graph; | |
class NestedGraph extends AbstractElement | |
{ | |
/** | |
* Nodes | |
* | |
* @var Node[] | |
*/ | |
private array $nodes = []; | |
/** | |
* @var Edge[] | |
*/ | |
private array $edges = []; | |
/** | |
* Node owning this graph if the graph is nested. | |
*/ | |
private ?Node $parentNode = null; | |
/** | |
* Root graph | |
*/ | |
protected ?Graph $rootGraph = null; | |
/** | |
* Node index | |
*/ | |
protected ElementAttrIndex $nodeAttrIndex; | |
/** | |
* Edge index | |
*/ | |
protected ElementAttrIndex $edgeAttrIndex; | |
/** | |
* Constructor. | |
*/ | |
protected function __construct(Node $parentNode = null, array $attrs = []) | |
{ | |
parent::__construct($attrs); | |
if ($parentNode) { | |
$this->parentNode = $parentNode; | |
$this->rootGraph = $this->parentNode->getGraph()->getRootGraph(); | |
$this->nodeAttrIndex = $this->rootGraph->nodeAttrIndex; | |
$this->edgeAttrIndex = $this->rootGraph->edgeAttrIndex; | |
} | |
} | |
public function getRootGraph(): Graph | |
{ | |
return $this->rootGraph; | |
} | |
/** | |
* Get all nodes in the graph | |
* | |
* @return Node[] | |
*/ | |
public function getNodes(): array | |
{ | |
return $this->nodes; | |
} | |
/** | |
* Get all edges in the graph | |
* | |
* @return Edge[] | |
*/ | |
public function getEdges(): array | |
{ | |
return $this->edges; | |
} | |
/** | |
* Create node and add it to the graph. | |
* | |
* @param string $id | |
* @param array $attrs | |
* @return Node | |
*/ | |
public function createNode(string $id, array $attrs = []): Node | |
{ | |
return new Node($this, $id, $attrs); | |
} | |
/** | |
* Add existing node to the graph. | |
* | |
* @param Node $node | |
* @return NestedGraph | |
*/ | |
public function addNode(Node $node): self | |
{ | |
$id = $node->getId(); | |
if (isset($this->nodes[$id])) { | |
throw new DuplicateNodeException(sprintf("Node \"%s\" already present in the graph.", $id)); | |
} | |
// Add element | |
$this->nodes[$id] = $node; | |
// Update attr indexes | |
$this->nodeAttrIndex->insertElement($node); | |
return $this; | |
} | |
/** | |
* Get node by its ID. | |
* | |
* @param string $id | |
* @return Node | |
*/ | |
public function getNode(string $id): Node | |
{ | |
if (isset($this->nodes[$id])) { | |
return $this->nodes[$id]; | |
} else { | |
throw new MissingNodeException(sprintf("Node \"%s\" not found in the graph.", $id)); | |
} | |
} | |
/** | |
* Returns true if a given node exists within the graph. | |
*/ | |
public function hasNode(string $id): bool | |
{ | |
return isset($this->nodes[$id]); | |
} | |
/** | |
* Remove node and connected edges from the graph. | |
* | |
* @param Node $node | |
* @return NestedGraph | |
*/ | |
public function removeNode(Node $node): self | |
{ | |
$id = $node->getId(); | |
if (isset($this->nodes[$id])) { | |
foreach ($node->getConnectedEdges() as $e) { | |
$e->remove(); | |
} | |
unset($this->nodes[$id]); | |
$this->nodeAttrIndex->removeElement($node); | |
return $this; | |
} else { | |
throw new MissingNodeException(sprintf("Node \"%s\" not found in the graph.", $id)); | |
} | |
} | |
/** | |
* Create a new edge and add it to the graph. | |
* | |
* @param string|null $id ID of the edge, generated automatically if null. | |
* @param array $attrs Key-value storage of the edge attributes. | |
* @param Node $start | |
* @param Node $end | |
* @return Edge | |
*/ | |
public function createEdge(?string $id, Node $start, Node $end, array $attrs = []): Edge | |
{ | |
if ($id === null) { | |
$id = '_' . count($this->getRootGraph()->getAllEdges()); | |
} | |
return new Edge($this, $id, $start, $end, $attrs); | |
} | |
/** | |
* Add existing edge to the graph. | |
* | |
* @param Edge $edge | |
* @return NestedGraph | |
*/ | |
public function addEdge(Edge $edge): self | |
{ | |
$id = $edge->getId(); | |
$start = $edge->getStart(); | |
$end = $edge->getEnd(); | |
if (isset($this->rootGraph->edges[$id])) { | |
throw new DuplicateEdgeException(sprintf("Edge \"%s\" already present in the graph.", $id)); | |
} | |
// Connect the edge to the nodes | |
$start->connectEdge($edge); | |
if ($start !== $end) { | |
$end->connectEdge($edge); | |
} | |
// Add element | |
$this->edges[$id] = $edge; | |
// Update attr indexes | |
$this->edgeAttrIndex->insertElement($edge); | |
return $this; | |
} | |
/** | |
* Get Edge by its ID. | |
* | |
* @param string $id | |
* @return Edge | |
*/ | |
public function getEdge(string $id): Edge | |
{ | |
if (isset($this->edges[$id])) { | |
return $this->edges[$id]; | |
} else { | |
throw new MissingEdgeException(sprintf("Edge \"%s\" not found in the graph.", $id)); | |
} | |
} | |
/** | |
* Returns true if a given edge exists within the graph. | |
*/ | |
public function hasEdge(string $id): bool | |
{ | |
return isset($this->edges[$id]); | |
} | |
/** | |
* Remove edge from the graph and disconnect it from nodes. | |
* | |
* @param Edge $edge | |
* @return NestedGraph | |
*/ | |
public function removeEdge(Edge $edge): self | |
{ | |
$id = $edge->getId(); | |
if (isset($this->edges[$id])) { | |
if ($this->edges[$id] !== $edge) { | |
throw new MissingEdgeException(sprintf("Edge \"%s\" found in the graph is not the expected edge.", $id)); // @codeCoverageIgnore | |
} | |
$this->edges[$id]->disconnectNodes(); | |
unset($this->edges[$id]); | |
$this->edgeAttrIndex->removeElement($edge); | |
return $this; | |
} else { | |
throw new MissingEdgeException(sprintf("Edge \"%s\" not found in the graph.", $id)); | |
} | |
} | |
public function getParentNode(): ?Node | |
{ | |
return $this->parentNode; | |
} | |
/** | |
* @internal | |
*/ | |
public function nodeAttrChanged($node, $key, $oldValue, $newValue) | |
{ | |
if ($this->nodeAttrIndex->hasAttrIndex($key)) { | |
$this->nodeAttrIndex->update($key, $oldValue, $newValue, $node); | |
} | |
} | |
/** | |
* @internal | |
*/ | |
public function edgeAttrChanged($node, $key, $oldValue, $newValue) | |
{ | |
if ($this->edgeAttrIndex->hasAttrIndex($key)) { | |
$this->edgeAttrIndex->update($key, $oldValue, $newValue, $node); | |
} | |
} | |
/** | |
* Handle change of an attribute. | |
* | |
* @codeCoverageIgnore | |
*/ | |
protected function onAttrChanged(string $key, $oldValue, $newValue) | |
{ | |
// No-op. | |
} | |
} | |