Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
41 / 41
100.00% covered (success)
100.00%
13 / 13
CRAP
100.00% covered (success)
100.00%
1 / 1
Node
100.00% covered (success)
100.00%
41 / 41
100.00% covered (success)
100.00%
13 / 13
25
100.00% covered (success)
100.00%
1 / 1
 withValue
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 withChildren
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 fromNode
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getIterator
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getValue
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 hasValue
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 setValue
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 transform
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 isLeaf
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 appendChildren
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 replaceChildren
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 popChild
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 __debugInfo
n/a
0 / 0
n/a
0 / 0
2
1<?php declare(strict_types=1);
2
3namespace Computator\FrameworkUtils\PHPTemplate\RenderTree;
4
5use ArgumentCountError;
6use EmptyIterator;
7use Iterator;
8use IteratorAggregate;
9
10use LogicException;
11use function count;
12
13class Node implements IteratorAggregate {
14    protected NodeLinkedList $children;
15    protected ?Renderable $value = null;
16
17    public static function withValue(?Renderable $value): static {
18        $n = new static();
19        $n->value = $value;
20        return $n;
21    }
22
23    public static function withChildren(self ...$children): static {
24        if (!count($children))
25            throw new ArgumentCountError("at least one parameter is required");
26        $n = new static();
27        $n->transform();
28        array_walk($children, fn ($c) => $n->children->push($c));
29        return $n;
30    }
31
32    public static function fromNode(self $node): self {
33        if ($node->isLeaf())
34            return self::withValue($node->getValue());
35        else
36            return self::withChildren(...$node);
37    }
38
39    private function __construct() {}
40
41    public function getIterator(): Iterator {
42        return $this->children ?? new EmptyIterator();
43    }
44
45    public function getValue(): ?Renderable {
46        if (!$this->isLeaf())
47            throw new LogicException(__METHOD__." is only valid on leaf nodes");
48        return $this->value;
49    }
50
51    public function hasValue(): bool {
52        if (!$this->isLeaf())
53            throw new LogicException(__METHOD__." is only valid on leaf nodes");
54        return $this->value !== null;
55    }
56
57    public function setValue(Renderable $value): ?Renderable {
58        if (!$this->isLeaf())
59            throw new LogicException(__METHOD__." is only valid on leaf nodes");
60        $oldval = $this->value;
61        $this->value = $value;
62        return $oldval;
63    }
64
65    protected function transform() {
66        assert(!isset($this->children));
67        $this->children = new NodeLinkedListImpl();
68        if ($this->value !== null) {
69            $this->children->push(Node::withValue($this->value));
70            $this->value = null;
71        }
72    }
73
74    public function isLeaf(): bool {
75        return !isset($this->children) || $this->children->isEmpty();
76    }
77
78    public function appendChildren(self ...$nodes): void {
79        if (!isset($this->children))
80            $this->transform();
81        array_walk($nodes, fn ($c) => $this->children->push($c));
82    }
83
84    public function replaceChildren(self ...$nodes): void {
85        if ($this->isLeaf())
86            $this->value = null;
87        else
88            $this->children = new NodeLinkedListImpl();
89        $this->appendChildren(...$nodes);
90    }
91
92    // TODO: somehow notify tree that child was removed
93    public function popChild(): self {
94        if ($this->isLeaf())
95            throw new LogicException(__METHOD__." is only valid on tree nodes");
96        return $this->children->pop();
97    }
98
99    /** @codeCoverageIgnore */
100    public function __debugInfo(): array {
101        if ($this->isLeaf()) {
102            return [
103                'value' => $this->getValue(),
104            ];
105        } else {
106            return [
107                'children' => [...$this],
108            ];
109        }
110    }
111}