178a26510SAndreas Gohr<?php 278a26510SAndreas Gohr 378a26510SAndreas Gohrnamespace dokuwiki\TreeBuilder\Node; 478a26510SAndreas Gohr 578a26510SAndreas Gohr/** 678a26510SAndreas Gohr * A node represents one entry in the tree. It can have a parent and children. 778a26510SAndreas Gohr */ 878a26510SAndreas Gohrabstract class AbstractNode 978a26510SAndreas Gohr{ 1078a26510SAndreas Gohr /** @var AbstractNode|null parent node */ 1178a26510SAndreas Gohr protected ?AbstractNode $parent = null; 1278a26510SAndreas Gohr /** @var string unique ID for this node, usually the page id or external URL */ 1378a26510SAndreas Gohr protected string $id = ''; 1478a26510SAndreas Gohr /** @var string|null title of the node, may be null */ 1578a26510SAndreas Gohr protected ?string $title = null; 1678a26510SAndreas Gohr 1778a26510SAndreas Gohr /** @var AbstractNode[] */ 1878a26510SAndreas Gohr protected array $parents = []; 1978a26510SAndreas Gohr /** @var AbstractNode[] */ 2078a26510SAndreas Gohr protected array $children = []; 2178a26510SAndreas Gohr /** @var array */ 2278a26510SAndreas Gohr protected array $properties = []; 2378a26510SAndreas Gohr 2478a26510SAndreas Gohr /** 2578a26510SAndreas Gohr * @param string $id The pageID or the external URL 2678a26510SAndreas Gohr * @param string|null $title The title as given in the link 2778a26510SAndreas Gohr */ 2878a26510SAndreas Gohr public function __construct(string $id, ?string $title) 2978a26510SAndreas Gohr { 3078a26510SAndreas Gohr $this->id = $id; 3178a26510SAndreas Gohr $this->title = $title; 3278a26510SAndreas Gohr } 3378a26510SAndreas Gohr 3478a26510SAndreas Gohr /** 3578a26510SAndreas Gohr * @return string 3678a26510SAndreas Gohr */ 3778a26510SAndreas Gohr public function getId(): string 3878a26510SAndreas Gohr { 3978a26510SAndreas Gohr return $this->id; 4078a26510SAndreas Gohr } 4178a26510SAndreas Gohr 4278a26510SAndreas Gohr /** 4378a26510SAndreas Gohr * Get the namespace of this node 4478a26510SAndreas Gohr * 4578a26510SAndreas Gohr * @return string 4678a26510SAndreas Gohr */ 4778a26510SAndreas Gohr public function getNs(): string 4878a26510SAndreas Gohr { 4978a26510SAndreas Gohr return getNS($this->id); 5078a26510SAndreas Gohr } 5178a26510SAndreas Gohr 5278a26510SAndreas Gohr /** 5378a26510SAndreas Gohr * @return string|null 5478a26510SAndreas Gohr */ 5578a26510SAndreas Gohr public function getTitle(): ?string 5678a26510SAndreas Gohr { 5778a26510SAndreas Gohr return $this->title; 5878a26510SAndreas Gohr } 5978a26510SAndreas Gohr 6078a26510SAndreas Gohr /** 6178a26510SAndreas Gohr * @param string|null $title 6278a26510SAndreas Gohr */ 6378a26510SAndreas Gohr public function setTitle(?string $title): void 6478a26510SAndreas Gohr { 6578a26510SAndreas Gohr $this->title = $title; 6678a26510SAndreas Gohr } 6778a26510SAndreas Gohr 6878a26510SAndreas Gohr /** 6978a26510SAndreas Gohr * Get all nodes on the same level 7078a26510SAndreas Gohr * @return AbstractNode[] 7178a26510SAndreas Gohr */ 7278a26510SAndreas Gohr public function getSiblings(): array 7378a26510SAndreas Gohr { 7478a26510SAndreas Gohr return $this->getParent()->getChildren(); 7578a26510SAndreas Gohr } 7678a26510SAndreas Gohr 7778a26510SAndreas Gohr /** 7878a26510SAndreas Gohr * Get all sub nodes, may return an empty array 7978a26510SAndreas Gohr * 8078a26510SAndreas Gohr * @return AbstractNode[] 8178a26510SAndreas Gohr */ 8278a26510SAndreas Gohr public function getChildren(): array 8378a26510SAndreas Gohr { 8478a26510SAndreas Gohr return $this->children; 8578a26510SAndreas Gohr } 8678a26510SAndreas Gohr 8778a26510SAndreas Gohr /** 8878a26510SAndreas Gohr * Does this node have children? 8978a26510SAndreas Gohr * 9078a26510SAndreas Gohr * @return bool 9178a26510SAndreas Gohr */ 9278a26510SAndreas Gohr public function hasChildren(): bool 9378a26510SAndreas Gohr { 94*a8e9ec06SAndreas Gohr return $this->children !== []; 9578a26510SAndreas Gohr } 9678a26510SAndreas Gohr 9778a26510SAndreas Gohr /** 9878a26510SAndreas Gohr * Get all sub nodes and their sub nodes and so on 9978a26510SAndreas Gohr * 10078a26510SAndreas Gohr * @return AbstractNode[] 10178a26510SAndreas Gohr */ 10278a26510SAndreas Gohr public function getDescendants(): array 10378a26510SAndreas Gohr { 10478a26510SAndreas Gohr $descendants = []; 10578a26510SAndreas Gohr foreach ($this->children as $child) { 10678a26510SAndreas Gohr $descendants[] = $child; 10778a26510SAndreas Gohr $descendants = array_merge($descendants, $child->getDescendants()); 10878a26510SAndreas Gohr } 10978a26510SAndreas Gohr return $descendants; 11078a26510SAndreas Gohr } 11178a26510SAndreas Gohr 11278a26510SAndreas Gohr /** 11378a26510SAndreas Gohr * Get all parent nodes in reverse order 11478a26510SAndreas Gohr * 11578a26510SAndreas Gohr * This list is cached, so it will only be calculated once. 11678a26510SAndreas Gohr * 11778a26510SAndreas Gohr * @return AbstractNode[] 11878a26510SAndreas Gohr */ 11978a26510SAndreas Gohr public function getParents(): array 12078a26510SAndreas Gohr { 12178a26510SAndreas Gohr if (!$this->parents) { 12278a26510SAndreas Gohr $parent = $this->parent; 12378a26510SAndreas Gohr while ($parent) { 12478a26510SAndreas Gohr $this->parents[] = $parent; 12578a26510SAndreas Gohr $parent = $parent->getParent(); 12678a26510SAndreas Gohr } 12778a26510SAndreas Gohr } 12878a26510SAndreas Gohr 12978a26510SAndreas Gohr return $this->parents; 13078a26510SAndreas Gohr } 13178a26510SAndreas Gohr 13278a26510SAndreas Gohr /** 13378a26510SAndreas Gohr * Set the direct parent node 13478a26510SAndreas Gohr */ 13578a26510SAndreas Gohr public function setParent(AbstractNode $parent): void 13678a26510SAndreas Gohr { 13778a26510SAndreas Gohr $this->parent = $parent; 13878a26510SAndreas Gohr } 13978a26510SAndreas Gohr 14078a26510SAndreas Gohr /** 14178a26510SAndreas Gohr * Get the direct parent node 14278a26510SAndreas Gohr * 14378a26510SAndreas Gohr * @return AbstractNode|null 14478a26510SAndreas Gohr */ 14578a26510SAndreas Gohr public function getParent(): ?AbstractNode 14678a26510SAndreas Gohr { 14778a26510SAndreas Gohr return $this->parent; 14878a26510SAndreas Gohr } 14978a26510SAndreas Gohr 15078a26510SAndreas Gohr /** 15178a26510SAndreas Gohr * @param AbstractNode $child 15278a26510SAndreas Gohr * @return void 15378a26510SAndreas Gohr */ 15478a26510SAndreas Gohr public function addChild(AbstractNode $child): void 15578a26510SAndreas Gohr { 15678a26510SAndreas Gohr $child->setParent($this); 15778a26510SAndreas Gohr $this->children[] = $child; 15878a26510SAndreas Gohr } 15978a26510SAndreas Gohr 16078a26510SAndreas Gohr /** 16178a26510SAndreas Gohr * Allows to attach an arbitrary property to the page 16278a26510SAndreas Gohr * 16378a26510SAndreas Gohr * @param string $name 16478a26510SAndreas Gohr * @param mixed $value 16578a26510SAndreas Gohr * @return void 16678a26510SAndreas Gohr */ 16778a26510SAndreas Gohr public function setProperty(string $name, $value): void 16878a26510SAndreas Gohr { 16978a26510SAndreas Gohr $this->properties[$name] = $value; 17078a26510SAndreas Gohr } 17178a26510SAndreas Gohr 17278a26510SAndreas Gohr /** 17378a26510SAndreas Gohr * Get the named property, default is returned when the property is not set 17478a26510SAndreas Gohr * 17578a26510SAndreas Gohr * @param string $name 17678a26510SAndreas Gohr * @param mixed $default 17778a26510SAndreas Gohr * @return mixed 17878a26510SAndreas Gohr */ 17978a26510SAndreas Gohr public function getProperty(string $name, $default = null) 18078a26510SAndreas Gohr { 181*a8e9ec06SAndreas Gohr return $this->properties[$name] ?? $default; 18278a26510SAndreas Gohr } 18378a26510SAndreas Gohr 18478a26510SAndreas Gohr /** 18578a26510SAndreas Gohr * Sort the children of this node and all its descendants 18678a26510SAndreas Gohr * 18778a26510SAndreas Gohr * The given comparator function will be called with two nodes as arguments and needs to 18878a26510SAndreas Gohr * return an integer less than, equal to, or greater than zero if the first argument is considered 18978a26510SAndreas Gohr * to be respectively less than, equal to, or greater than the second. 19078a26510SAndreas Gohr * 19178a26510SAndreas Gohr * @param callable $comparator 19278a26510SAndreas Gohr * @return void 19378a26510SAndreas Gohr */ 19478a26510SAndreas Gohr public function sort(callable $comparator): void 19578a26510SAndreas Gohr { 19678a26510SAndreas Gohr usort($this->children, $comparator); 19778a26510SAndreas Gohr foreach ($this->children as $child) { 19878a26510SAndreas Gohr $child->sort($comparator); 19978a26510SAndreas Gohr } 20078a26510SAndreas Gohr } 20178a26510SAndreas Gohr 20278a26510SAndreas Gohr /** 20378a26510SAndreas Gohr * Get the string representation of the node 20478a26510SAndreas Gohr * 20578a26510SAndreas Gohr * Uses plus char to show the depth of the node in the tree 20678a26510SAndreas Gohr * 20778a26510SAndreas Gohr * @return string 20878a26510SAndreas Gohr */ 20978a26510SAndreas Gohr public function __toString(): string 21078a26510SAndreas Gohr { 21178a26510SAndreas Gohr return str_pad('', count($this->getParents()), '+') . $this->id; 21278a26510SAndreas Gohr } 21378a26510SAndreas Gohr} 214