Completed
Pull Request — master (#10)
by Steve
02:41
created

Node::setWhiteAfter()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * (c) Steve Nebes <[email protected]>
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
declare(strict_types=1);
10
11
namespace DaisyDiff\Html\Dom;
12
13
use DaisyDiff\Html\Dom\Helper\LastCommonParentResult;
14
15
/**
16
 * Represents any element in the DOM tree of a HTML file.
17
 */
18
abstract class Node
19
{
20
    /** @var TagNode|null */
21
    protected $parent;
22
23
    /** @var TagNode */
24
    private $root;
25
26
    /** @var bool */
27
    private $whiteBefore = false;
28
29
    /** @var bool */
30
    private $whiteAfter = false;
31
32
    /**
33
     * This constructor not only sets the parameter as the parent for the created node, but also appends the created
34
     * node to the collection of parent's children.
35
     *
36
     * @param TagNode|null $parent
37
     */
38 130
    public function __construct(?TagNode $parent)
39
    {
40 130
        $this->parent = $parent;
41
42 130
        if (null !== $parent) {
43 88
            $parent->addChild($this);
44 88
            $this->root = $parent->getRoot();
45 130
        } elseif ($this instanceof TagNode) {
46 130
            $this->root = $this;
47
        }
48 130
    }
49
50
    /**
51
     * Get the parent node instance.
52
     *
53
     * @return TagNode|null
54
     */
55 89
    public function getParent(): ?TagNode
56
    {
57 89
        return $this->parent;
58
    }
59
60
    /**
61
     * Returns a list of the ancestors that is ordered starting from the root by the depth. Index of an element in that
62
     * list corresponds its depth (if depth of the root is 0).
63
     *
64
     * @return TagNode[]
65
     */
66 18
    public function getParentTree(): array
67
    {
68
        /** @var TagNode[] $ancestors */
69 18
        $ancestors = [];
70
71 18
        for ($ancestor = $this->getParent(); null !== $ancestor; $ancestor = $ancestor->getParent()) {
72 18
            $ancestors[] = $ancestor;
73
        }
74
75 18
        return \array_reverse($ancestors);
76
    }
77
78
    /**
79
     * "equals" method should work differently for the case where the compared nodes are from the same tree, and in that
80
     * case return true only if it's the same object.
81
     *
82
     * This method returns the root of the tree (which should be common ancestor for every node in the tree). If the
83
     * roots are the same object, then the nodes are in the same tree.
84
     *
85
     * Returns the "top" ancestor if this node has a parent, or the node itself if there is no parent, and this is a
86
     * TagNode or null if there is no parents and this node isn't a TagNode.
87
     *
88
     * @return TagNode
89
     */
90 91
    public function getRoot(): TagNode
91
    {
92 91
        return $this->root;
93
    }
94
95
    /**
96
     * @param int $id
97
     * @return Node[]
98
     */
99
    abstract public function getMinimalDeletedSet(int $id): array;
100
101
    /**
102
     * Descent the ancestors list for both nodes stopping either at the first no-match case or when either of the lists
103
     * is exhausted.
104
     *
105
     * @param Node $other
106
     * @return LastCommonParentResult
107
     */
108 6
    public function getLastCommonParent(Node $other): LastCommonParentResult
109
    {
110 6
        $result = new LastCommonParentResult();
111
112
        // Note: these lists are never null, but sometimes are empty.
113 6
        $myParents = $this->getParentTree();
114 6
        $otherParents = $other->getParentTree();
115
116 6
        $myParentsCount = \count($myParents);
117 6
        $otherParentsCount = \count($otherParents);
118
119 6
        $i = 1;
120 6
        $isSame = true;
121
122 6
        while ($isSame && $i < $myParentsCount && $i < $otherParentsCount) {
123 4
            if (!$myParents[$i]->isSameTag($otherParents[$i])) {
124 1
                $isSame = false;
125
            } else {
126
                // After the while, the index $i-1 must be the last common parent.
127 4
                $i++;
128
            }
129
        }
130
131 6
        $result->setLastCommonParentDepth($i - 1);
132 6
        $result->setLastCommonParent($myParents[$i - 1]);
133
134 6
        if (!$isSame) {
135
            // Found different parent.
136 1
            $result->setIndexInLastCommonParent($myParents[$i - 1]->getIndexOf($myParents[$i]));
137 1
            $result->setSplittingNeeded();
138 6
        } elseif ($myParentsCount < $otherParentsCount) {
139
            // Current node is not so deeply nested.
140 1
            $result->setIndexInLastCommonParent($myParents[$i - 1]->getIndexOf($this));
141 6
        } elseif ($myParentsCount > $otherParentsCount) {
142
            // All tags matched but there are tags left in this tree - other node is not so deeply nested.
143 1
            $result->setIndexInLastCommonParent($myParents[$i - 1]->getIndexOf($myParents[$i]));
144 1
            $result->setSplittingNeeded();
145
        } else {
146
            // All tags matched until the very last one in both trees or there were not tags besides the BODY.
147 6
            $result->setIndexInLastCommonParent($myParents[$i - 1]->getIndexOf($this));
148
        }
149
150 6
        return $result;
151
    }
152
153
    /**
154
     * Changes the $parent field of this node. Does NOT append/remove itself from the previous or the new parent
155
     * children collection.
156
     *
157
     * @param TagNode|null $parent
158
     */
159 17
    public function setParent(?TagNode $parent): void
160
    {
161 17
        $this->parent = $parent;
162
163 17
        if (null !== $parent) {
164 17
            $this->setRoot($parent->getRoot());
165
        }
166 17
    }
167
168
    /**
169
     * @param TagNode $root
170
     */
171 18
    protected function setRoot(TagNode $root): void
172
    {
173 18
        $this->root = $root;
174 18
    }
175
176
    /**
177
     * Return a deep copy of this node tree.
178
     *
179
     * @return Node
180
     */
181
    abstract public function copyTree(): Node;
182
183
    /**
184
     * Return true only if one of the ancestors is a <pre> tag. False otherwise, including the case where this node is
185
     * a <pre> tag.
186
     *
187
     * @return bool
188
     */
189 1
    public function inPre(): bool
190
    {
191 1
        foreach ($this->getParentTree() as $ancestor) {
192 1
            if ($ancestor instanceof TagNode && $ancestor->isPre()) {
193 1
                return true;
194
            }
195
        }
196
197 1
        return false;
198
    }
199
200
    /**
201
     * @return bool
202
     */
203 21
    public function isWhiteBefore(): bool
204
    {
205 21
        return $this->whiteBefore;
206
    }
207
208
    /**
209
     * @param bool $value
210
     */
211 25
    public function setWhiteBefore(bool $value): void
212
    {
213 25
        $this->whiteBefore = $value;
214 25
    }
215
216
    /**
217
     * @return bool
218
     */
219 24
    public function isWhiteAfter(): bool
220
    {
221 24
        return $this->whiteAfter;
222
    }
223
224
    /**
225
     * @param bool $value
226
     */
227 18
    public function setWhiteAfter(bool $value): void
228
    {
229 18
        $this->whiteAfter = $value;
230 18
    }
231
232
    /**
233
     * @return Node
234
     */
235
    abstract public function getLeftMostChild(): Node;
236
237
    /**
238
     * @return Node
239
     */
240
    abstract public function getRightMostChild(): Node;
241
242
    /**
243
     * @return string
244
     */
245
    abstract public function __toString(): string;
246
}
247