Skip to content
This repository was archived by the owner on Jan 24, 2025. It is now read-only.

Commit 4a460d5

Browse files
committed
v1
1 parent f2b83e2 commit 4a460d5

File tree

11 files changed

+598
-0
lines changed

11 files changed

+598
-0
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.softwaremill.diffx.instances.string
2+
3+
import java.util
4+
5+
class Chunk[T](position: Int, lines: util.List[T]) {
6+
7+
def getPosition: Int = position
8+
def getLines: util.List[T] = lines
9+
def size: Int = lines.size()
10+
11+
override def toString: String = s"Chunk($getPosition, $getLines, $size)"
12+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.softwaremill.diffx.instances.string
2+
3+
sealed abstract class Delta[T](original: Chunk[T], revised: Chunk[T]) {
4+
5+
sealed abstract class TYPE
6+
object TYPE {
7+
case object CHANGE extends TYPE
8+
case object DELETE extends TYPE
9+
case object INSERT extends TYPE
10+
}
11+
def getType: TYPE
12+
def getOriginal: Chunk[T] = original
13+
def getRevised: Chunk[T] = revised
14+
15+
override def toString: String = s"Delta($getType, $getOriginal, $getRevised)"
16+
}
17+
class ChangeDelta[T](original: Chunk[T], revised: Chunk[T]) extends Delta(original, revised) {
18+
override def getType: TYPE = TYPE.CHANGE
19+
}
20+
class InsertDelta[T](original: Chunk[T], revised: Chunk[T]) extends Delta(original, revised) {
21+
override def getType: TYPE = TYPE.INSERT
22+
}
23+
class DeleteDelta[T](original: Chunk[T], revised: Chunk[T]) extends Delta(original, revised) {
24+
override def getType: TYPE = TYPE.DELETE
25+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.softwaremill.diffx.instances.string
2+
3+
import java.util
4+
5+
trait DiffAlgorithm[T] {
6+
def diff(original: util.List[T], revised: util.List[T]): Patch[T]
7+
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
package com.softwaremill.diffx.instances.string
2+
3+
import com.softwaremill.diffx.{DiffResult, DiffResultValue, IdenticalValue}
4+
5+
import java.util
6+
7+
object DiffUtils {
8+
def generateUnifiedDiff(
9+
original: String,
10+
revised: String,
11+
originalLines: util.List[String],
12+
patch: Patch[String],
13+
contextSize: Int
14+
): util.List[DiffResult] = {
15+
if (!patch.getDeltas.isEmpty) {
16+
val ret: util.List[DiffResult] = new util.ArrayList()
17+
// ret.add("--- " + original)
18+
// ret.add("+++ " + revised)
19+
val patchDeltas: util.List[Delta[String]] =
20+
new util.ArrayList(patch.getDeltas)
21+
val deltas: util.List[Delta[String]] = new util.ArrayList()
22+
23+
var delta = patchDeltas.get(0)
24+
deltas.add(delta)
25+
26+
if (patchDeltas.size() > 1) {
27+
for (i <- 1 until patchDeltas.size) {
28+
val position = delta.getOriginal.getPosition // store
29+
// the
30+
// current
31+
// position
32+
// of
33+
// the first Delta
34+
// Check if the next Delta is too close to the current
35+
// position.
36+
// And if it is, add it to the current set
37+
val nextDelta = patchDeltas.get(i)
38+
if (
39+
(position + delta.getOriginal.size + contextSize) >=
40+
(nextDelta.getOriginal.getPosition - contextSize)
41+
) {
42+
deltas.add(nextDelta)
43+
} else { // if it isn't, output the current set,
44+
// then create a new set and add the current Delta to
45+
// it.
46+
val curBlock = processDeltas(originalLines, deltas, contextSize)
47+
ret.addAll(curBlock)
48+
deltas.clear()
49+
deltas.add(nextDelta)
50+
}
51+
delta = nextDelta
52+
}
53+
}
54+
val curBlock = processDeltas(originalLines, deltas, contextSize)
55+
ret.addAll(curBlock)
56+
ret
57+
} else {
58+
new util.ArrayList[DiffResult]()
59+
}
60+
}
61+
def diff(
62+
original: util.List[String],
63+
revised: util.List[String]
64+
): Patch[String] =
65+
new MyersDiff[String]().diff(original, revised)
66+
67+
private def processDeltas(
68+
origLines: util.List[String],
69+
deltas: util.List[Delta[String]],
70+
contextSize: Int
71+
) = {
72+
val buffer = new util.ArrayList[DiffResult]
73+
var origTotal = 0 // counter for total lines output from Original
74+
var revTotal = 0
75+
var line = 0
76+
var curDelta = deltas.get(0)
77+
// NOTE: +1 to overcome the 0-offset Position
78+
var origStart = curDelta.getOriginal.getPosition + 1 - contextSize
79+
if (origStart < 1) origStart = 1
80+
var revStart = curDelta.getRevised.getPosition + 1 - contextSize
81+
if (revStart < 1) revStart = 1
82+
// find the start of the wrapper context code
83+
var contextStart = curDelta.getOriginal.getPosition - contextSize
84+
if (contextStart < 0) contextStart = 0 // clamp to the start of the file
85+
// output the context before the first Delta
86+
line = contextStart
87+
while ({
88+
line < curDelta.getOriginal.getPosition
89+
}) { //
90+
buffer.add(IdenticalValue(" " + origLines.get(line)))
91+
origTotal += 1
92+
revTotal += 1
93+
94+
line += 1
95+
}
96+
// output the first Delta
97+
buffer.add(getDeltaText(curDelta))
98+
origTotal += curDelta.getOriginal.getLines.size
99+
revTotal += curDelta.getRevised.getLines.size
100+
var deltaIndex = 1
101+
while ({
102+
deltaIndex < deltas.size
103+
}) { // for each of the other Deltas
104+
val nextDelta = deltas.get(deltaIndex)
105+
val intermediateStart =
106+
curDelta.getOriginal.getPosition + curDelta.getOriginal.getLines.size
107+
line = intermediateStart
108+
while ({
109+
line < nextDelta.getOriginal.getPosition
110+
}) { // output the code between the last Delta and this one
111+
buffer.add(IdenticalValue(" " + origLines.get(line)))
112+
origTotal += 1
113+
revTotal += 1
114+
115+
line += 1
116+
}
117+
buffer.add(getDeltaText(nextDelta)) // output the Delta
118+
119+
origTotal += nextDelta.getOriginal.getLines.size
120+
revTotal += nextDelta.getRevised.getLines.size
121+
curDelta = nextDelta
122+
deltaIndex += 1
123+
}
124+
// Now output the post-Delta context code, clamping the end of the file
125+
contextStart = curDelta.getOriginal.getPosition + curDelta.getOriginal.getLines.size
126+
line = contextStart
127+
while ({
128+
(line < (contextStart + contextSize)) && (line < origLines.size)
129+
}) {
130+
buffer.add(IdenticalValue(" " + origLines.get(line)))
131+
origTotal += 1
132+
revTotal += 1
133+
134+
line += 1
135+
}
136+
// Create and insert the block header, conforming to the Unified Diff
137+
// // standard
138+
// val header = new StringBuffer
139+
// header.append("@@ -")
140+
// header.append(origStart)
141+
// header.append(",")
142+
// header.append(origTotal)
143+
// header.append(" +")
144+
// header.append(revStart)
145+
// header.append(",")
146+
// header.append(revTotal)
147+
// header.append(" @@")
148+
// buffer.add(0, header.toString)
149+
buffer
150+
}
151+
152+
private def getDeltaText(delta: Delta[String]) = { //TOOD tutaj trzeba zmienic na missing/additional
153+
import scala.collection.JavaConverters._
154+
// val buffer = new util.ArrayList[String]
155+
// for (line <- delta.getOriginal.getLines.asScala) {
156+
// buffer.add("-" + line)
157+
// }
158+
// for (line <- delta.getRevised.getLines.asScala) {
159+
// buffer.add("+" + line)
160+
// }
161+
// buffer
162+
DiffResultValue(delta.getOriginal.getLines.asScala.mkString("\n"), delta.getRevised.getLines.asScala.mkString("\n"))
163+
}
164+
165+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package com.softwaremill.diffx.instances.string
2+
3+
class DifferentiationFailedException(message: String) extends Exception(message)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.softwaremill.diffx.instances.string
2+
3+
trait Equalizer[T] {
4+
def equals(original: T, revised: T): Boolean
5+
}
6+
object Equalizer {
7+
def default[T]: Equalizer[T] = new Equalizer[T] {
8+
override def equals(original: T, revised: T): Boolean = {
9+
original.equals(revised)
10+
}
11+
}
12+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package com.softwaremill.diffx.instances.string
2+
3+
import com.softwaremill.diffx.DiffResultString
4+
5+
import scala.collection.JavaConverters._
6+
7+
class ExDiff(val obtained: String, val expected: String) extends Serializable {
8+
val obtainedClean: String = filterAnsi(obtained)
9+
val expectedClean: String = filterAnsi(expected)
10+
val obtainedLines: Seq[String] = splitIntoLines(obtainedClean)
11+
val expectedLines: Seq[String] = splitIntoLines(expectedClean)
12+
val unifiedDiff: DiffResultString = createUnifiedDiff(obtainedLines, expectedLines)
13+
14+
def createReport(
15+
title: String,
16+
printObtainedAsStripMargin: Boolean = true
17+
): String = {
18+
val sb = new StringBuilder
19+
if (title.nonEmpty) {
20+
sb.append(title)
21+
.append("\n")
22+
}
23+
if (obtainedClean.length < 1000) {
24+
header("Obtained", sb).append("\n")
25+
if (printObtainedAsStripMargin) {
26+
sb.append(asStripMargin(obtainedClean))
27+
} else {
28+
sb.append(obtainedClean)
29+
}
30+
sb.append("\n")
31+
}
32+
appendDiffOnlyReport(sb)
33+
sb.toString()
34+
}
35+
36+
def createDiffOnlyReport(): String = {
37+
val out = new StringBuilder
38+
appendDiffOnlyReport(out)
39+
out.toString()
40+
}
41+
42+
private def appendDiffOnlyReport(sb: StringBuilder): Unit = {
43+
sb.append(unifiedDiff)
44+
}
45+
46+
private def asStripMargin(obtained: String): String = {
47+
if (!obtained.contains("\n")) obtained
48+
else {
49+
val out = new StringBuilder
50+
val lines = obtained.trim.linesIterator
51+
val head = if (lines.hasNext) lines.next() else ""
52+
out.append(" \"\"\"|" + head + "\n")
53+
lines.foreach(line => {
54+
out.append(" |").append(line).append("\n")
55+
})
56+
out.append(" |\"\"\".stripMargin")
57+
out.toString()
58+
}
59+
}
60+
61+
private def header(t: String, sb: StringBuilder): StringBuilder = {
62+
sb.append(s"=> $t")
63+
}
64+
65+
private def createUnifiedDiff(
66+
original: Seq[String],
67+
revised: Seq[String]
68+
): DiffResultString = {
69+
val diff = DiffUtils.diff(original.asJava, revised.asJava)
70+
val result = DiffUtils
71+
.generateUnifiedDiff(
72+
"obtained",
73+
"expected",
74+
original.asJava,
75+
diff,
76+
1
77+
)
78+
.asScala
79+
.iterator
80+
DiffResultString(result.toList)
81+
}
82+
83+
private def splitIntoLines(string: String): Seq[String] = {
84+
string.trim().replace("\r\n", "\n").split("\n").toIndexedSeq
85+
}
86+
87+
private def filterAnsi(s: String): String = {
88+
if (s == null) {
89+
null
90+
} else {
91+
val len = s.length
92+
val r = new java.lang.StringBuilder(len)
93+
var i = 0
94+
while (i < len) {
95+
val c = s.charAt(i)
96+
if (c == '\u001B') {
97+
i += 1
98+
while (i < len && s.charAt(i) != 'm') i += 1
99+
} else {
100+
r.append(c)
101+
}
102+
i += 1
103+
}
104+
r.toString()
105+
}
106+
}
107+
}

0 commit comments

Comments
 (0)