Merge pull request #5964 from WoltLab/tag-form-field-allow-value-load
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / data / TObjectTreeNode.class.php
CommitLineData
755d49f0 1<?php
a9229942 2
755d49f0 3namespace wcf\data;
a9229942 4
755d49f0
MS
5use 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
15trait 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}