Completed
Pull Request — master (#10)
by Steve
04:31
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 19
     */
31
    public function __construct(DiffOutputInterface $output)
32 19
    {
33 19
        $this->output = $output;
34
    }
35
36
    /**
37
     * @param TextNodeComparator $ancestorComparator
38
     * @param TextNodeComparator $leftComparator
39
     * @param TextNodeComparator $rightComparator
40
     *
41
     * @throws
42 1
     */
43
//    public function diff3(
44
//        TextNodeComparator $ancestorComparator,
45
//        TextNodeComparator $leftComparator,
46
//        TextNodeComparator $rightComparator
47
//    ): void {
48 1
//        /** @var RangeDifference[] $differences */
49 1
//        $differences = RangeDifferencer::findDifferences3($ancestorComparator, $leftComparator, $rightComparator);
50
//        $pDifferences = $this->preProcess($differences);
51 1
//
52 1
//        $currentIndexAncestor = 0;
53 1
//        $currentIndexLeft = 0;
54
//        $currentIndexRight = 0;
55
//
56 1
//        /** @var RangeDifference $d */
57 1
//        foreach ($pDifferences as $d) {
58
//            $tempKind = $d->getKind();
59 1
//
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 1
//
65 1
//            if ($d->getLeftStart() > $currentIndexLeft) {
66 1
//                $ancestorComparator->handlePossibleChangedPart(
67 1
//                    $currentIndexLeft, $d->getLeftStart(),
68 1
//                    $currentIndexAncestor, $d->getAncestorStart(),
69
//                    $leftComparator);
70
//            }
71 1
//
72 1
//            if ($d->getRightStart() > $currentIndexRight) {
73 1
//                $ancestorComparator->handlePossibleChangedPart(
74 1
//                    $currentIndexRight, $d->getRightStart(),
75 1
//                    $currentIndexAncestor, $d->getAncestorStart(),
76
//                    $rightComparator);
77
//            }
78 1
//
79
//            if (RangeDifference::CONFLICT === $tempKind || RangeDifference::LEFT === $tempKind) {
80 1
//                // Conflicts and changes on the left side.
81 1
//                if ($d->getLeftLength() > 0) {
82 1
//                    $ancestorComparator->markAsDeleted(
83 1
//                        $d->getLeftStart(), $d->getLeftEnd(), $leftComparator,
84
//                        $d->getAncestorStart(), $d->getAncestorEnd(), ModificationType::ADDED);
85
//                }
86
//            }
87 1
//
88
//            if (RangeDifference::CONFLICT === $tempKind || RangeDifference::RIGHT === $tempKind) {
89 1
//                // Conflicts and changes on the right side.
90 1
//                if ($d->getRightLength() > 0) {
91 1
//                    $ancestorComparator->markAsDeleted(
92 1
//                        $d->getRightStart(), $d->getRightEnd(), $rightComparator,
93
//                        $d->getAncestorStart(), $d->getAncestorEnd(), ModificationType::ADDED);
94
//                }
95
//            }
96 1
//
97
//            $ancestorComparator->markAsNew($d->getAncestorStart(), $d->getAncestorEnd(), ModificationType::REMOVED);
98 1
//
99 1
//            $currentIndexAncestor = $d->getAncestorEnd();
100 1
//            $currentIndexLeft = $d->getLeftEnd();
101
//            $currentIndexRight = $d->getRightEnd();
102
//        }
103 1
//
104 1
//        if ($currentIndexLeft < $leftComparator->getRangeCount()) {
105 1
//            $ancestorComparator->handlePossibleChangedPart(
106 1
//                $currentIndexLeft, $leftComparator->getRangeCount(),
107 1
//                $currentIndexAncestor, $ancestorComparator->getRangeCount(),
108
//                $leftComparator);
109
//        }
110 1
//
111 1
//        if ($currentIndexRight < $rightComparator->getRangeCount()) {
112 1
//            $ancestorComparator->handlePossibleChangedPart(
113 1
//                $currentIndexRight, $rightComparator->getRangeCount(),
114 1
//                $currentIndexAncestor, $ancestorComparator->getRangeCount(),
115
//                $rightComparator);
116
//        }
117 1
//
118 1
//        $ancestorComparator->expandWhiteSpace();
119 1
//        $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 18
     */
130
    public function diff(TextNodeComparator $leftComparator, TextNodeComparator $rightComparator): void
131
    {
132 18
        $settings = new LCSSettings();
133 18
        $settings->setUseGreedyMethod(false);
134
135 18
        /** @var RangeDifference[] $differences */
136 18
        $differences = RangeDifferencer::findDifferences($leftComparator, $rightComparator, $settings);
137
        $pDifferences = $this->preProcess($differences);
138
139 18
        $currentIndexLeft = 0;
140 13
        $currentIndexRight = 0;
141 9
142 9
        /** @var RangeDifference $d */
143 9
        foreach ($pDifferences as $d) {
144 9
            if ($d->getLeftStart() > $currentIndexLeft) {
145
                $rightComparator->handlePossibleChangedPart(
146
                    $currentIndexLeft, $d->getLeftStart(),
147 13
                    $currentIndexRight, $d->getRightStart(),
148 7
                    $leftComparator);
149 7
            }
150 7
151 7
            if ($d->getLeftLength() > 0) {
152
                $rightComparator->markAsDeleted(
153
                    $d->getLeftStart(), $d->getLeftEnd(),
154 11
                    $leftComparator,
155
                    $d->getRightStart());
156 11
            }
157 11
158
            $rightComparator->markAsNew($d->getRightStart(), $d->getRightEnd());
159
160 16
            $currentIndexLeft = $d->getLeftEnd();
161 12
            $currentIndexRight = $d->getRightEnd();
162 12
        }
163 12
164 12
//        if ($currentIndexLeft < $leftComparator->getRangeCount()) {
165
//            $rightComparator->handlePossibleChangedPart(
166
//                $currentIndexLeft, $leftComparator->getRangeCount(),
167 15
//                $currentIndexRight, $rightComparator->getRangeCount(),
168 15
//                $leftComparator);
169 15
//        }
170
171
        $rightComparator->expandWhiteSpace();
172
        $this->output->generateOutput($rightComparator->getBodyNode());
173
    }
174
175 19
    /**
176
     * @param RangeDifference[] $differences
177
     * @return RangeDifference[]
178 19
     */
179
    private function preProcess(array $differences): array
180 19
    {
181 14
        /** @var RangeDifference[] */
182 14
        $newRanges = [];
183 14
184 14
        for ($i = 0, $iMax = \count($differences); $i < $iMax; $i++) {
185 14
            $ancestorStart = $differences[$i]->getAncestorStart();
186 14
            $ancestorEnd = $differences[$i]->getAncestorEnd();
187 14
            $leftStart = $differences[$i]->getLeftStart();
188
            $leftEnd = $differences[$i]->getLeftEnd();
189 14
            $rightStart = $differences[$i]->getRightStart();
190 14
            $rightEnd = $differences[$i]->getRightEnd();
191 14
            $kind = $differences[$i]->getKind();
192
193 14
            $ancestorLength = $ancestorEnd - $ancestorStart;
194
            $leftLength = $leftEnd - $leftStart;
195 14
            $rightLength = $rightEnd - $rightStart;
196
197
            while (
198
                $i + 1 < $iMax &&
199
                $differences[$i + 1]->getKind() === $kind &&
200
                $this->score($leftLength, $differences[$i + 1]->getLeftLength(), $rightLength,
201
                    $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 14
                $ancestorLength = $ancestorEnd - $ancestorStart;
208 14
                $leftLength = $leftEnd - $leftStart;
209 14
                $rightLength = $rightEnd - $rightStart;
210 14
211 14
                $i++;
212
            }
213
214 19
            $newRanges[] = new RangeDifference(
215
                $kind,
216
                $rightStart, $rightLength,
217
                $leftStart, $leftLength,
218
                $ancestorStart, $ancestorLength);
219
        }
220
221
        return $newRanges;
222
    }
223
224
    /**
225
     * @param int[] $numbers
226
     * @return float
227
     */
228
    public function score(int ...$numbers): float
229
    {
230
        if (\count($numbers) < 3) {
231
            throw new \OutOfBoundsException('Need at least 3 numbers.');
232
        }
233
234
        if (($numbers[0] === 0 && $numbers[1] === 0) || ($numbers[2] === 0 && $numbers[3] === 0)) {
235
            return (float) 0.0;
236
        }
237
238
        $d = 0.0;
239
240
        foreach ($numbers as $number) {
241
            while ($number > 3) {
242
                $d += 3;
243
                $number -= 3;
244
                $number *= 0.5;
245
            }
246
247
            $d += $number;
248
        }
249
250
        return (float) ($d / 1.5 * \count($numbers));
251
    }
252
}
253