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

HtmlDiffer::diff3()   C

Complexity

Conditions 13
Paths 152

Size

Total Lines 77
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 45
CRAP Score 13.0017

Importance

Changes 0
Metric Value
cc 13
eloc 45
nc 152
nop 3
dl 0
loc 77
ccs 45
cts 46
cp 0.9783
crap 13.0017
rs 6.1833
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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