forked from scalameta/munit
/
MyersDiff.scala
132 lines (123 loc) · 3.43 KB
/
MyersDiff.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package munit.diff
import java.util
class MyersDiff[T](equalizer: Equalizer[T])
extends munit.diff.DiffAlgorithm[T] {
def this() = this(Equalizer.default[T])
override def diff(
original: util.List[T],
revised: util.List[T]
): munit.diff.Patch[T] = {
try {
buildRevision(buildPath(original, revised), original, revised)
} catch {
case e: DifferentiationFailedException =>
e.printStackTrace()
new munit.diff.Patch[T]()
}
}
private def buildRevision(
_path: PathNode,
orig: util.List[T],
rev: util.List[T]
): munit.diff.Patch[T] = {
var path = _path
val patch = new munit.diff.Patch[T]
if (path.isSnake) path = path.prev
while (
path != null &&
path.prev != null &&
path.prev.j >= 0
) {
if (path.isSnake)
throw new IllegalStateException(
"bad diffpath: found snake when looking for diff"
)
val i = path.i
val j = path.j
path = path.prev
val ianchor = path.i
val janchor = path.j
val original =
new munit.diff.Chunk[T](
ianchor,
copyOfRange(orig, ianchor, i)
)
val revised =
new munit.diff.Chunk[T](
janchor,
copyOfRange(rev, janchor, j)
)
val delta: munit.diff.Delta[T] =
if (original.size == 0 && revised.size != 0) {
new munit.diff.InsertDelta[T](original, revised)
} else if (original.size > 0 && revised.size == 0) {
new munit.diff.DeleteDelta[T](original, revised)
} else {
new munit.diff.ChangeDelta[T](original, revised)
}
patch.addDelta(delta)
if (path.isSnake) {
path = path.prev
}
}
patch
}
private def copyOfRange(original: util.List[T], fromIndex: Int, to: Int) =
new util.ArrayList[T](original.subList(fromIndex, to))
def buildPath(
orig: util.List[T],
rev: util.List[T]
): PathNode = {
val N = orig.size()
val M = rev.size()
val MAX = N + M + 1
val size = 1 + 2 * MAX
val middle = size / 2
val diagonal = new Array[PathNode](size)
diagonal(middle + 1) = new Snake(0, -1, null)
var d = 0
while (d < MAX) {
var k = -d
while (k <= d) {
val kmiddle = middle + k
val kplus = kmiddle + 1
val kminus = kmiddle - 1
var prev: PathNode = null
var i = 0
if ((k == -d) || (k != d && diagonal(kminus).i < diagonal(kplus).i)) {
i = diagonal(kplus).i
prev = diagonal(kplus)
} else {
i = diagonal(kminus).i + 1
prev = diagonal(kminus)
}
diagonal(kminus) = null // no longer used
var j = i - k
var node: PathNode = new DiffNode(i, j, prev)
// orig and rev are zero-based
// but the algorithm is one-based
// that's why there's no +1 when indexing the sequences
while (
i < N &&
j < M &&
equalizer.equals(orig.get(i), rev.get(j))
) {
i += 1
j += 1
}
if (i > node.i) {
node = new Snake(i, j, node)
}
diagonal(kmiddle) = node
if (i >= N && j >= M) {
return diagonal(kmiddle)
}
k += 2
}
diagonal(middle + d - 1) = null
d += 1
}
// According to Myers, this cannot happen
throw new DifferentiationFailedException("could not find a diff path")
}
}