Completed
Pull Request — master (#10)
by Steve
04:31
created

Node::detectIgnorableWhiteSpace()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 0
nc 1
nop 0
dl 0
loc 2
ccs 1
cts 1
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 138
    public function __construct(?TagNode $parent)
39
    {
40 138
        $this->parent = $parent;
41
42 138
        if (null !== $parent) {
43 95
            $parent->addChild($this);
44 95
            $this->root = $parent->getRoot();
45 138
        } elseif ($this instanceof TagNode) {
46 138
            $this->root = $this;
47
        }
48 138
    }
49
50
    /**
51
     * Get the parent node instance.
52
     *
53
     * @return TagNode|null
54
     */
55 96
    public function getParent(): ?TagNode
56
    {
57 96
        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 21
    public function getParentTree(): array
67
    {
68 21
        /** @var TagNode[] $ancestors */
69
        $ancestors = [];
70 21
71 21
        for ($ancestor = $this->getParent(); null !== $ancestor; $ancestor = $ancestor->getParent()) {
72
            $ancestors[] = $ancestor;
73
        }
74 21
75
        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 98
     */
90
    public function getRoot(): TagNode
91 98
    {
92
        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 1
     * is exhausted.
104
     *
105
     * @param Node $other
106 1
     * @return LastCommonParentResult
107
     */
108
    public function getLastCommonParent(Node $other): LastCommonParentResult
109
    {
110
        $result = new LastCommonParentResult();
111
112
        // Note: these lists are never null, but sometimes are empty.
113
        $myParents = $this->getParentTree();
114
        $otherParents = $other->getParentTree();
115 10
116
        $myParentsCount = \count($myParents);
117 10
        $otherParentsCount = \count($otherParents);
118 1
119
        $i = 1;
120
        $isSame = true;
121 9
122
        while ($isSame && $i < $myParentsCount && $i < $otherParentsCount) {
123
            if (!$myParents[$i]->isSameTag($otherParents[$i])) {
124 9
                $isSame = false;
125 9
            } else {
126
                // After the while, the index $i-1 must be the last common parent.
127 9
                $i++;
128 9
            }
129
        }
130 9
131 5
        $result->setLastCommonParentDepth($i - 1);
132 1
        $result->setLastCommonParent($myParents[$i - 1]);
133
134
        if (!$isSame) {
135 5
            // Found different parent.
136
            $result->setIndexInLastCommonParent($myParents[$i - 1]->getIndexOf($myParents[$i]));
137
            $result->setSplittingNeeded();
138
        } elseif ($myParentsCount < $otherParentsCount) {
139 9
            // Current node is not so deeply nested.
140 9
            $result->setIndexInLastCommonParent($myParents[$i - 1]->getIndexOf($this));
141
        } elseif ($myParentsCount > $otherParentsCount) {
142 9
            // All tags matched but there are tags left in this tree - other node is not so deeply nested.
143
            $result->setIndexInLastCommonParent($myParents[$i - 1]->getIndexOf($myParents[$i]));
144 1
            $result->setSplittingNeeded();
145 1
        } else {
146 9
            // All tags matched until the very last one in both trees or there were not tags besides the BODY.
147
            $result->setIndexInLastCommonParent($myParents[$i - 1]->getIndexOf($this));
148 1
        }
149 9
150
        return $result;
151 3
    }
152 3
153
    /**
154
     * Changes the $parent field of this node. Does NOT append/remove itself from the previous or the new parent
155 7
     * children collection.
156
     *
157
     * @param TagNode|null $parent
158 9
     */
159
    public function setParent(?TagNode $parent): void
160
    {
161
        $this->parent = $parent;
162
163
        if (null !== $parent) {
164
            $this->setRoot($parent->getRoot());
165
        }
166
    }
167 20
168
    /**
169 20
     * @param TagNode $root
170
     */
171 20
    protected function setRoot(TagNode $root): void
172 19
    {
173
        $this->root = $root;
174 20
    }
175
176
    /**
177
     * Return a deep copy of this node tree.
178
     *
179 20
     * @return Node
180
     */
181 20
    abstract public function copyTree(): Node;
182 20
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
    public function inPre(): bool
190
    {
191
        foreach ($this->getParentTree() as $ancestor) {
192
            if ($ancestor instanceof TagNode && $ancestor->isPre()) {
193
                return true;
194
            }
195
        }
196
197 1
        return false;
198
    }
199 1
200 1
    /**
201 1
     * @return bool
202
     */
203
    public function isWhiteBefore(): bool
204
    {
205 1
        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
    }
215
216
    /**
217
     * @return bool
218
     */
219 31
    public function isWhiteAfter(): bool
220
    {
221 31
        return $this->whiteAfter;
222 31
    }
223
224
    /**
225
     * @param bool $value
226
     */
227 28
    public function setWhiteAfter(bool $value): void
228
    {
229 28
        $this->whiteAfter = $value;
230
    }
231
232
    /**
233
     * @return Node
234
     */
235 20
    abstract public function getLeftMostChild(): Node;
236
237 20
    /**
238 20
     * @return Node
239
     */
240
    abstract public function getRightMostChild(): Node;
241
242
    /**
243
     * @return string
244
     */
245
    abstract public function __toString(): string;
246
}
247