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

HtmlDiffer::score()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
eloc 10
nc 4
nop 1
dl 0
loc 19
ccs 11
cts 11
cp 1
crap 7
rs 8.8333
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;
12
13
use DaisyDiff\Html\Modification\ModificationType;
14
use DaisyDiff\Output\DiffOutputInterface;
15
use DaisyDiff\RangeDifferencer\Core\LCSSettings;
16
use DaisyDiff\RangeDifferencer\RangeDifference;
17
use DaisyDiff\RangeDifferencer\RangeDifferencer;
18
19
/**
20
 * Takes TextNodeComparator instances, computes the difference between them, marks the changes, and outputs a merged
21
 * tree to a [] instance.
22
 */
23
class HtmlDiffer
24
{
25
    /** @var DiffOutputInterface */
26
    private $output;
27
28
    /**
29
     * @param DiffOutputInterface $output
30
     */
31 9
    public function __construct(DiffOutputInterface $output)
32
    {
33 9
        $this->output = $output;
34 9
    }
35
36
    /**
37
     * @param TextNodeComparator $ancestorComparator
38
     * @param TextNodeComparator $leftComparator
39
     * @param TextNodeComparator $rightComparator
40
     *
41
     * @throws
42
     */
43
//    public function diff3(
44
//        TextNodeComparator $ancestorComparator,
45
//        TextNodeComparator $leftComparator,
46
//        TextNodeComparator $rightComparator
47
//    ): void {
48
//        /** @var RangeDifference[] $differences */
49
//        $differences = RangeDifferencer::findDifferences3($ancestorComparator, $leftComparator, $rightComparator);
50
//        $pDifferences = $this->preProcess($differences);
51
//
52
//        $currentIndexAncestor = 0;
53
//        $currentIndexLeft = 0;
54
//        $currentIndexRight = 0;
55
//
56
//        /** @var RangeDifference $d */
57
//        foreach ($pDifferences as $d) {
58
//            $tempKind = $d->getKind();
59
//
60
//            if (RangeDifference::ANCESTOR === $tempKind) {
61
//                // Ignore, we won't show pseudo-conflicts currently (left and right have the same change).
62
//                continue;
63
//            }
64
//
65
//            if ($d->getLeftStart() > $currentIndexLeft) {
66
//                $ancestorComparator->handlePossibleChangedPart(
67
//                    $currentIndexLeft, $d->getLeftStart(),
68
//                    $currentIndexAncestor, $d->getAncestorStart(),
69
//                    $leftComparator);
70
//            }
71
//
72
//            if ($d->getRightStart() > $currentIndexRight) {
73
//                $ancestorComparator->handlePossibleChangedPart(
74
//                    $currentIndexRight, $d->getRightStart(),
75
//                    $currentIndexAncestor, $d->getAncestorStart(),
76
//                    $rightComparator);
77
//            }
78
//
79
//            if (RangeDifference::CONFLICT === $tempKind || RangeDifference::LEFT === $tempKind) {
80
//                // Conflicts and changes on the left side.
81
//                if ($d->getLeftLength() > 0) {
82
//                    $ancestorComparator->markAsDeleted(
83
//                        $d->getLeftStart(), $d->getLeftEnd(), $leftComparator,
84
//                        $d->getAncestorStart(), $d->getAncestorEnd(), ModificationType::ADDED);
85
//                }
86
//            }
87
//
88
//            if (RangeDifference::CONFLICT === $tempKind || RangeDifference::RIGHT === $tempKind) {
89
//                // Conflicts and changes on the right side.
90
//                if ($d->getRightLength() > 0) {
91
//                    $ancestorComparator->markAsDeleted(
92
//                        $d->getRightStart(), $d->getRightEnd(), $rightComparator,
93
//                        $d->getAncestorStart(), $d->getAncestorEnd(), ModificationType::ADDED);
94
//                }
95
//            }
96
//
97
//            $ancestorComparator->markAsNew($d->getAncestorStart(), $d->getAncestorEnd(), ModificationType::REMOVED);
98
//
99
//            $currentIndexAncestor = $d->getAncestorEnd();
100
//            $currentIndexLeft = $d->getLeftEnd();
101
//            $currentIndexRight = $d->getRightEnd();
102
//        }
103
//
104
//        if ($currentIndexLeft < $leftComparator->getRangeCount()) {
105
//            $ancestorComparator->handlePossibleChangedPart(
106
//                $currentIndexLeft, $leftComparator->getRangeCount(),
107
//                $currentIndexAncestor, $ancestorComparator->getRangeCount(),
108
//                $leftComparator);
109
//        }
110
//
111
//        if ($currentIndexRight < $rightComparator->getRangeCount()) {
112
//            $ancestorComparator->handlePossibleChangedPart(
113
//                $currentIndexRight, $rightComparator->getRangeCount(),
114
//                $currentIndexAncestor, $ancestorComparator->getRangeCount(),
115
//                $rightComparator);
116
//        }
117
//
118
//        $ancestorComparator->expandWhiteSpace();
119
//        $this->output->generateOutput($ancestorComparator->getBodyNode());
120
//    }
121
122
    /**
123
     * Compares two Node Trees.
124
     *
125
     * @param TextNodeComparator $leftComparator  Root of the first tree.
126
     * @param TextNodeComparator $rightComparator Root of the second tree.
127
     *
128
     * @throws
129
     */
130 9
    public function diff(TextNodeComparator $leftComparator, TextNodeComparator $rightComparator): void
131
    {
132 9
        $settings = new LCSSettings();
133 9
        $settings->setUseGreedyMethod(false);
134
135
        /** @var RangeDifference[] $differences */
136 9
        $differences = RangeDifferencer::findDifferences($leftComparator, $rightComparator, $settings);
137 9
        $pDifferences = $this->preProcess($differences);
138
139 9
        $currentIndexLeft = 0;
140 9
        $currentIndexRight = 0;
141
142
        /** @var RangeDifference $d */
143 9
        foreach ($pDifferences as $d) {
144 5
            if ($d->getLeftStart() > $currentIndexLeft) {
145 5
                $rightComparator->handlePossibleChangedPart(
146 5
                    $currentIndexLeft, $d->getLeftStart(),
147 5
                    $currentIndexRight, $d->getRightStart(),
148 5
                    $leftComparator);
149
            }
150
151 5
            if ($d->getLeftLength() > 0) {
152 3
                $rightComparator->markAsDeleted(
153 3
                    $d->getLeftStart(), $d->getLeftEnd(),
154 3
                    $leftComparator,
155 3
                    $d->getRightStart());
156
            }
157
158 5
            $rightComparator->markAsNew($d->getRightStart(), $d->getRightEnd());
159
160 5
            $currentIndexLeft = $d->getLeftEnd();
161 5
            $currentIndexRight = $d->getRightEnd();
162
        }
163
164 9
        if ($currentIndexLeft < $leftComparator->getRangeCount()) {
165 9
            $rightComparator->handlePossibleChangedPart(
166 9
                $currentIndexLeft, $leftComparator->getRangeCount(),
167 9
                $currentIndexRight, $rightComparator->getRangeCount(),
168 9
                $leftComparator);
169
        }
170
171 9
        $rightComparator->expandWhiteSpace();
172 9
        $this->output->generateOutput($rightComparator->getBodyNode());
173 9
    }
174
175
    /**
176
     * @param RangeDifference[] $differences
177
     * @return RangeDifference[]
178
     */
179 9
    private function preProcess(array $differences): array
180
    {
181
        /** @var RangeDifference[] */
182 9
        $newRanges = [];
183
184 9
        for ($i = 0, $iMax = \count($differences); $i < $iMax; $i++) {
185 5
            $ancestorStart = $differences[$i]->getAncestorStart();
186 5
            $ancestorEnd = $differences[$i]->getAncestorEnd();
187 5
            $leftStart = $differences[$i]->getLeftStart();
188 5
            $leftEnd = $differences[$i]->getLeftEnd();
189 5
            $rightStart = $differences[$i]->getRightStart();
190 5
            $rightEnd = $differences[$i]->getRightEnd();
191 5
            $kind = $differences[$i]->getKind();
192
193 5
            $ancestorLength = $ancestorEnd - $ancestorStart;
194 5
            $leftLength = $leftEnd - $leftStart;
195 5
            $rightLength = $rightEnd - $rightStart;
196
197
            while (
198 5
                $i + 1 < $iMax &&
199 5
                $differences[$i + 1]->getKind() === $kind &&
200
                $this->score($leftLength, $differences[$i + 1]->getLeftLength(), $rightLength,
201 5
                    $differences[$i + 1]->getRightLength()) > ($differences[$i + 1]->getLeftStart() - $leftEnd)
202
            ) {
203
                $ancestorEnd = $differences[$i + 1]->getAncestorEnd();
204
                $leftEnd = $differences[$i + 1]->getLeftEnd();
205
                $rightEnd = $differences[$i + 1]->getRightEnd();
206
207
                $ancestorLength = $ancestorEnd - $ancestorStart;
208
                $leftLength = $leftEnd - $leftStart;
209
                $rightLength = $rightEnd - $rightStart;
210
211
                $i++;
212
            }
213
214 5
            $newRanges[] = new RangeDifference(
215 5
                $kind,
216 5
                $rightStart, $rightLength,
217 5
                $leftStart, $leftLength,
218 5
                $ancestorStart, $ancestorLength);
219
        }
220
221 9
        return $newRanges;
222
    }
223
224
    /**
225
     * @param int[] $numbers
226
     * @return float
227
     */
228 1
    public static function score(int ...$numbers): float
229
    {
230 1
        if (($numbers[0] === 0 && $numbers[1] === 0) || ($numbers[2] === 0 && $numbers[3] === 0)) {
231 1
            return (float) 0;
232
        }
233
234 1
        $d = 0;
235
236 1
        foreach ($numbers as $number) {
237 1
            while ($number > 3) {
238 1
                $d += 3;
239 1
                $number -= 3;
240 1
                $number *= 0.5;
241
            }
242
243 1
            $d += $number;
244
        }
245
246 1
        return (float) ($d / (1.5 * \count($numbers)));
247
    }
248
}
249