593160f66e922f2476639d21dfe6a8592f9e41e7
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / data / TObjectTreeNode.class.php
1 <?php
2
3 namespace wcf\data;
4
5 use wcf\util\ClassUtil;
6
7 /**
8 * Default implementation of `IObjectTreeNode`.
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>
13 * @since 5.2
14 */
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.
54 */
55 public function count(): int
56 {
57 return \count($this->children);
58 }
59
60 /**
61 * Return the currently iterated child node.
62 */
63 public function current(): static
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.
70 */
71 public function getChildren(): static
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`.
80 */
81 public function getDepth(): int
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.
96 */
97 public function getOpenParentNodes(): int
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.
112 */
113 public function getParentNode(): ?self
114 {
115 return $this->parentNode;
116 }
117
118 /**
119 * Returns `true` if the node as any children and return `false` otherwise.
120 */
121 public function hasChildren(): bool
122 {
123 return !empty($this->children);
124 }
125
126 /**
127 * Return the key of the currently iterated child node.
128 */
129 public function key(): int
130 {
131 return $this->index;
132 }
133
134 /**
135 * Returns `true` if this node is the last sibling and `false` otherwise.
136 */
137 public function isLastSibling(): bool
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 */
151 public function next(): void
152 {
153 $this->index++;
154 }
155
156 /**
157 * Rewind the iteration to the first child node.
158 */
159 public function rewind(): void
160 {
161 $this->index = 0;
162 }
163
164 /**
165 * Sets the parent node of this node.
166 *
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.
180 */
181 public function valid(): bool
182 {
183 return isset($this->children[$this->index]);
184 }
185 }