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