All Downloads are FREE. Search and download functionalities are using the official Maven repository.

munit.internal.difflib.MyersDiff.scala Maven / Gradle / Ivy

There is a newer version: 1.0.0-M10
Show newest version
package munit.internal.difflib

import java.util

class MyersDiff[T](equalizer: Equalizer[T])
    extends munit.internal.difflib.DiffAlgorithm[T] {
  def this() = this(Equalizer.default[T])
  override def diff(
      original: util.List[T],
      revised: util.List[T]
  ): munit.internal.difflib.Patch[T] = {
    try {
      buildRevision(buildPath(original, revised), original, revised)
    } catch {
      case e: DifferentiationFailedException =>
        e.printStackTrace()
        new munit.internal.difflib.Patch[T]()
    }
  }
  private def buildRevision(
      _path: PathNode,
      orig: util.List[T],
      rev: util.List[T]
  ): munit.internal.difflib.Patch[T] = {
    var path = _path
    val patch = new munit.internal.difflib.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.internal.difflib.Chunk[T](
          ianchor,
          copyOfRange(orig, ianchor, i)
        )
      val revised =
        new munit.internal.difflib.Chunk[T](
          janchor,
          copyOfRange(rev, janchor, j)
        )
      val delta: munit.internal.difflib.Delta[T] =
        if (original.size == 0 && revised.size != 0) {
          new munit.internal.difflib.InsertDelta[T](original, revised)
        } else if (original.size > 0 && revised.size == 0) {
          new munit.internal.difflib.DeleteDelta[T](original, revised)
        } else {
          new munit.internal.difflib.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")
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy