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