Skip to content

Commit

Permalink
Add NodeInterface::all(), NodeInterface::find() and NodeInterface::de…
Browse files Browse the repository at this point in the history
…lete().
  • Loading branch information
drupol committed Jun 9, 2019
1 parent 1334ba8 commit ece93d8
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 11 deletions.
48 changes: 47 additions & 1 deletion spec/drupol/phptree/Node/NodeSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,52 @@ public function it_can_count_its_children()
->shouldReturn(1);
}

public function it_can_delete()
{
$node1 = new Node();
$node2 = new Node();
$node3 = new Node();
$node4 = new Node();
$node3->add($node4);

$this
->add($node1, $node2, $node3);

$this
->count()
->shouldReturn(4);

$this
->delete($node2)
->shouldReturn($node2->setParent(null));

$this
->degree()
->shouldReturn(2);

$this
->count()
->shouldReturn(3);

$this
->delete($node3)
->shouldReturn($node3->setParent(null));

$this
->degree()
->shouldReturn(1);

$node5 = new Node();

$this
->delete($node5)
->shouldReturn(null);

$this
->shouldThrow(\InvalidArgumentException::class)
->during('delete', [$this]);
}

public function it_can_get_and_set_the_parent(NodeInterface $parent)
{
$this
Expand Down Expand Up @@ -278,7 +324,7 @@ public function it_is_a_traversable()
$this
->add($node1, $node2, $node3);

$this->shouldIterateLike($this->children());
$this->shouldIterateLike($this->all());
}

public function it_is_initializable()
Expand Down
63 changes: 56 additions & 7 deletions src/Node/Node.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ public function add(NodeInterface ...$nodes): NodeInterface
return $this;
}

/**
* {@inheritdoc}
*/
public function all(): \Traversable
{
yield $this;

foreach ($this->children() as $candidate) {
yield from $candidate->all();
}
}

/**
* {@inheritdoc}
*
Expand All @@ -59,11 +71,10 @@ public function children(): \Traversable
*/
public function count(): int
{
$count = 0;
$count = -1;

/** @var \drupol\phptree\Node\NodeInterface $child */
foreach ($this->children() as $child) {
$count += 1 + $child->count();
foreach ($this->all() as $node) {
++$count;
}

return $count;
Expand All @@ -77,6 +88,28 @@ public function degree(): int
return $this->storage()->getChildren()->count();
}

/**
* {@inheritdoc}
*/
public function delete(NodeInterface $node, NodeInterface $root = null): ?NodeInterface
{
$root = $root ?? $this;

if ($candidate = $this->find($node)) {
if ($candidate === $root) {
throw new \InvalidArgumentException('Unable to delete root node.');
}

if (null !== $parent = $candidate->getParent()) {
$parent->remove($node);
}

return $candidate->setParent(null);
}

return null;
}

/**
* {@inheritdoc}
*/
Expand All @@ -85,6 +118,20 @@ public function depth(): int
return \iterator_count($this->getAncestors());
}

/**
* {@inheritdoc}
*/
public function find(NodeInterface $node): ?NodeInterface
{
foreach ($this->all() as $candidate) {
if ($candidate === $node) {
return $node;
}
}

return null;
}

/**
* {@inheritdoc}
*/
Expand All @@ -102,7 +149,7 @@ public function getAncestors(): \Traversable
*/
public function getIterator()
{
yield from $this->children();
yield from $this->all();
}

/**
Expand Down Expand Up @@ -185,7 +232,9 @@ public function offsetGet($offset)
public function offsetSet($offset, $value)
{
if (!($value instanceof NodeInterface)) {
throw new \InvalidArgumentException('The value must implements NodeInterface.');
throw new \InvalidArgumentException(
'The value must implements NodeInterface.'
);
}

$this->storage()->getChildren()
Expand Down Expand Up @@ -220,7 +269,7 @@ static function ($child) use ($nodes) {
/**
* {@inheritdoc}
*/
public function setParent(NodeInterface $node = null): NodeInterface
public function setParent(?NodeInterface $node): NodeInterface
{
$this->storage()->setParent($node);

Expand Down
36 changes: 33 additions & 3 deletions src/Node/NodeInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,18 @@ interface NodeInterface extends \Countable, \ArrayAccess, \Traversable, \Iterato
*/
public function add(NodeInterface ...$node): NodeInterface;

/**
* Get all the nodes of a tree including the parent node itself.
*
* @return \drupol\phptree\Node\NodeInterface|\Traversable
* The node.
*/
public function all(): \Traversable;

/**
* Get the children.
*
* @return \Traversable
* @return \drupol\phptree\Node\NodeInterface|\Traversable
* The children
*/
public function children(): \Traversable;
Expand All @@ -36,6 +44,17 @@ public function children(): \Traversable;
*/
public function degree(): int;

/**
* Remove a node (and its subnodes) from a tree.
*
* @param \drupol\phptree\Node\NodeInterface $node
* The node to remove.
*
* @return null|\drupol\phptree\Node\NodeInterface
* The node that has been removed without parent, null otherwise.
*/
public function delete(NodeInterface $node): ?NodeInterface;

/**
* Get the node depth from the root node.
*
Expand All @@ -44,6 +63,17 @@ public function degree(): int;
*/
public function depth(): int;

/**
* Check if a node is find is in the tree.
*
* @param \drupol\phptree\Node\NodeInterface $node
* The node to find.
*
* @return null|\drupol\phptree\Node\NodeInterface
* The node if found, false otherwise.
*/
public function find(NodeInterface $node): ?NodeInterface;

/**
* Get the ancestors of a node.
*
Expand Down Expand Up @@ -106,13 +136,13 @@ public function remove(NodeInterface ...$node): NodeInterface;
/**
* Set the parent.
*
* @param NodeInterface $node
* @param null|NodeInterface $node
* The parent node
*
* @return NodeInterface
* The node
*/
public function setParent(NodeInterface $node = null): NodeInterface;
public function setParent(?NodeInterface $node): NodeInterface;

/**
* Get a clone of the object with or without children.
Expand Down

0 comments on commit ece93d8

Please sign in to comment.