1*78a26510SAndreas Gohr<?php 2*78a26510SAndreas Gohr 3*78a26510SAndreas Gohrnamespace dokuwiki\TreeBuilder\Node; 4*78a26510SAndreas Gohr 5*78a26510SAndreas Gohr/** 6*78a26510SAndreas Gohr * A node represents one entry in the tree. It can have a parent and children. 7*78a26510SAndreas Gohr */ 8*78a26510SAndreas Gohrabstract class AbstractNode 9*78a26510SAndreas Gohr{ 10*78a26510SAndreas Gohr /** @var AbstractNode|null parent node */ 11*78a26510SAndreas Gohr protected ?AbstractNode $parent = null; 12*78a26510SAndreas Gohr /** @var string unique ID for this node, usually the page id or external URL */ 13*78a26510SAndreas Gohr protected string $id = ''; 14*78a26510SAndreas Gohr /** @var string|null title of the node, may be null */ 15*78a26510SAndreas Gohr protected ?string $title = null; 16*78a26510SAndreas Gohr 17*78a26510SAndreas Gohr /** @var AbstractNode[] */ 18*78a26510SAndreas Gohr protected array $parents = []; 19*78a26510SAndreas Gohr /** @var AbstractNode[] */ 20*78a26510SAndreas Gohr protected array $children = []; 21*78a26510SAndreas Gohr /** @var array */ 22*78a26510SAndreas Gohr protected array $properties = []; 23*78a26510SAndreas Gohr 24*78a26510SAndreas Gohr /** 25*78a26510SAndreas Gohr * @param string $id The pageID or the external URL 26*78a26510SAndreas Gohr * @param string|null $title The title as given in the link 27*78a26510SAndreas Gohr */ 28*78a26510SAndreas Gohr public function __construct(string $id, ?string $title) 29*78a26510SAndreas Gohr { 30*78a26510SAndreas Gohr $this->id = $id; 31*78a26510SAndreas Gohr $this->title = $title; 32*78a26510SAndreas Gohr } 33*78a26510SAndreas Gohr 34*78a26510SAndreas Gohr /** 35*78a26510SAndreas Gohr * @return string 36*78a26510SAndreas Gohr */ 37*78a26510SAndreas Gohr public function getId(): string 38*78a26510SAndreas Gohr { 39*78a26510SAndreas Gohr return $this->id; 40*78a26510SAndreas Gohr } 41*78a26510SAndreas Gohr 42*78a26510SAndreas Gohr /** 43*78a26510SAndreas Gohr * Get the namespace of this node 44*78a26510SAndreas Gohr * 45*78a26510SAndreas Gohr * @return string 46*78a26510SAndreas Gohr */ 47*78a26510SAndreas Gohr public function getNs(): string 48*78a26510SAndreas Gohr { 49*78a26510SAndreas Gohr return getNS($this->id); 50*78a26510SAndreas Gohr } 51*78a26510SAndreas Gohr 52*78a26510SAndreas Gohr /** 53*78a26510SAndreas Gohr * @return string|null 54*78a26510SAndreas Gohr */ 55*78a26510SAndreas Gohr public function getTitle(): ?string 56*78a26510SAndreas Gohr { 57*78a26510SAndreas Gohr return $this->title; 58*78a26510SAndreas Gohr } 59*78a26510SAndreas Gohr 60*78a26510SAndreas Gohr /** 61*78a26510SAndreas Gohr * @param string|null $title 62*78a26510SAndreas Gohr */ 63*78a26510SAndreas Gohr public function setTitle(?string $title): void 64*78a26510SAndreas Gohr { 65*78a26510SAndreas Gohr $this->title = $title; 66*78a26510SAndreas Gohr } 67*78a26510SAndreas Gohr 68*78a26510SAndreas Gohr /** 69*78a26510SAndreas Gohr * Get all nodes on the same level 70*78a26510SAndreas Gohr * @return AbstractNode[] 71*78a26510SAndreas Gohr */ 72*78a26510SAndreas Gohr public function getSiblings(): array 73*78a26510SAndreas Gohr { 74*78a26510SAndreas Gohr return $this->getParent()->getChildren(); 75*78a26510SAndreas Gohr } 76*78a26510SAndreas Gohr 77*78a26510SAndreas Gohr /** 78*78a26510SAndreas Gohr * Get all sub nodes, may return an empty array 79*78a26510SAndreas Gohr * 80*78a26510SAndreas Gohr * @return AbstractNode[] 81*78a26510SAndreas Gohr */ 82*78a26510SAndreas Gohr public function getChildren(): array 83*78a26510SAndreas Gohr { 84*78a26510SAndreas Gohr return $this->children; 85*78a26510SAndreas Gohr } 86*78a26510SAndreas Gohr 87*78a26510SAndreas Gohr /** 88*78a26510SAndreas Gohr * Does this node have children? 89*78a26510SAndreas Gohr * 90*78a26510SAndreas Gohr * @return bool 91*78a26510SAndreas Gohr */ 92*78a26510SAndreas Gohr public function hasChildren(): bool 93*78a26510SAndreas Gohr { 94*78a26510SAndreas Gohr return !empty($this->children); 95*78a26510SAndreas Gohr } 96*78a26510SAndreas Gohr 97*78a26510SAndreas Gohr /** 98*78a26510SAndreas Gohr * Get all sub nodes and their sub nodes and so on 99*78a26510SAndreas Gohr * 100*78a26510SAndreas Gohr * @return AbstractNode[] 101*78a26510SAndreas Gohr */ 102*78a26510SAndreas Gohr public function getDescendants(): array 103*78a26510SAndreas Gohr { 104*78a26510SAndreas Gohr $descendants = []; 105*78a26510SAndreas Gohr foreach ($this->children as $child) { 106*78a26510SAndreas Gohr $descendants[] = $child; 107*78a26510SAndreas Gohr $descendants = array_merge($descendants, $child->getDescendants()); 108*78a26510SAndreas Gohr } 109*78a26510SAndreas Gohr return $descendants; 110*78a26510SAndreas Gohr } 111*78a26510SAndreas Gohr 112*78a26510SAndreas Gohr /** 113*78a26510SAndreas Gohr * Get all parent nodes in reverse order 114*78a26510SAndreas Gohr * 115*78a26510SAndreas Gohr * This list is cached, so it will only be calculated once. 116*78a26510SAndreas Gohr * 117*78a26510SAndreas Gohr * @return AbstractNode[] 118*78a26510SAndreas Gohr */ 119*78a26510SAndreas Gohr public function getParents(): array 120*78a26510SAndreas Gohr { 121*78a26510SAndreas Gohr if (!$this->parents) { 122*78a26510SAndreas Gohr $parent = $this->parent; 123*78a26510SAndreas Gohr while ($parent) { 124*78a26510SAndreas Gohr $this->parents[] = $parent; 125*78a26510SAndreas Gohr $parent = $parent->getParent(); 126*78a26510SAndreas Gohr } 127*78a26510SAndreas Gohr } 128*78a26510SAndreas Gohr 129*78a26510SAndreas Gohr return $this->parents; 130*78a26510SAndreas Gohr } 131*78a26510SAndreas Gohr 132*78a26510SAndreas Gohr /** 133*78a26510SAndreas Gohr * Set the direct parent node 134*78a26510SAndreas Gohr */ 135*78a26510SAndreas Gohr public function setParent(AbstractNode $parent): void 136*78a26510SAndreas Gohr { 137*78a26510SAndreas Gohr $this->parent = $parent; 138*78a26510SAndreas Gohr } 139*78a26510SAndreas Gohr 140*78a26510SAndreas Gohr /** 141*78a26510SAndreas Gohr * Get the direct parent node 142*78a26510SAndreas Gohr * 143*78a26510SAndreas Gohr * @return AbstractNode|null 144*78a26510SAndreas Gohr */ 145*78a26510SAndreas Gohr public function getParent(): ?AbstractNode 146*78a26510SAndreas Gohr { 147*78a26510SAndreas Gohr return $this->parent; 148*78a26510SAndreas Gohr } 149*78a26510SAndreas Gohr 150*78a26510SAndreas Gohr /** 151*78a26510SAndreas Gohr * @param AbstractNode $child 152*78a26510SAndreas Gohr * @return void 153*78a26510SAndreas Gohr */ 154*78a26510SAndreas Gohr public function addChild(AbstractNode $child): void 155*78a26510SAndreas Gohr { 156*78a26510SAndreas Gohr $child->setParent($this); 157*78a26510SAndreas Gohr $this->children[] = $child; 158*78a26510SAndreas Gohr } 159*78a26510SAndreas Gohr 160*78a26510SAndreas Gohr /** 161*78a26510SAndreas Gohr * Allows to attach an arbitrary property to the page 162*78a26510SAndreas Gohr * 163*78a26510SAndreas Gohr * @param string $name 164*78a26510SAndreas Gohr * @param mixed $value 165*78a26510SAndreas Gohr * @return void 166*78a26510SAndreas Gohr */ 167*78a26510SAndreas Gohr public function setProperty(string $name, $value): void 168*78a26510SAndreas Gohr { 169*78a26510SAndreas Gohr $this->properties[$name] = $value; 170*78a26510SAndreas Gohr } 171*78a26510SAndreas Gohr 172*78a26510SAndreas Gohr /** 173*78a26510SAndreas Gohr * Get the named property, default is returned when the property is not set 174*78a26510SAndreas Gohr * 175*78a26510SAndreas Gohr * @param string $name 176*78a26510SAndreas Gohr * @param mixed $default 177*78a26510SAndreas Gohr * @return mixed 178*78a26510SAndreas Gohr */ 179*78a26510SAndreas Gohr public function getProperty(string $name, $default = null) 180*78a26510SAndreas Gohr { 181*78a26510SAndreas Gohr if (isset($this->properties[$name])) { 182*78a26510SAndreas Gohr return $this->properties[$name]; 183*78a26510SAndreas Gohr } 184*78a26510SAndreas Gohr return $default; 185*78a26510SAndreas Gohr } 186*78a26510SAndreas Gohr 187*78a26510SAndreas Gohr /** 188*78a26510SAndreas Gohr * Sort the children of this node and all its descendants 189*78a26510SAndreas Gohr * 190*78a26510SAndreas Gohr * The given comparator function will be called with two nodes as arguments and needs to 191*78a26510SAndreas Gohr * return an integer less than, equal to, or greater than zero if the first argument is considered 192*78a26510SAndreas Gohr * to be respectively less than, equal to, or greater than the second. 193*78a26510SAndreas Gohr * 194*78a26510SAndreas Gohr * @param callable $comparator 195*78a26510SAndreas Gohr * @return void 196*78a26510SAndreas Gohr */ 197*78a26510SAndreas Gohr public function sort(callable $comparator): void 198*78a26510SAndreas Gohr { 199*78a26510SAndreas Gohr usort($this->children, $comparator); 200*78a26510SAndreas Gohr foreach ($this->children as $child) { 201*78a26510SAndreas Gohr $child->sort($comparator); 202*78a26510SAndreas Gohr } 203*78a26510SAndreas Gohr } 204*78a26510SAndreas Gohr 205*78a26510SAndreas Gohr /** 206*78a26510SAndreas Gohr * Get the string representation of the node 207*78a26510SAndreas Gohr * 208*78a26510SAndreas Gohr * Uses plus char to show the depth of the node in the tree 209*78a26510SAndreas Gohr * 210*78a26510SAndreas Gohr * @return string 211*78a26510SAndreas Gohr */ 212*78a26510SAndreas Gohr public function __toString(): string 213*78a26510SAndreas Gohr { 214*78a26510SAndreas Gohr return str_pad('', count($this->getParents()), '+') . $this->id; 215*78a26510SAndreas Gohr } 216*78a26510SAndreas Gohr} 217