xref: /dokuwiki/inc/TreeBuilder/Node/AbstractNode.php (revision a8e9ec06570f392919a588ec98f916aab604b5a1)
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