Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ libraryDependencies ++= {
// 3.2.20 of below depends on scala3-library 3.1.3, so use this one for cross-compilation.
"org.scalatest" %% "scalatest" % "3.2.20" % "test",
// 2.9.0 of below depends on scala3-library 3.1.3, so use this one for cross-compilation.
"org.scala-lang.modules" %% "scala-collection-compat" % "2.7.0" % "test",
"ai.lum" %% "common" % "0.2.0-SNAPSHOT",
"org.scala-lang.modules" %% "scala-collection-compat" % "2.9.0" % "test",
"org.scala-lang.modules" %% "scala-xml" % scalaXmlVersion
)
}
Expand Down
100 changes: 100 additions & 0 deletions src/main/scala/ai/lum/nxmlreader/Interval.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package ai.lum.nxmlreader

class Interval private (val start: Int, val end: Int) extends Ordered[Interval] {

override def toString: String = {
if (isEmpty) "{}"
else if (length == 1) s"{$start}"
else s"[$start, $end)"
}

// Since Intervals will be used as keys in a map,
// both equals and hashCode are necessary.
override def equals(that: Any): Boolean = that match {
case that: Interval => that.canEqual(this) && {
if (this.isEmpty) that.isEmpty
else {
// Since this is not empty, it will not match
// an empty that.
this.start == that.start &&
this.end == that.end
}
}
case _ => false
}

def canEqual(that: Any): Boolean = that.isInstanceOf[Interval]

override def hashCode: Int = {
start * 23 + end
}

def length = end - start

def isEmpty: Boolean = length <= 0

def intersects(that: Interval): Boolean = {
if (this.isEmpty || that.isEmpty)
false
else if (this.start < that.start)
this.end > that.start
else if (that.start < this.start)
that.end > this.start
else
true
}

def borders(that: Interval): Boolean = {
if (this.isEmpty || that.isEmpty) false
else {
this.start == that.end ||
that.start == this.end
}
}

override def compare(that: Interval): Int = {
if (this.start > that.start) 1
else if (this.start < that.start) -1
else this.length - that.length
}

def union(that: Interval): Interval = {
if (this.isEmpty) that
else if (that.isEmpty) this
else {
require(this.borders(that) || this.intersects(that))
Interval.open(
math.min(this.start, that.start),
math.max(this.end, that.end)
)
}
}
}

object Interval {
val emptyInterval = new Interval(0, 0)

def apply(start: Int, end: Int): Interval = {
require(start <= end, s"end < start: $end < $start")
if (start < end) new Interval(start, end)
else emptyInterval
}

def singleton(i: Int): Interval = apply(i, i + 1)

def open(start: Int, end: Int): Interval = apply(start, end)

def ofLength(start: Int, length: Int): Interval = open(start, start + length)

def union(col: Seq[Interval]): Interval = {
val sorted = col.sorted

try {
sorted.reduceLeft(_.union(_))
}
catch {
case _: IllegalArgumentException =>
throw new IllegalArgumentException("gap in intervals: " + sorted)
}
}
}
4 changes: 1 addition & 3 deletions src/main/scala/ai/lum/nxmlreader/NxmlDocument.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package ai.lum.nxmlreader

import scala.xml._
import ai.lum.common.Interval
import ai.lum.nxmlreader.standoff._

import scala.annotation.tailrec

import scala.xml._

class NxmlDocument(val root: Node, val preprocessor: Preprocessor) {

Expand Down
3 changes: 1 addition & 2 deletions src/main/scala/ai/lum/nxmlreader/standoff/Tree.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package ai.lum.nxmlreader.standoff

import ai.lum.common.Interval

import ai.lum.nxmlreader.Interval

sealed trait Tree {

Expand Down