Commit | Line | Data |
---|---|---|
755d49f0 | 1 | <?php |
a9229942 | 2 | |
755d49f0 | 3 | namespace wcf\data; |
a9229942 | 4 | |
755d49f0 MS |
5 | use wcf\util\ClassUtil; |
6 | ||
7 | /** | |
8 | * Default implementation of `IObjectTreeNode`. | |
a9229942 TD |
9 | * |
10 | * @author Matthias Schmidt | |
11 | * @copyright 2001-2019 WoltLab GmbH | |
12 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> | |
a9229942 | 13 | * @since 5.2 |
755d49f0 | 14 | */ |
a9229942 TD |
15 | trait TObjectTreeNode |
16 | { | |
17 | /** | |
18 | * child nodes | |
19 | * @var static[] | |
20 | */ | |
21 | protected $children = []; | |
22 | ||
23 | /** | |
24 | * current iterator key | |
25 | * @var int | |
26 | */ | |
27 | protected $index = 0; | |
28 | ||
29 | /** | |
30 | * parent node object | |
31 | * @var static | |
32 | */ | |
33 | protected $parentNode; | |
34 | ||
35 | /** | |
36 | * Adds the given node as child node and sets the child node's parent node to this node. | |
37 | * | |
38 | * @param IObjectTreeNode $child added child node | |
39 | * @throws \InvalidArgumentException if given object is no (deocrated) instance of this class | |
40 | */ | |
41 | public function addChild(IObjectTreeNode $child) | |
42 | { | |
43 | if (!($child instanceof $this) && !ClassUtil::isDecoratedInstanceOf($child, static::class)) { | |
44 | throw new \InvalidArgumentException("Child has to be a (decorated) instance of '" . static::class . "', but instance of '" . \get_class($child) . "' given."); | |
45 | } | |
46 | ||
47 | $child->setParentNode($this); | |
48 | ||
49 | $this->children[] = $child; | |
50 | } | |
51 | ||
52 | /** | |
53 | * Returns the number of child nodes. | |
a9229942 | 54 | */ |
1d87d105 | 55 | public function count(): int |
a9229942 TD |
56 | { |
57 | return \count($this->children); | |
58 | } | |
59 | ||
60 | /** | |
61 | * Return the currently iterated child node. | |
a9229942 | 62 | */ |
1d87d105 | 63 | public function current(): static |
a9229942 TD |
64 | { |
65 | return $this->children[$this->index]; | |
66 | } | |
67 | ||
68 | /** | |
69 | * Returns an iterator for the currently iterated child node by returning the node itself. | |
a9229942 | 70 | */ |
1d87d105 | 71 | public function getChildren(): static |
a9229942 TD |
72 | { |
73 | return $this->children[$this->index]; | |
74 | } | |
75 | ||
76 | /** | |
77 | * Returns the depth of the node within the tree. | |
78 | * | |
79 | * The minimum depth is `1`. | |
a9229942 | 80 | */ |
1d87d105 | 81 | public function getDepth(): int |
a9229942 TD |
82 | { |
83 | $element = $this; | |
84 | $depth = 1; | |
85 | ||
86 | while ($element->parentNode->parentNode !== null) { | |
87 | $depth++; | |
88 | $element = $element->parentNode; | |
89 | } | |
90 | ||
91 | return $depth; | |
92 | } | |
93 | ||
94 | /** | |
95 | * Returns the number of open parent nodes. | |
a9229942 | 96 | */ |
1d87d105 | 97 | public function getOpenParentNodes(): int |
a9229942 TD |
98 | { |
99 | $element = $this; | |
100 | $i = 0; | |
101 | ||
102 | while ($element->parentNode->parentNode !== null && $element->isLastSibling()) { | |
103 | $i++; | |
104 | $element = $element->parentNode; | |
105 | } | |
106 | ||
107 | return $i; | |
108 | } | |
109 | ||
110 | /** | |
111 | * Retruns the parent node of this node. | |
a9229942 | 112 | */ |
1d87d105 | 113 | public function getParentNode(): ?self |
a9229942 TD |
114 | { |
115 | return $this->parentNode; | |
116 | } | |
117 | ||
118 | /** | |
119 | * Returns `true` if the node as any children and return `false` otherwise. | |
a9229942 | 120 | */ |
1d87d105 | 121 | public function hasChildren(): bool |
a9229942 TD |
122 | { |
123 | return !empty($this->children); | |
124 | } | |
125 | ||
126 | /** | |
127 | * Return the key of the currently iterated child node. | |
a9229942 | 128 | */ |
1d87d105 | 129 | public function key(): int |
a9229942 TD |
130 | { |
131 | return $this->index; | |
132 | } | |
133 | ||
134 | /** | |
135 | * Returns `true` if this node is the last sibling and `false` otherwise. | |
a9229942 | 136 | */ |
1d87d105 | 137 | public function isLastSibling(): bool |
a9229942 TD |
138 | { |
139 | foreach ($this->parentNode as $key => $child) { | |
140 | if ($child === $this) { | |
141 | return $key === \count($this->parentNode) - 1; | |
142 | } | |
143 | } | |
144 | ||
145 | throw new \LogicException("Unreachable"); | |
146 | } | |
147 | ||
148 | /** | |
149 | * Moves the iteration forward to next child node. | |
150 | */ | |
1d87d105 | 151 | public function next(): void |
a9229942 TD |
152 | { |
153 | $this->index++; | |
154 | } | |
155 | ||
156 | /** | |
157 | * Rewind the iteration to the first child node. | |
158 | */ | |
1d87d105 | 159 | public function rewind(): void |
a9229942 TD |
160 | { |
161 | $this->index = 0; | |
162 | } | |
163 | ||
164 | /** | |
165 | * Sets the parent node of this node. | |
166 | * | |
a9229942 TD |
167 | * @throws \InvalidArgumentException if given object is no (deocrated) instance of this class |
168 | */ | |
169 | public function setParentNode(IObjectTreeNode $parentNode) | |
170 | { | |
171 | if (!($parentNode instanceof $this) && !ClassUtil::isDecoratedInstanceOf($parentNode, static::class)) { | |
172 | throw new \InvalidArgumentException("Parent has to be a (decorated) instance of '" . static::class . "', but instance of '" . \get_class($parentNode) . "' given."); | |
173 | } | |
174 | ||
175 | $this->parentNode = $parentNode; | |
176 | } | |
177 | ||
178 | /** | |
179 | * Returns `true` if current iteration position is valid and `false` otherwise. | |
a9229942 | 180 | */ |
1d87d105 | 181 | public function valid(): bool |
a9229942 TD |
182 | { |
183 | return isset($this->children[$this->index]); | |
184 | } | |
755d49f0 | 185 | } |