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