summaryrefslogtreecommitdiff
path: root/javalib/src/main/scala/java/util
diff options
context:
space:
mode:
Diffstat (limited to 'javalib/src/main/scala/java/util')
-rw-r--r--javalib/src/main/scala/java/util/Arrays.scala401
-rw-r--r--javalib/src/main/scala/java/util/Comparator.scala6
-rw-r--r--javalib/src/main/scala/java/util/Date.scala147
-rw-r--r--javalib/src/main/scala/java/util/Formattable.scala5
-rw-r--r--javalib/src/main/scala/java/util/FormattableFlags.scala7
-rw-r--r--javalib/src/main/scala/java/util/Formatter.scala273
-rw-r--r--javalib/src/main/scala/java/util/Random.scala119
-rw-r--r--javalib/src/main/scala/java/util/Throwables.scala166
-rw-r--r--javalib/src/main/scala/java/util/UUID.scala163
-rw-r--r--javalib/src/main/scala/java/util/concurrent/ExecutionException.scala9
-rw-r--r--javalib/src/main/scala/java/util/concurrent/Executor.scala5
-rw-r--r--javalib/src/main/scala/java/util/concurrent/TimeUnit.scala133
-rw-r--r--javalib/src/main/scala/java/util/concurrent/atomic/AtomicBoolean.scala33
-rw-r--r--javalib/src/main/scala/java/util/concurrent/atomic/AtomicInteger.scala63
-rw-r--r--javalib/src/main/scala/java/util/concurrent/atomic/AtomicLong.scala61
-rw-r--r--javalib/src/main/scala/java/util/concurrent/atomic/AtomicReference.scala34
-rw-r--r--javalib/src/main/scala/java/util/regex/MatchResult.scala13
-rw-r--r--javalib/src/main/scala/java/util/regex/Matcher.scala274
-rw-r--r--javalib/src/main/scala/java/util/regex/Pattern.scala154
19 files changed, 2066 insertions, 0 deletions
diff --git a/javalib/src/main/scala/java/util/Arrays.scala b/javalib/src/main/scala/java/util/Arrays.scala
new file mode 100644
index 0000000..ed9afd1
--- /dev/null
+++ b/javalib/src/main/scala/java/util/Arrays.scala
@@ -0,0 +1,401 @@
+package java.util
+
+import scala.annotation.tailrec
+
+object Arrays {
+ def sort[T <: Object](array: Array[Object], comparator: Comparator[T]): Unit = {
+ scala.util.Sorting.stableSort[Object](array,
+ (a: Object, b: Object) =>
+ comparator.compare(a.asInstanceOf[T], b.asInstanceOf[T]) < 0)
+ }
+
+ def fill(a: Array[Boolean], value: Boolean): Unit =
+ fillImpl(a, value)
+
+ def fill(a: Array[Boolean], fromIndex: Int, toIndex: Int, value: Boolean): Unit =
+ fillImpl(a, fromIndex, toIndex, value)
+
+ def fill(a: Array[Byte], value: Byte): Unit =
+ fillImpl(a, value)
+
+ def fill(a: Array[Byte], fromIndex: Int, toIndex: Int, value: Byte): Unit =
+ fillImpl(a, fromIndex, toIndex, value)
+
+ def fill(a: Array[Char], value: Char): Unit =
+ fillImpl(a, value)
+
+ def fill(a: Array[Char], fromIndex: Int, toIndex: Int, value: Char): Unit =
+ fillImpl(a, fromIndex, toIndex, value)
+
+ def fill(a: Array[Short], value: Short): Unit =
+ fillImpl(a, value)
+
+ def fill(a: Array[Short], fromIndex: Int, toIndex: Int, value: Short): Unit =
+ fillImpl(a, fromIndex, toIndex, value)
+
+ def fill(a: Array[Int], value: Int): Unit =
+ fillImpl(a, value)
+
+ def fill(a: Array[Int], fromIndex: Int, toIndex: Int, value: Int): Unit =
+ fillImpl(a, fromIndex, toIndex, value)
+
+ def fill(a: Array[Long], value: Long): Unit =
+ fillImpl(a, value)
+
+ def fill(a: Array[Long], fromIndex: Int, toIndex: Int, value: Long): Unit =
+ fillImpl(a, fromIndex, toIndex, value)
+
+ def fill(a: Array[Float], value: Float): Unit =
+ fillImpl(a, value)
+
+ def fill(a: Array[Float], fromIndex: Int, toIndex: Int, value: Float): Unit =
+ fillImpl(a, fromIndex, toIndex, value)
+
+ def fill(a: Array[Double], value: Double): Unit =
+ fillImpl(a, value)
+
+ def fill(a: Array[Double], fromIndex: Int, toIndex: Int, value: Double): Unit =
+ fillImpl(a, fromIndex, toIndex, value)
+
+ private def fillImpl[@specialized T](a: Array[T], value: T): Unit = {
+ var i = 0
+ while (i != a.length) {
+ a(i) = value
+ i += 1
+ }
+ }
+
+ private def fillImpl[@specialized T](a: Array[T],
+ fromIndex: Int, toIndex: Int, value: T): Unit = {
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException
+ if (fromIndex < 0 || toIndex > a.length)
+ throw new ArrayIndexOutOfBoundsException
+
+ var i = fromIndex
+ while (i != toIndex) {
+ a(i) = value
+ i += 1
+ }
+ }
+
+ def fill(a: Array[AnyRef], value: AnyRef): Unit = {
+ var i = 0
+ while (i < a.length) {
+ a(i) = value
+ i += 1
+ }
+ }
+
+ def fill(a: Array[AnyRef],
+ fromIndex: Int, toIndex: Int, value: AnyRef): Unit = {
+ if (fromIndex > toIndex)
+ throw new IllegalArgumentException
+ if (fromIndex < 0 || toIndex > a.length)
+ throw new ArrayIndexOutOfBoundsException
+
+ var i = fromIndex
+ while (i < toIndex) {
+ a(i) = value
+ i += 1
+ }
+ }
+
+ @inline private def checkIndexForBinarySearch(
+ length: Int, start: Int, end: Int): Unit = {
+ if (start > end)
+ throw new IllegalArgumentException("fromIndex(" + start + ") > toIndex(" + end + ")")
+ if (start < 0)
+ throw new ArrayIndexOutOfBoundsException("Array index out of range: " + start)
+ if (end > length)
+ throw new ArrayIndexOutOfBoundsException("Array index out of range: " + end)
+ }
+
+ def binarySearch(a: Array[Char], key: Char): Int =
+ binarySearchImpl[Char](a, 0, a.length, key, _ < _)
+
+ def binarySearch(a: Array[Char],
+ startIndex: Int, endIndex: Int, key: Char): Int = {
+ checkIndexForBinarySearch(a.length, startIndex, endIndex)
+ binarySearchImpl[Char](a, startIndex, endIndex, key, _ < _)
+ }
+
+ def binarySearch(a: Array[Short], key: Short): Int =
+ binarySearchImpl[Short](a, 0, a.length, key, _ < _)
+
+ def binarySearch(a: Array[Short],
+ startIndex: Int, endIndex: Int, key: Short): Int = {
+ checkIndexForBinarySearch(a.length, startIndex, endIndex)
+ binarySearchImpl[Short](a, startIndex, endIndex, key, _ < _)
+ }
+
+ def binarySearch(a: Array[Int], key: Int): Int =
+ binarySearchImpl[Int](a, 0, a.length, key, _ < _)
+
+ def binarySearch(a: Array[Int],
+ startIndex: Int, endIndex: Int, key: Int): Int = {
+ checkIndexForBinarySearch(a.length, startIndex, endIndex)
+ binarySearchImpl[Int](a, startIndex, endIndex, key, _ < _)
+ }
+
+ def binarySearch(a: Array[Long], key: Long): Int =
+ binarySearchImpl[Long](a, 0, a.length, key, _ < _)
+
+ def binarySearch(a: Array[Long],
+ startIndex: Int, endIndex: Int, key: Long): Int = {
+ checkIndexForBinarySearch(a.length, startIndex, endIndex)
+ binarySearchImpl[Long](a, startIndex, endIndex, key, _ < _)
+ }
+
+ def binarySearch(a: Array[Float], key: Float): Int =
+ binarySearchImpl[Float](a, 0, a.length, key, _ < _)
+
+ def binarySearch(a: Array[Float],
+ startIndex: Int, endIndex: Int, key: Float): Int = {
+ checkIndexForBinarySearch(a.length, startIndex, endIndex)
+ binarySearchImpl[Float](a, startIndex, endIndex, key, _ < _)
+ }
+
+ def binarySearch(a: Array[Double], key: Double): Int =
+ binarySearchImpl[Double](a, 0, a.length, key, _ < _)
+
+ def binarySearch(a: Array[Double],
+ startIndex: Int, endIndex: Int, key: Double): Int = {
+ checkIndexForBinarySearch(a.length, startIndex, endIndex)
+ binarySearchImpl[Double](a, startIndex, endIndex, key, _ < _)
+ }
+
+ @inline
+ @tailrec
+ private def binarySearchImpl[@specialized T](a: Array[T],
+ startIndex: Int, endIndex: Int, key: T, lt: (T, T) => Boolean): Int = {
+ if (startIndex == endIndex) {
+ // Not found
+ -startIndex - 1
+ } else {
+ // Indices are unsigned 31-bit integer, so this does not overflow
+ val mid = (startIndex + endIndex) >>> 1
+ val elem = a(mid)
+ if (lt(key, elem)) {
+ binarySearchImpl(a, startIndex, mid, key, lt)
+ } else if (key == elem) {
+ // Found
+ mid
+ } else {
+ binarySearchImpl(a, mid + 1, endIndex, key, lt)
+ }
+ }
+ }
+
+ def binarySearch(a: Array[AnyRef], key: AnyRef): Int =
+ binarySearchImplRef(a, 0, a.length, key)
+
+ def binarySearch(a: Array[AnyRef],
+ startIndex: Int, endIndex: Int, key: AnyRef): Int = {
+ checkIndexForBinarySearch(a.length, startIndex, endIndex)
+ binarySearchImplRef(a, startIndex, endIndex, key)
+ }
+
+ @inline
+ @tailrec
+ def binarySearchImplRef(a: Array[AnyRef],
+ startIndex: Int, endIndex: Int, key: AnyRef): Int = {
+ if (startIndex == endIndex) {
+ // Not found
+ -startIndex - 1
+ } else {
+ // Indices are unsigned 31-bit integer, so this does not overflow
+ val mid = (startIndex + endIndex) >>> 1
+ val cmp = key.asInstanceOf[Comparable[AnyRef]].compareTo(a(mid))
+ if (cmp < 0) {
+ binarySearchImplRef(a, startIndex, mid, key)
+ } else if (cmp == 0) {
+ // Found
+ mid
+ } else {
+ binarySearchImplRef(a, mid + 1, endIndex, key)
+ }
+ }
+ }
+
+ def copyOf(original: Array[Boolean], newLength: Int): Array[Boolean] =
+ copyOfImpl(original, newLength, new Array(_))
+
+ def copyOfRange(original: Array[Boolean], start: Int, end: Int): Array[Boolean] =
+ copyOfRangeImpl(original, start, end, new Array(_))
+
+ def copyOf(original: Array[Char], newLength: Int): Array[Char] =
+ copyOfImpl(original, newLength, new Array(_))
+
+ def copyOfRange(original: Array[Char], start: Int, end: Int): Array[Char] =
+ copyOfRangeImpl(original, start, end, new Array(_))
+
+ def copyOf(original: Array[Byte], newLength: Int): Array[Byte] =
+ copyOfImpl(original, newLength, new Array(_))
+
+ def copyOfRange(original: Array[Byte], start: Int, end: Int): Array[Byte] =
+ copyOfRangeImpl(original, start, end, new Array(_))
+
+ def copyOf(original: Array[Short], newLength: Int): Array[Short] =
+ copyOfImpl(original, newLength, new Array(_))
+
+ def copyOfRange(original: Array[Short], start: Int, end: Int): Array[Short] =
+ copyOfRangeImpl(original, start, end, new Array(_))
+
+ def copyOf(original: Array[Int], newLength: Int): Array[Int] =
+ copyOfImpl(original, newLength, new Array(_))
+
+ def copyOfRange(original: Array[Int], start: Int, end: Int): Array[Int] =
+ copyOfRangeImpl(original, start, end, new Array(_))
+
+ def copyOf(original: Array[Long], newLength: Int): Array[Long] =
+ copyOfImpl(original, newLength, new Array(_))
+
+ def copyOfRange(original: Array[Long], start: Int, end: Int): Array[Long] =
+ copyOfRangeImpl(original, start, end, new Array(_))
+
+ def copyOf(original: Array[Float], newLength: Int): Array[Float] =
+ copyOfImpl(original, newLength, new Array(_))
+
+ def copyOfRange(original: Array[Float], start: Int, end: Int): Array[Float] =
+ copyOfRangeImpl(original, start, end, new Array(_))
+
+ def copyOf(original: Array[Double], newLength: Int): Array[Double] =
+ copyOfImpl(original, newLength, new Array(_))
+
+ def copyOfRange(original: Array[Double], start: Int, end: Int): Array[Double] =
+ copyOfRangeImpl(original, start, end, new Array(_))
+
+ @inline private def copyOfImpl[@specialized T](original: Array[T],
+ newLength: Int, newArray: Int => Array[T]): Array[T] = {
+ checkArrayLength(newLength)
+ val copyLength = Math.min(newLength, original.length)
+ val ret = newArray(newLength)
+ System.arraycopy(original, 0, ret, 0, copyLength)
+ ret
+ }
+
+ @inline private def copyOfRangeImpl[@specialized T](original: Array[T],
+ start: Int, end: Int, newArray: Int => Array[T]): Array[T] = {
+ checkIndicesForCopyOfRange(original.length, start, end)
+ val retLength = end - start
+ val copyLength = Math.min(retLength, original.length - start)
+ val ret = newArray(retLength)
+ System.arraycopy(original, start, ret, 0, copyLength)
+ ret
+ }
+
+ def copyOf(original: Array[AnyRef], newLength: Int): Array[AnyRef] = {
+ checkArrayLength(newLength)
+ val copyLength = Math.min(newLength, original.length)
+ val ret = java.lang.reflect.Array.newInstance(
+ original.getClass.getComponentType, newLength).asInstanceOf[Array[AnyRef]]
+ System.arraycopy(original, 0, ret, 0, copyLength)
+ ret
+ }
+
+ def copyOfRange(original: Array[AnyRef], start: Int, end: Int): Array[AnyRef] = {
+ checkIndicesForCopyOfRange(original.length, start, end)
+ val retLength = end - start
+ val copyLength = Math.min(retLength, original.length - start)
+ val ret = java.lang.reflect.Array.newInstance(
+ original.getClass.getComponentType, retLength).asInstanceOf[Array[AnyRef]]
+ System.arraycopy(original, start, ret, 0, copyLength)
+ ret
+ }
+
+ @inline private def checkArrayLength(len: Int): Unit = {
+ if (len < 0)
+ throw new NegativeArraySizeException
+ }
+
+ @inline private def checkIndicesForCopyOfRange(
+ len: Int, start: Int, end: Int): Unit = {
+ if (start > end)
+ throw new IllegalArgumentException(start + " > " + end)
+ if (start < 0 || start > len)
+ throw new ArrayIndexOutOfBoundsException
+ }
+
+ def hashCode(a: Array[Boolean]): Int = {
+ if (a == null) 0
+ else a.foldLeft(1)((acc, x) => 31*acc + new java.lang.Boolean(x).hashCode)
+ }
+
+ def hashCode(a: Array[Char]): Int = {
+ if (a == null) 0
+ else a.foldLeft(1)((acc, x) => 31*acc + new java.lang.Character(x).hashCode)
+ }
+
+ def hashCode(a: Array[Byte]): Int = {
+ if (a == null) 0
+ else a.foldLeft(1)((acc, x) => 31*acc + new java.lang.Byte(x).hashCode)
+ }
+
+ def hashCode(a: Array[Short]): Int = {
+ if (a == null) 0
+ else a.foldLeft(1)((acc, x) => 31*acc + new java.lang.Short(x).hashCode)
+ }
+
+ def hashCode(a: Array[Int]): Int = {
+ if (a == null) 0
+ else a.foldLeft(1)((acc, x) => 31*acc + new java.lang.Integer(x).hashCode)
+ }
+
+ def hashCode(a: Array[Long]): Int = {
+ if (a == null) 0
+ else a.foldLeft(1)((acc, x) => 31*acc + new java.lang.Long(x).hashCode)
+ }
+
+ def hashCode(a: Array[Float]): Int = {
+ if (a == null) 0
+ else a.foldLeft(1)((acc, x) => 31*acc + new java.lang.Float(x).hashCode)
+ }
+
+ def hashCode(a: Array[Double]): Int = {
+ if (a == null) 0
+ else a.foldLeft(1)((acc, x) => 31*acc + new java.lang.Double(x).hashCode)
+ }
+
+ def hashCode(a: Array[AnyRef]): Int = {
+ if (a == null) 0
+ else a.foldLeft(1)((acc, x) => 31*acc + (if (x == null) 0 else x.hashCode))
+ }
+
+ def equals(a: Array[Boolean], b: Array[Boolean]): Boolean =
+ (a eq b) || (a != null && b != null && a.length == b.length &&
+ (0 until a.size).forall(i => a(i) == b(i)))
+
+ def equals(a: Array[Char], b: Array[Char]): Boolean =
+ (a eq b) || (a != null && b != null && a.length == b.length &&
+ (0 until a.size).forall(i => a(i) == b(i)))
+
+ def equals(a: Array[Byte], b: Array[Byte]): Boolean =
+ (a eq b) || (a != null && b != null && a.length == b.length &&
+ (0 until a.size).forall(i => a(i) == b(i)))
+
+ def equals(a: Array[Short], b: Array[Short]): Boolean =
+ (a eq b) || (a != null && b != null && a.length == b.length &&
+ (0 until a.size).forall(i => a(i) == b(i)))
+
+ def equals(a: Array[Int], b: Array[Int]): Boolean =
+ (a eq b) || (a != null && b != null && a.length == b.length &&
+ (0 until a.size).forall(i => a(i) == b(i)))
+
+ def equals(a: Array[Long], b: Array[Long]): Boolean =
+ (a eq b) || (a != null && b != null && a.length == b.length &&
+ (0 until a.size).forall(i => a(i) == b(i)))
+
+ def equals(a: Array[Float], b: Array[Float]): Boolean =
+ (a eq b) || (a != null && b != null && a.length == b.length &&
+ (0 until a.size).forall(i => a(i) == b(i)))
+
+ def equals(a: Array[Double], b: Array[Double]): Boolean =
+ (a eq b) || (a != null && b != null && a.length == b.length &&
+ (0 until a.size).forall(i => a(i) == b(i)))
+
+ def equals(a: Array[AnyRef], b: Array[AnyRef]): Boolean =
+ (a eq b) || (a != null && b != null && a.length == b.length &&
+ (0 until a.size).forall(i => a(i) == b(i)))
+
+}
diff --git a/javalib/src/main/scala/java/util/Comparator.scala b/javalib/src/main/scala/java/util/Comparator.scala
new file mode 100644
index 0000000..d63c48e
--- /dev/null
+++ b/javalib/src/main/scala/java/util/Comparator.scala
@@ -0,0 +1,6 @@
+package java.util
+
+trait Comparator[A] {
+ def compare(o1: A, o2: A): Int
+ def equals(obj: Any): Boolean
+}
diff --git a/javalib/src/main/scala/java/util/Date.scala b/javalib/src/main/scala/java/util/Date.scala
new file mode 100644
index 0000000..8f4e85f
--- /dev/null
+++ b/javalib/src/main/scala/java/util/Date.scala
@@ -0,0 +1,147 @@
+/**
+ * 2014 Matt Seddon
+ * This code is donated in full to the scala-js project.
+ */
+
+package java.util
+
+import scalajs.js
+
+class Date private (private val date: js.Date) extends Object
+ with Serializable with Cloneable with Comparable[Date] {
+
+ import Date._
+
+ def this() = this(new js.Date())
+
+ @Deprecated
+ def this(year: Int, month: Int, date: Int, hrs: Int, min: Int, sec: Int) = {
+ this(new js.Date())
+ this.date.setFullYear(1900 + year, month, date)
+ this.date.setHours(hrs, min, sec, 0)
+ }
+
+ @Deprecated
+ def this(year: Int, month: Int, date: Int, hrs: Int, min: Int) =
+ this(year, month, date, hrs, min, 0)
+
+ @Deprecated
+ def this(year: Int, month: Int, date: Int) =
+ this(year, month, date, 0, 0, 0)
+
+ def this(date: Long) = this(new js.Date(date))
+
+ @Deprecated
+ def this(date: String) = this(new js.Date(date))
+
+ def after(when: Date): Boolean = date.getTime() > when.date.getTime()
+
+ def before(when: Date): Boolean = date.getTime() < when.date.getTime()
+
+ override def clone(): Object = new Date(new js.Date(date.getTime()))
+
+ override def compareTo(anotherDate: Date): Int =
+ date.getTime().compareTo(anotherDate.date.getTime())
+
+ override def equals(obj: Any): Boolean = obj match {
+ case d: Date => d.date.getTime() == date.getTime()
+ case _ => false
+ }
+
+ override def hashCode(): Int = date.getTime().hashCode()
+
+ @Deprecated
+ def getDate(): Int = date.getDate()
+
+ @Deprecated
+ def getDay(): Int = date.getDay()
+
+ @Deprecated
+ def getHours(): Int = date.getHours()
+
+ @Deprecated
+ def getMinutes(): Int = date.getMinutes()
+
+ @Deprecated
+ def getMonth(): Int = date.getMonth()
+
+ @Deprecated
+ def getSeconds(): Int = date.getSeconds()
+
+ def getTime(): Long = date.getTime().toLong
+
+ @Deprecated
+ def getTimeZoneOffset(): Int = date.getTimezoneOffset()
+
+ @Deprecated
+ def getYear(): Int = date.getFullYear() - 1900
+
+ @Deprecated
+ def setDate(date: Int): Unit = this.date.setDate(date)
+
+ @Deprecated
+ def setHours(hours: Int): Unit = date.setHours(hours)
+
+ @Deprecated
+ def setMinutes(minutes: Int): Unit = date.setMinutes(minutes)
+
+ @Deprecated
+ def setMonth(month: Int): Unit = date.setMonth(month)
+
+ @Deprecated
+ def setSeconds(seconds: Int): Unit = date.setSeconds(seconds)
+
+ def setTime(time: Long): Unit = date.setTime(time)
+
+ @Deprecated
+ def setYear(year: Int): Unit = date.setFullYear(1900 + year)
+
+ @Deprecated
+ def toGMTString(): String = {
+ date.getUTCDate() + " " + Months(date.getUTCMonth()) + " " +
+ date.getUTCFullYear() + " " + pad0(date.getUTCHours()) + ":" +
+ pad0(date.getUTCMinutes()) + ":" +
+ pad0(date.getUTCSeconds()) +" GMT"
+ }
+
+ @Deprecated
+ def toLocaleString(): String = {
+ date.getDate() + "-" + Months(date.getMonth()) + "-" +
+ date.getFullYear() + "-" + pad0(date.getHours()) + ":" +
+ pad0(date.getMinutes()) + ":" + pad0(date.getSeconds())
+ }
+
+ override def toString(): String = {
+ val offset = -date.getTimezoneOffset()
+ val sign = if(offset < 0) "-" else "+"
+ val hours = pad0(Math.abs(offset) / 60)
+ val mins = pad0(Math.abs(offset) % 60)
+ Days(date.getDay()) + " "+ Months(date.getMonth()) + " " +
+ pad0(date.getHours()) + ":" + pad0(date.getMinutes()) + ":" +
+ pad0(date.getSeconds()) + " GMT" + sign + hours + mins + " " +
+ date.getFullYear()
+ }
+}
+
+object Date {
+ private val Days = Array(
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat")
+
+ private val Months = Array(
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")
+
+ private def pad0(i: Int): String = {
+ val str = "" + i
+ if (str.length < 2) "0" + str else str
+ }
+
+ @Deprecated
+ def UTC(year: Int, month: Int, date: Int,
+ hrs: Int, min: Int, sec: Int): Long =
+ js.Date.UTC(year + 1900, month, date, hrs, min, sec).toLong
+
+ @Deprecated
+ def parse(string: String): Long =
+ new Date(new js.Date(string)).getTime.toLong
+}
diff --git a/javalib/src/main/scala/java/util/Formattable.scala b/javalib/src/main/scala/java/util/Formattable.scala
new file mode 100644
index 0000000..e651fbb
--- /dev/null
+++ b/javalib/src/main/scala/java/util/Formattable.scala
@@ -0,0 +1,5 @@
+package java.util
+
+trait Formattable {
+ def formatTo(formatter: Formatter, flags: Int, width: Int, precision: Int): Unit
+}
diff --git a/javalib/src/main/scala/java/util/FormattableFlags.scala b/javalib/src/main/scala/java/util/FormattableFlags.scala
new file mode 100644
index 0000000..02f5bce
--- /dev/null
+++ b/javalib/src/main/scala/java/util/FormattableFlags.scala
@@ -0,0 +1,7 @@
+package java.util
+
+object FormattableFlags {
+ final val ALTERNATE = 4
+ final val LEFT_JUSTIFY = 1
+ final val UPPERCASE = 2
+}
diff --git a/javalib/src/main/scala/java/util/Formatter.scala b/javalib/src/main/scala/java/util/Formatter.scala
new file mode 100644
index 0000000..5e0ab22
--- /dev/null
+++ b/javalib/src/main/scala/java/util/Formatter.scala
@@ -0,0 +1,273 @@
+package java.util
+
+import scala.annotation.switch
+import scala.scalajs.js
+
+import java.io._
+import java.lang._
+
+final class Formatter(private val dest: Appendable) extends Closeable with Flushable {
+ import Formatter._
+
+ var closed = false
+
+ def this() = this(new StringBuilder())
+
+ def close(): Unit = {
+ if (!closed) {
+ dest match {
+ case cl: Closeable => cl.close()
+ case _ =>
+ }
+ }
+ closed = true
+ }
+
+ def flush(): Unit = ifNotClosed {
+ dest match {
+ case fl: Flushable => fl.flush()
+ case _ =>
+ }
+ }
+
+ // Begin implem of format()
+
+ def format(format_in: String, args: Array[AnyRef]): Formatter = ifNotClosed {
+ import js.Any.fromDouble // to have .toFixed and .toExponential on Doubles
+
+ var fmt: String = format_in
+ var lastImplicitIndex: Int = 0
+ var lastIndex: Int = 0 // required for < flag
+
+ while (!fmt.isEmpty) {
+ fmt match {
+ case RegularChunk(matchResult) =>
+ fmt = fmt.substring(matchResult(0).get.length)
+ dest.append(matchResult(0).get)
+
+ case DoublePercent(_) =>
+ fmt = fmt.substring(2)
+ dest.append('%')
+
+ case EOLChunk(_) =>
+ fmt = fmt.substring(2)
+ dest.append('\n')
+
+ case FormattedChunk(matchResult) =>
+ fmt = fmt.substring(matchResult(0).get.length)
+
+ val flags = matchResult(2).get
+ def hasFlag(flag: String) = flags.indexOf(flag) >= 0
+
+ val indexStr = matchResult(1).getOrElse("")
+ val index = if (!indexStr.isEmpty) {
+ Integer.parseInt(indexStr)
+ } else if (hasFlag("<")) {
+ lastIndex
+ } else {
+ lastImplicitIndex += 1
+ lastImplicitIndex
+ }
+ lastIndex = index
+ if (index <= 0 || index > args.length)
+ throw new MissingFormatArgumentException(matchResult(5).get)
+ val arg = args(index-1)
+
+ val widthStr = matchResult(3).getOrElse("")
+ val hasWidth = !widthStr.isEmpty
+ val width =
+ if (hasWidth) Integer.parseInt(widthStr)
+ else 0
+
+ val precisionStr = matchResult(4).getOrElse("")
+ val hasPrecision = !precisionStr.isEmpty
+ val precision =
+ if (hasPrecision) Integer.parseInt(precisionStr)
+ else 0
+
+ val conversion = matchResult(5).get.charAt(0)
+
+ def intArg: Int = (arg: Any) match {
+ case arg: Int => arg
+ case arg: Char => arg.toInt
+ }
+ def numberArg: scala.Double = (arg: Any) match {
+ case arg: Number => arg.doubleValue()
+ case arg: Char => arg.toDouble
+ }
+
+ def padCaptureSign(argStr: String, prefix: String) = {
+ val firstChar = argStr.charAt(0)
+ if (firstChar == '+' || firstChar == '-')
+ pad(argStr.substring(1), firstChar+prefix)
+ else
+ pad(argStr, prefix)
+ }
+
+ def strRepeat(s: String, times: Int) = {
+ var result: String = ""
+ var i = times
+ while (i > 0) {
+ result += s
+ i -= 1
+ }
+ result
+ }
+
+ def with_+(s: String, preventZero: scala.Boolean = false) = {
+ if (s.charAt(0) != '-') {
+ if (hasFlag("+"))
+ pad(s, "+", preventZero)
+ else if (hasFlag(" "))
+ pad(s, " ", preventZero)
+ else
+ pad(s, "", preventZero)
+ } else {
+ if (hasFlag("("))
+ pad(s.substring(1) + ")", "(", preventZero)
+ else
+ pad(s.substring(1), "-", preventZero)
+ }
+ }
+
+ def pad(argStr: String, prefix: String = "",
+ preventZero: Boolean = false) = {
+ val prePadLen = argStr.length + prefix.length
+
+ val padStr = {
+ if (width <= prePadLen) {
+ prefix + argStr
+ } else {
+ val padRight = hasFlag("-")
+ val padZero = hasFlag("0") && !preventZero
+ val padLength = width - prePadLen
+ val padChar: String = if (padZero) "0" else " "
+ val padding = strRepeat(padChar, padLength)
+
+ if (padZero && padRight)
+ throw new java.util.IllegalFormatFlagsException(flags)
+ else if (padRight) prefix + argStr + padding
+ else if (padZero) prefix + padding + argStr
+ else padding + prefix + argStr
+ }
+ }
+
+ val casedStr =
+ if (conversion.isUpper) padStr.toUpperCase()
+ else padStr
+ dest.append(casedStr)
+ }
+
+ (conversion: @switch) match {
+ case 'b' | 'B' => pad { arg match {
+ case null => "false"
+ case b: Boolean => String.valueOf(b)
+ case _ => "true"
+ } }
+ case 'h' | 'H' => pad {
+ if (arg eq null) "null"
+ else Integer.toHexString(arg.hashCode)
+ }
+ case 's' | 'S' => arg match {
+ case null if !hasFlag("#") => pad("null")
+ case formattable: Formattable =>
+ val flags = (
+ (if (hasFlag("-")) FormattableFlags.LEFT_JUSTIFY else 0) |
+ (if (hasFlag("#")) FormattableFlags.ALTERNATE else 0) |
+ (if (conversion.isUpper) FormattableFlags.UPPERCASE else 0)
+ )
+
+ formattable.formatTo(this, flags,
+ if (hasWidth) width.toInt else -1,
+ if (hasPrecision) precision.toInt else -1)
+ None // no further processing
+ case t: AnyRef if !hasFlag("#") => pad(t.toString)
+ case _ =>
+ throw new FormatFlagsConversionMismatchException("#", 's')
+ }
+ case 'c' | 'C' =>
+ pad(js.String.fromCharCode(intArg))
+ case 'd' =>
+ with_+(numberArg.toString())
+ case 'o' =>
+ val str = (arg: Any) match {
+ case arg: scala.Int => Integer.toOctalString(arg)
+ case arg: scala.Long => Long.toOctalString(arg)
+ }
+ padCaptureSign(str, if (hasFlag("#")) "0" else "")
+ case 'x' | 'X' =>
+ val str = (arg: Any) match {
+ case arg: scala.Int => Integer.toHexString(arg)
+ case arg: scala.Long => Long.toHexString(arg)
+ }
+ padCaptureSign(str, if (hasFlag("#")) "0x" else "")
+ case 'e' | 'E' =>
+ sciNotation(if (hasPrecision) precision else 6)
+ case 'g' | 'G' =>
+ val m = Math.abs(numberArg)
+ // precision handling according to JavaDoc
+ // precision here means number of significant digits
+ // not digits after decimal point
+ val p =
+ if (!hasPrecision) 6
+ else if (precision == 0) 1
+ else precision
+ // between 1e-4 and 10e(p): display as fixed
+ if (m >= 1e-4 && m < Math.pow(10, p)) {
+ val sig = Math.ceil(Math.log10(m))
+ with_+(numberArg.toFixed(Math.max(p - sig, 0)))
+ } else sciNotation(p - 1)
+ case 'f' =>
+ with_+({
+ // JavaDoc: 6 is default precision
+ numberArg.toFixed(if (hasPrecision) precision else 6)
+ }, numberArg.isNaN || numberArg.isInfinite)
+ }
+
+ def sciNotation(precision: Int) = {
+ val exp = numberArg.toExponential(precision)
+ with_+({
+ // check if we need additional 0 padding in exponent
+ // JavaDoc: at least 2 digits
+ if ("e" == exp.charAt(exp.length - 3)) {
+ exp.substring(0, exp.length - 1) + "0" +
+ exp.charAt(exp.length - 1)
+ } else exp
+ }, numberArg.isNaN || numberArg.isInfinite)
+ }
+ }
+ }
+
+ this
+ }
+
+ def ioException(): IOException = null
+ def locale(): Locale = ifNotClosed { null }
+ def out(): Appendable = ifNotClosed { dest }
+
+ override def toString(): String = out().toString()
+
+ @inline private def ifNotClosed[T](body: => T): T =
+ if (closed) throwClosedException()
+ else body
+
+ private def throwClosedException(): Nothing =
+ throw new FormatterClosedException()
+
+}
+
+object Formatter {
+
+ private class RegExpExtractor(val regexp: js.RegExp) {
+ def unapply(str: String): Option[js.RegExp.ExecResult] = {
+ Option(regexp.exec(str))
+ }
+ }
+
+ private val RegularChunk = new RegExpExtractor(new js.RegExp("""^[^\x25]+"""))
+ private val DoublePercent = new RegExpExtractor(new js.RegExp("""^\x25{2}"""))
+ private val EOLChunk = new RegExpExtractor(new js.RegExp("""^\x25n"""))
+ private val FormattedChunk = new RegExpExtractor(new js.RegExp(
+ """^\x25(?:([1-9]\d*)\$)?([-#+ 0,\(<]*)(\d*)(?:\.(\d+))?([A-Za-z])"""))
+
+}
diff --git a/javalib/src/main/scala/java/util/Random.scala b/javalib/src/main/scala/java/util/Random.scala
new file mode 100644
index 0000000..80428fb
--- /dev/null
+++ b/javalib/src/main/scala/java/util/Random.scala
@@ -0,0 +1,119 @@
+package java.util
+
+import scala.annotation.tailrec
+
+import scala.scalajs.js
+
+class Random(seed_in: Long) extends AnyRef with java.io.Serializable {
+
+ private var seed: Long = _
+
+ // see nextGaussian()
+ private var nextNextGaussian: Double = _
+ private var haveNextNextGaussian: Boolean = false
+
+ setSeed(seed_in)
+
+ def this() = this(Random.randomSeed())
+
+ def setSeed(seed_in: Long): Unit = {
+ seed = (seed_in ^ 0x5DEECE66DL) & ((1L << 48) - 1)
+ haveNextNextGaussian = false
+ }
+
+ protected def next(bits: Int): Int = {
+ seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)
+ (seed >>> (48 - bits)).toInt
+ }
+
+ def nextDouble(): Double =
+ ((next(26).toLong << 27) + next(27)) / (1L << 53).toDouble
+
+ def nextBoolean(): Boolean = next(1) != 0
+
+ def nextInt(): Int = next(32)
+
+ def nextInt(n: Int): Int = {
+ if (n <= 0)
+ throw new IllegalArgumentException("n must be positive");
+
+ if ((n & -n) == n) // i.e., n is a power of 2
+ ((n * next(31).toLong) >> 31).toInt
+ else {
+ @tailrec
+ def loop(): Int = {
+ val bits = next(31)
+ val value = bits % n
+ if (bits - value + (n-1) < 0) loop()
+ else value
+ }
+
+ loop()
+ }
+ }
+
+ def nextLong(): Long = (next(32).toLong << 32) + next(32)
+
+ def nextFloat(): Float = next(24) / (1 << 24).toFloat
+
+ def nextBytes(bytes: Array[Byte]): Unit = {
+ var i = 0
+ while (i < bytes.length) {
+ var rnd = nextInt()
+ var n = Math.min(bytes.length - i, 4)
+ while (n > 0) {
+ bytes(i) = rnd.toByte
+ rnd >>= 8
+ n -= 1
+ i += 1
+ }
+ }
+ }
+
+ def nextGaussian(): Double = {
+ // See http://www.protonfish.com/jslib/boxmuller.shtml
+
+ /* The Box-Muller algorithm produces two random numbers at once. We save
+ * the second one in `nextNextGaussian` to be used by the next call to
+ * nextGaussian().
+ */
+
+ if (haveNextNextGaussian) {
+ haveNextNextGaussian = false
+ return nextNextGaussian
+ }
+
+ var x, y, rds: Double = 0
+
+ /* Get two random numbers from -1 to 1.
+ * If the radius is zero or greater than 1, throw them out and pick two new
+ * ones.
+ * Rejection sampling throws away about 20% of the pairs.
+ */
+ do {
+ x = nextDouble()*2-1
+ y = nextDouble()*2-1
+ rds = x*x + y*y
+ } while (rds == 0 || rds > 1)
+
+ val c = Math.sqrt(-2 * Math.log(rds) / rds)
+
+ // Save y*c for next time
+ nextNextGaussian = y*c
+ haveNextNextGaussian = true
+
+ // And return x*c
+ x*c
+ }
+}
+
+object Random {
+
+ /** Generate a random long from JS RNG to seed a new Random */
+ private def randomSeed(): Long =
+ (randomInt().toLong << 32) | (randomInt().toLong & 0xffffffffL)
+
+ private def randomInt(): Int =
+ (Math.floor(js.Math.random() * 4294967296.0) - 2147483648.0).toInt
+
+}
diff --git a/javalib/src/main/scala/java/util/Throwables.scala b/javalib/src/main/scala/java/util/Throwables.scala
new file mode 100644
index 0000000..c4bb3d6
--- /dev/null
+++ b/javalib/src/main/scala/java/util/Throwables.scala
@@ -0,0 +1,166 @@
+package java.util
+
+class ServiceConfigurationError(s: String, e: Throwable) extends Error(s, e) {
+ def this(s: String) = this(s, null)
+}
+
+class ConcurrentModificationException(s: String) extends RuntimeException(s) {
+ def this() = this(null)
+}
+
+class DuplicateFormatFlagsException private() extends IllegalFormatException {
+ private var flags: String = null
+ def this(f: String) {
+ this()
+ if (f == null)
+ throw new NullPointerException()
+ flags = f
+ }
+ def getFlags(): String = flags
+ override def getMessage(): String = s"Flags = '$flags'"
+}
+
+class EmptyStackException extends RuntimeException
+
+class FormatFlagsConversionMismatchException private(private val c: Char) extends IllegalFormatException {
+ private var f: String = null
+ def this(f: String, c: Char) {
+ this(c)
+ if (f == null)
+ throw new NullPointerException()
+ this.f = f
+ }
+ def getFlags(): String = f
+ def getConversion(): Char = c
+ override def getMessage(): String = "Conversion = " + c + ", Flags = " + f
+}
+
+class FormatterClosedException extends IllegalStateException
+
+class IllegalFormatCodePointException(private val c: Int) extends IllegalFormatException {
+ def getCodePoint(): Int = c
+ override def getMessage(): String = s"Code point = $c"
+}
+
+class IllegalFormatConversionException private(private val c: Char) extends IllegalFormatException {
+ private var arg: Class[_] = null
+ def this(c: Char, arg: Class[_]) {
+ this(c)
+ if (arg == null)
+ throw new NullPointerException()
+ this.arg = arg
+ }
+ def getConversion(): Char = c
+ def getArgumentClass(): Class[_] = arg
+ override def getMessage(): String = s"$c != ${arg.getName()}"
+}
+
+class IllegalFormatException private[util] () extends IllegalArgumentException
+
+class IllegalFormatFlagsException private() extends IllegalFormatException {
+ private var flags: String = null
+ def this(f: String) {
+ this()
+ if (f == null)
+ throw new NullPointerException()
+ this.flags = f
+ }
+ def getFlags(): String = flags
+ override def getMessage(): String = "Flags = '" + flags + "'"
+}
+
+class IllegalFormatPrecisionException(private val p: Int) extends IllegalFormatException {
+ def getPrecision(): Int = p
+ override def getMessage(): String = Integer.toString(p)
+}
+
+class IllegalFormatWidthException(private val w: Int) extends IllegalFormatException {
+ def getWidth(): Int = w
+ override def getMessage(): String = Integer.toString(w)
+}
+
+class IllformedLocaleException(s: String, errorIndex: Int)
+ extends RuntimeException(s + (if(errorIndex < 0) "" else " [at index " + errorIndex + "]")) {
+ def this() = this(null, -1)
+ def this(s: String) = this(s, -1)
+ def getErrorIndex(): Int = errorIndex
+}
+
+class InputMismatchException(s: String) extends NoSuchElementException(s) {
+ def this() = this(null)
+}
+
+class InvalidPropertiesFormatException(s: String) extends java.io.IOException(s) {
+ def this(e: Throwable) {
+ this(if(e == null) null.asInstanceOf[String] else e.toString())
+ this.initCause(e)
+ }
+ // private def writeObject(out: java.io.ObjectOutputStream) =
+ // throw new java.io.NotSerializableException("Not serializable.")
+ // private def readObject(in: java.io.ObjectInputStream) =
+ // throw new java.io.NotSerializableException("Not serializable.")
+}
+
+class MissingFormatArgumentException private() extends IllegalFormatException {
+ private var s: String = null
+ def this(s: String) {
+ this()
+ if (s == null)
+ throw new NullPointerException()
+ this.s = s
+ }
+ def getFormatSpecifier(): String = s
+ override def getMessage(): String = "Format specifier '" + s + "'"
+}
+
+class MissingFormatWidthException private() extends IllegalFormatException {
+ private var s: String = null
+ def this(s: String) {
+ this()
+ if (s == null)
+ throw new NullPointerException()
+ this.s = s
+ }
+ def getFormatSpecifier(): String = s
+ override def getMessage(): String = s
+}
+
+class MissingResourceException private[util](
+ s: String, private var className: String, private var key: String, e: Throwable)
+ extends RuntimeException(s, e) {
+ def this(s: String, className: String, key: String) = this(s, className, key, null)
+ def getClassName(): String = className
+ def getKey(): String = key
+}
+
+class NoSuchElementException(s: String) extends RuntimeException(s) {
+ def this() = this(null)
+}
+
+class TooManyListenersException(s: String) extends Exception(s) {
+ def this() = this(null)
+}
+
+class UnknownFormatConversionException private () extends IllegalFormatException {
+ private var s: String = null
+ def this(s: String) {
+ this()
+ if (s == null)
+ throw new NullPointerException()
+ this.s = s
+ }
+ def getConversion(): String = s
+ override def getMessage(): String = s"Conversion = '$s'"
+}
+
+class UnknownFormatFlagsException private() extends IllegalFormatException {
+ private var flags: String = null
+ def this(f: String) {
+ this()
+ if (f == null)
+ throw new NullPointerException()
+ this.flags = f
+ }
+ def getFlags(): String = flags
+ override def getMessage(): String = "Flags = " + flags
+}
diff --git a/javalib/src/main/scala/java/util/UUID.scala b/javalib/src/main/scala/java/util/UUID.scala
new file mode 100644
index 0000000..9e6c1d4
--- /dev/null
+++ b/javalib/src/main/scala/java/util/UUID.scala
@@ -0,0 +1,163 @@
+package java.util
+
+import java.lang.{Long => JLong}
+
+import scala.scalajs.js
+
+final class UUID private (
+ private val i1: Int, private val i2: Int,
+ private val i3: Int, private val i4: Int,
+ private[this] var l1: JLong, private[this] var l2: JLong)
+ extends AnyRef with java.io.Serializable with Comparable[UUID] {
+
+ import UUID._
+
+ /* Most significant long:
+ *
+ * 0xFFFFFFFF00000000 time_low
+ * 0x00000000FFFF0000 time_mid
+ * 0x000000000000F000 version
+ * 0x0000000000000FFF time_hi
+ *
+ * Least significant long:
+ *
+ * 0xC000000000000000 variant
+ * 0x3FFF000000000000 clock_seq
+ * 0x0000FFFFFFFFFFFF node
+ */
+
+ def this(mostSigBits: Long, leastSigBits: Long) = {
+ this((mostSigBits >>> 32).toInt, mostSigBits.toInt,
+ (leastSigBits >>> 32).toInt, leastSigBits.toInt,
+ mostSigBits, leastSigBits)
+ }
+
+ def getLeastSignificantBits(): Long = {
+ if (l2 eq null)
+ l2 = (i3.toLong << 32) | (i4.toLong & 0xffffffffL)
+ l2.longValue
+ }
+
+ def getMostSignificantBits(): Long = {
+ if (l1 eq null)
+ l1 = (i1.toLong << 32) | (i2.toLong & 0xffffffffL)
+ l1.longValue
+ }
+
+ def version(): Int =
+ (i2 & 0xf000) >> 12
+
+ def variant(): Int = {
+ if ((i3 & 0x80000000) == 0) {
+ // MSB0 not set: NCS backwards compatibility variant
+ 0
+ } else if ((i3 & 0x40000000) != 0) {
+ // MSB1 set: either MS reserved or future reserved
+ (i3 & 0xe0000000) >>> 29
+ } else {
+ // MSB1 not set: RFC 4122 variant
+ 2
+ }
+ }
+
+ def timestamp(): Long = {
+ if (version() != TimeBased)
+ throw new UnsupportedOperationException("Not a time-based UUID")
+ (((i2 >>> 16) | ((i2 & 0x0fff) << 16)).toLong << 32) | (i1.toLong & 0xffffffffL)
+ }
+
+ def clockSequence(): Int = {
+ if (version() != TimeBased)
+ throw new UnsupportedOperationException("Not a time-based UUID")
+ (i3 & 0x3fff0000) >> 16
+ }
+
+ def node(): Long = {
+ if (version() != TimeBased)
+ throw new UnsupportedOperationException("Not a time-based UUID")
+ ((i3 & 0xffff).toLong << 32) | (i4.toLong & 0xffffffffL)
+ }
+
+ override def toString(): String = {
+ @inline def paddedHex8(i: Int): String = {
+ val s = Integer.toHexString(i)
+ "00000000".substring(s.length) + s
+ }
+
+ @inline def paddedHex4(i: Int): String = {
+ val s = Integer.toHexString(i)
+ "0000".substring(s.length) + s
+ }
+
+ paddedHex8(i1) + "-" + paddedHex4(i2 >>> 16) + "-" + paddedHex4(i2 & 0xffff) + "-" +
+ paddedHex4(i3 >>> 16) + "-" + paddedHex4(i3 & 0xffff) + paddedHex8(i4)
+ }
+
+ override def hashCode(): Int =
+ i1 ^ i2 ^ i3 ^ i4
+
+ override def equals(that: Any): Boolean = that match {
+ case that: UUID =>
+ i1 == that.i1 && i2 == that.i2 && i3 == that.i3 && i4 == that.i4
+ case _ =>
+ false
+ }
+
+ def compareTo(that: UUID): Int = {
+ if (this.i1 != that.i1) {
+ if (this.i1 > that.i1) 1 else -1
+ } else if (this.i2 != that.i2) {
+ if (this.i2 > that.i2) 1 else -1
+ } else if (this.i3 != that.i3) {
+ if (this.i3 > that.i3) 1 else -1
+ } else if (this.i4 != that.i4) {
+ if (this.i4 > that.i4) 1 else -1
+ } else {
+ 0
+ }
+ }
+}
+
+object UUID {
+ private final val TimeBased = 1
+ private final val DCESecurity = 2
+ private final val NameBased = 3
+ private final val Random = 4
+
+ private lazy val rng = new Random() // TODO Use java.security.SecureRandom
+
+ def randomUUID(): UUID = {
+ val i1 = rng.nextInt()
+ val i2 = (rng.nextInt() & ~0x0000f000) | 0x00004000
+ val i3 = (rng.nextInt() & ~0xc0000000) | 0x80000000
+ val i4 = rng.nextInt()
+ new UUID(i1, i2, i3, i4, null, null)
+ }
+
+ // Not implemented (requires messing with MD5 or SHA-1):
+ //def nameUUIDFromBytes(name: Array[Byte]): UUID = ???
+
+ def fromString(name: String): UUID = {
+ import Integer.parseInt
+
+ def fail(): Nothing =
+ throw new IllegalArgumentException("Illegal UUID string: "+name)
+
+ @inline def parseHex8(his: String, los: String): Int =
+ (parseInt(his, 16) << 16) | parseInt(los, 16)
+
+ if (name.length != 36 || name.charAt(8) != '-' ||
+ name.charAt(13) != '-' || name.charAt(18) != '-' || name.charAt(23) != '-')
+ fail()
+
+ try {
+ val i1 = parseHex8(name.substring(0, 4), name.substring(4, 8))
+ val i2 = parseHex8(name.substring(9, 13), name.substring(14, 18))
+ val i3 = parseHex8(name.substring(19, 23), name.substring(24, 28))
+ val i4 = parseHex8(name.substring(28, 32), name.substring(32, 36))
+ new UUID(i1, i2, i3, i4, null, null)
+ } catch {
+ case _: NumberFormatException => fail()
+ }
+ }
+}
diff --git a/javalib/src/main/scala/java/util/concurrent/ExecutionException.scala b/javalib/src/main/scala/java/util/concurrent/ExecutionException.scala
new file mode 100644
index 0000000..6d04889
--- /dev/null
+++ b/javalib/src/main/scala/java/util/concurrent/ExecutionException.scala
@@ -0,0 +1,9 @@
+package java.util.concurrent
+
+class ExecutionException(message: String, cause: Throwable)
+ extends Exception(message, cause) {
+
+ protected def this() = this(null, null)
+ protected def this(message: String) = this(message, null)
+ def this(cause: Throwable) = this(null, cause)
+}
diff --git a/javalib/src/main/scala/java/util/concurrent/Executor.scala b/javalib/src/main/scala/java/util/concurrent/Executor.scala
new file mode 100644
index 0000000..d030551
--- /dev/null
+++ b/javalib/src/main/scala/java/util/concurrent/Executor.scala
@@ -0,0 +1,5 @@
+package java.util.concurrent
+
+trait Executor {
+ def execute(command: Runnable): Unit
+}
diff --git a/javalib/src/main/scala/java/util/concurrent/TimeUnit.scala b/javalib/src/main/scala/java/util/concurrent/TimeUnit.scala
new file mode 100644
index 0000000..a77dbfc
--- /dev/null
+++ b/javalib/src/main/scala/java/util/concurrent/TimeUnit.scala
@@ -0,0 +1,133 @@
+package java.util.concurrent
+
+abstract class TimeUnit private (_index: Int,
+ _name: String) extends java.io.Serializable {
+
+ def convert(a: Long, u: TimeUnit): Long
+
+ def toNanos(a: Long): Long
+ def toMicros(a: Long): Long
+ def toMillis(a: Long): Long
+ def toSeconds(a: Long): Long
+ def toMinutes(a: Long): Long
+ def toHours(a: Long): Long
+ def toDays(a: Long): Long
+
+ // not used
+ //private[concurrent] def excessNanos(a: Long, b: Long): Int
+
+ def name(): String = _name
+ def ordinal(): Int = _index
+
+ // methods that cannot be implemented
+ //def timedWait(arg1: AnyRef, arg2: Long): Unit
+ //def timedJoin(arg1: Thread, arg2: Long): Unit
+ //def sleep(arg1: Long): Unit
+
+ override def toString() = name()
+}
+
+object TimeUnit {
+ final val NANOSECONDS: TimeUnit = new TimeUnit(0, "NANOSECONDS") {
+ def convert(a: Long, u: TimeUnit): Long = u.toNanos(a)
+ def toNanos(a: Long): Long = a
+ def toMicros(a: Long): Long = a / (C1/C0)
+ def toMillis(a: Long): Long = a / (C2/C0)
+ def toSeconds(a: Long): Long = a / (C3/C0)
+ def toMinutes(a: Long): Long = a / (C4/C0)
+ def toHours(a: Long): Long = a / (C5/C0)
+ def toDays(a: Long): Long = a / (C6/C0)
+ }
+
+ final val MICROSECONDS: TimeUnit = new TimeUnit(1, "MICROSECONDS") {
+ def convert(a: Long, u: TimeUnit): Long = u.toMicros(a)
+ def toNanos(a: Long): Long = x(a, C1/C0, MAX/(C1/C0))
+ def toMicros(a: Long): Long = a
+ def toMillis(a: Long): Long = a / (C2/C1)
+ def toSeconds(a: Long): Long = a / (C3/C1)
+ def toMinutes(a: Long): Long = a / (C4/C1)
+ def toHours(a: Long): Long = a / (C5/C1)
+ def toDays(a: Long): Long = a / (C6/C1)
+ }
+
+ final val MILLISECONDS: TimeUnit = new TimeUnit(2, "MILLISECONDS") {
+ def convert(a: Long, u: TimeUnit): Long = u.toMillis(a)
+ def toNanos(a: Long): Long = x(a, C2/C0, MAX/(C2/C0))
+ def toMicros(a: Long): Long = x(a, C2/C1, MAX/(C2/C1))
+ def toMillis(a: Long): Long = a
+ def toSeconds(a: Long): Long = a / (C3/C2)
+ def toMinutes(a: Long): Long = a / (C4/C2)
+ def toHours(a: Long): Long = a / (C5/C2)
+ def toDays(a: Long): Long = a / (C6/C2)
+ }
+
+ final val SECONDS: TimeUnit = new TimeUnit(3, "SECONDS") {
+ def convert(a: Long, u: TimeUnit): Long = u.toSeconds(a)
+ def toNanos(a: Long): Long = x(a, C3/C0, MAX/(C3/C0))
+ def toMicros(a: Long): Long = x(a, C3/C1, MAX/(C3/C1))
+ def toMillis(a: Long): Long = x(a, C3/C2, MAX/(C3/C2))
+ def toSeconds(a: Long): Long = a
+ def toMinutes(a: Long): Long = a / (C4/C3)
+ def toHours(a: Long): Long = a / (C5/C3)
+ def toDays(a: Long): Long = a / (C6/C3)
+ }
+
+ final val MINUTES: TimeUnit = new TimeUnit(4, "MINUTES") {
+ def convert(a: Long, u: TimeUnit): Long = u.toMinutes(a)
+ def toNanos(a: Long): Long = x(a, C4/C0, MAX/(C4/C0))
+ def toMicros(a: Long): Long = x(a, C4/C1, MAX/(C4/C1))
+ def toMillis(a: Long): Long = x(a, C4/C2, MAX/(C4/C2))
+ def toSeconds(a: Long): Long = x(a, C4/C3, MAX/(C4/C3))
+ def toMinutes(a: Long): Long = a
+ def toHours(a: Long): Long = a / (C5/C4)
+ def toDays(a: Long): Long = a / (C6/C4)
+ }
+
+ final val HOURS: TimeUnit = new TimeUnit(5, "HOURS") {
+ def convert(a: Long, u: TimeUnit): Long = u.toHours(a)
+ def toNanos(a: Long): Long = x(a, C5/C0, MAX/(C5/C0))
+ def toMicros(a: Long): Long = x(a, C5/C1, MAX/(C5/C1))
+ def toMillis(a: Long): Long = x(a, C5/C2, MAX/(C5/C2))
+ def toSeconds(a: Long): Long = x(a, C5/C3, MAX/(C5/C3))
+ def toMinutes(a: Long): Long = x(a, C5/C4, MAX/(C5/C4))
+ def toHours(a: Long): Long = a
+ def toDays(a: Long): Long = a / (C6/C5)
+ }
+
+ final val DAYS: TimeUnit = new TimeUnit(6, "DAYS") {
+ def convert(a: Long, u: TimeUnit): Long = u.toDays(a)
+ def toNanos(a: Long): Long = x(a, C6/C0, MAX/(C6/C0))
+ def toMicros(a: Long): Long = x(a, C6/C1, MAX/(C6/C1))
+ def toMillis(a: Long): Long = x(a, C6/C2, MAX/(C6/C2))
+ def toSeconds(a: Long): Long = x(a, C6/C3, MAX/(C6/C3))
+ def toMinutes(a: Long): Long = x(a, C6/C4, MAX/(C6/C4))
+ def toHours(a: Long): Long = x(a, C6/C5, MAX/(C6/C5))
+ def toDays(a: Long): Long = a
+ }
+
+ private[this] val _values: Array[TimeUnit] =
+ Array(NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS)
+
+ // deliberately without type ascription to make them compile-time constants
+ private final val C0 = 1L
+ private final val C1 = C0 * 1000L
+ private final val C2 = C1 * 1000L
+ private final val C3 = C2 * 1000L
+ private final val C4 = C3 * 60L
+ private final val C5 = C4 * 60L
+ private final val C6 = C5 * 24L
+ private final val MAX = Long.MaxValue
+
+ def values(): Array[TimeUnit] = _values.clone()
+
+ def valueOf(name: String): TimeUnit = {
+ _values.find(_.name == name).getOrElse(
+ throw new IllegalArgumentException("No enum const TimeUnit." + name))
+ }
+
+ private def x(a: Long, b: Long, max: Long): Long = {
+ if (a > max) MAX
+ else if (a < -max) -MAX
+ else a * b
+ }
+}
diff --git a/javalib/src/main/scala/java/util/concurrent/atomic/AtomicBoolean.scala b/javalib/src/main/scala/java/util/concurrent/atomic/AtomicBoolean.scala
new file mode 100644
index 0000000..5675c31
--- /dev/null
+++ b/javalib/src/main/scala/java/util/concurrent/atomic/AtomicBoolean.scala
@@ -0,0 +1,33 @@
+package java.util.concurrent.atomic
+
+class AtomicBoolean(private[this] var value: Boolean) extends Serializable {
+ def this() = this(false)
+
+ final def get(): Boolean = value
+
+ final def compareAndSet(expect: Boolean, update: Boolean): Boolean = {
+ if (expect != value) false else {
+ value = update
+ true
+ }
+ }
+
+ // For some reason, this method is not final
+ def weakCompareAndSet(expect: Boolean, update: Boolean): Boolean =
+ compareAndSet(expect, update)
+
+ final def set(newValue: Boolean): Unit =
+ value = newValue
+
+ final def lazySet(newValue: Boolean): Unit =
+ set(newValue)
+
+ final def getAndSet(newValue: Boolean): Boolean = {
+ val old = value
+ value = newValue
+ old
+ }
+
+ override def toString(): String =
+ value.toString()
+}
diff --git a/javalib/src/main/scala/java/util/concurrent/atomic/AtomicInteger.scala b/javalib/src/main/scala/java/util/concurrent/atomic/AtomicInteger.scala
new file mode 100644
index 0000000..1f24b7b
--- /dev/null
+++ b/javalib/src/main/scala/java/util/concurrent/atomic/AtomicInteger.scala
@@ -0,0 +1,63 @@
+package java.util.concurrent.atomic
+
+class AtomicInteger(private[this] var value: Int)
+ extends Number with Serializable {
+
+ def this() = this(0)
+
+ final def get(): Int = value
+
+ final def set(newValue: Int): Unit =
+ value = newValue
+
+ final def lazySet(newValue: Int): Unit =
+ set(newValue)
+
+ final def getAndSet(newValue: Int): Int = {
+ val old = value
+ value = newValue
+ old
+ }
+
+ final def compareAndSet(expect: Int, update: Int): Boolean = {
+ if (expect != value) false else {
+ value = update
+ true
+ }
+ }
+
+ final def weakCompareAndSet(expect: Int, update: Int): Boolean =
+ compareAndSet(expect, update)
+
+ final def getAndIncrement(): Int =
+ getAndAdd(1)
+
+ final def getAndDecrement(): Int =
+ getAndAdd(-1)
+
+ @inline final def getAndAdd(delta: Int): Int = {
+ val old = value
+ value = old + delta
+ old
+ }
+
+ final def incrementAndGet(): Int =
+ addAndGet(1)
+
+ final def decrementAndGet(): Int =
+ addAndGet(-1)
+
+ @inline final def addAndGet(delta: Int): Int = {
+ val newValue = value + delta
+ value = newValue
+ newValue
+ }
+
+ override def toString(): String =
+ value.toString()
+
+ def intValue(): Int = value
+ def longValue(): Long = value.toLong
+ def floatValue(): Float = value.toFloat
+ def doubleValue(): Double = value.toDouble
+}
diff --git a/javalib/src/main/scala/java/util/concurrent/atomic/AtomicLong.scala b/javalib/src/main/scala/java/util/concurrent/atomic/AtomicLong.scala
new file mode 100644
index 0000000..5bfecf2
--- /dev/null
+++ b/javalib/src/main/scala/java/util/concurrent/atomic/AtomicLong.scala
@@ -0,0 +1,61 @@
+package java.util.concurrent.atomic
+
+class AtomicLong(private[this] var value: Long) extends Number with Serializable {
+ def this() = this(0L)
+
+ final def get(): Long = value
+
+ final def set(newValue: Long): Unit =
+ value = newValue
+
+ final def lazySet(newValue: Long): Unit =
+ set(newValue)
+
+ final def getAndSet(newValue: Long): Long = {
+ val old = value
+ value = newValue
+ old
+ }
+
+ final def compareAndSet(expect: Long, update: Long): Boolean = {
+ if (expect != value) false else {
+ value = update
+ true
+ }
+ }
+
+ final def weakCompareAndSet(expect: Long, update: Long): Boolean =
+ compareAndSet(expect, update)
+
+ final def getAndIncrement(): Long =
+ getAndAdd(1L)
+
+ final def getAndDecrement(): Long =
+ getAndAdd(-1L)
+
+ @inline final def getAndAdd(delta: Long): Long = {
+ val old = value
+ value = old + delta
+ old
+ }
+
+ final def incrementAndGet(): Long =
+ addAndGet(1L)
+
+ final def decrementAndGet(): Long =
+ addAndGet(-1L)
+
+ @inline final def addAndGet(delta: Long): Long = {
+ val newValue = value + delta
+ value = newValue
+ newValue
+ }
+
+ override def toString(): String =
+ value.toString()
+
+ def intValue(): Int = value.toInt
+ def longValue(): Long = value
+ def floatValue(): Float = value.toFloat
+ def doubleValue(): Double = value.toDouble
+}
diff --git a/javalib/src/main/scala/java/util/concurrent/atomic/AtomicReference.scala b/javalib/src/main/scala/java/util/concurrent/atomic/AtomicReference.scala
new file mode 100644
index 0000000..650b1e0
--- /dev/null
+++ b/javalib/src/main/scala/java/util/concurrent/atomic/AtomicReference.scala
@@ -0,0 +1,34 @@
+package java.util.concurrent.atomic
+
+class AtomicReference[T <: AnyRef](
+ private[this] var value: T) extends Serializable {
+
+ def this() = this(null.asInstanceOf[T])
+
+ final def get(): T = value
+
+ final def set(newValue: T): Unit =
+ value = newValue
+
+ final def lazySet(newValue: T): Unit =
+ set(newValue)
+
+ final def compareAndSet(expect: T, update: T): Boolean = {
+ if (expect ne value) false else {
+ value = update
+ true
+ }
+ }
+
+ final def weakCompareAndSet(expect: T, update: T): Boolean =
+ compareAndSet(expect, update)
+
+ final def getAndSet(newValue: T): T = {
+ val old = value
+ value = newValue
+ old
+ }
+
+ override def toString(): String =
+ String.valueOf(value)
+}
diff --git a/javalib/src/main/scala/java/util/regex/MatchResult.scala b/javalib/src/main/scala/java/util/regex/MatchResult.scala
new file mode 100644
index 0000000..f321c60
--- /dev/null
+++ b/javalib/src/main/scala/java/util/regex/MatchResult.scala
@@ -0,0 +1,13 @@
+package java.util.regex
+
+trait MatchResult {
+ def groupCount(): Int
+
+ def start(): Int
+ def end(): Int
+ def group(): String
+
+ def start(group: Int): Int
+ def end(group: Int): Int
+ def group(group: Int): String
+}
diff --git a/javalib/src/main/scala/java/util/regex/Matcher.scala b/javalib/src/main/scala/java/util/regex/Matcher.scala
new file mode 100644
index 0000000..331f56b
--- /dev/null
+++ b/javalib/src/main/scala/java/util/regex/Matcher.scala
@@ -0,0 +1,274 @@
+package java.util.regex
+
+import scala.language.implicitConversions
+
+import scala.annotation.switch
+
+import scala.scalajs.js
+
+final class Matcher private[regex] (
+ private var pattern0: Pattern, private var input0: CharSequence,
+ private var regionStart0: Int, private var regionEnd0: Int)
+ extends AnyRef with MatchResult {
+
+ import Matcher._
+
+ def pattern(): Pattern = pattern0
+
+ // Configuration (updated manually)
+ private var regexp = new js.RegExp(pattern0.jspattern, pattern0.jsflags)
+ private var inputstr = input0.subSequence(regionStart0, regionEnd0).toString
+
+ // Match result (updated by successful matches)
+ private var lastMatch: js.RegExp.ExecResult = null
+ private var lastMatchIsValid = false
+ private var canStillFind = true
+
+ // Append state (updated by replacement methods)
+ private var appendPos: Int = 0
+
+ // Lookup methods
+
+ def matches(): Boolean = {
+ reset()
+ find()
+ // TODO this check is wrong with non-greedy patterns
+ // Further, it might be wrong to just use ^$ delimiters for two reasons:
+ // - They might already be there
+ // - They might not behave as expected when newline characters are present
+ if ((lastMatch ne null) && (start != 0 || end != inputstr.length))
+ reset()
+ lastMatch ne null
+ }
+
+ def lookingAt(): Boolean = {
+ reset()
+ find()
+ if ((lastMatch ne null) && (start != 0))
+ reset()
+ lastMatch ne null
+ }
+
+ def find(): Boolean = if (canStillFind) {
+ lastMatchIsValid = true
+ lastMatch = regexp.exec(inputstr)
+ if (lastMatch ne null) {
+ if (lastMatch(0).get.isEmpty)
+ regexp.lastIndex += 1
+ } else {
+ canStillFind = false
+ }
+ lastMatch ne null
+ } else false
+
+ def find(start: Int): Boolean = {
+ reset()
+ regexp.lastIndex = start
+ find()
+ }
+
+ // Replace methods
+
+ def appendReplacement(sb: StringBuffer, replacement: String): Matcher = {
+ sb.append(inputstr.substring(appendPos, start))
+
+ @inline def isDigit(c: Char) = c >= '0' && c <= '9'
+
+ val len = replacement.length
+ var i = 0
+ while (i < len) {
+ replacement.charAt(i) match {
+ case '$' =>
+ i += 1
+ val j = i
+ while (i < len && isDigit(replacement.charAt(i)))
+ i += 1
+ val group = Integer.parseInt(replacement.substring(j, i))
+ sb.append(this.group(group))
+
+ case '\\' =>
+ i += 1
+ if (i < len)
+ sb.append(replacement.charAt(i))
+ i += 1
+
+ case c =>
+ sb.append(c)
+ i += 1
+ }
+ }
+
+ appendPos = end
+ this
+ }
+
+ def appendTail(sb: StringBuffer): StringBuffer = {
+ sb.append(inputstr.substring(appendPos))
+ appendPos = inputstr.length
+ sb
+ }
+
+ def replaceFirst(replacement: String): String = {
+ reset()
+
+ if (find()) {
+ val sb = new StringBuffer
+ appendReplacement(sb, replacement)
+ appendTail(sb)
+ sb.toString
+ } else {
+ inputstr
+ }
+ }
+
+ def replaceAll(replacement: String): String = {
+ reset()
+
+ val sb = new StringBuffer
+ while (find()) {
+ appendReplacement(sb, replacement)
+ }
+ appendTail(sb)
+
+ sb.toString
+ }
+
+ // Reset methods
+
+ def reset(): Matcher = {
+ regexp.lastIndex = 0
+ lastMatch = null
+ lastMatchIsValid = false
+ canStillFind = true
+ appendPos = 0
+ this
+ }
+
+ def reset(input: CharSequence): Matcher = {
+ regionStart0 = 0
+ regionEnd0 = input.length()
+ input0 = input
+ inputstr = input0.toString
+ reset()
+ }
+
+ def usePattern(pattern: Pattern): Matcher = {
+ val prevLastIndex = regexp.lastIndex
+ pattern0 = pattern
+ regexp = new js.RegExp(pattern.jspattern, pattern.jsflags)
+ regexp.lastIndex = prevLastIndex
+ lastMatch = null
+ this
+ }
+
+ // Query state methods - implementation of MatchResult
+
+ private def ensureLastMatch: js.RegExp.ExecResult = {
+ if (lastMatch == null)
+ throw new IllegalStateException("No match available")
+ lastMatch
+ }
+
+ def groupCount(): Int = ensureLastMatch.length-1
+
+ def start(): Int = ensureLastMatch.index
+ def end(): Int = start() + group().length
+ def group(): String = ensureLastMatch(0).get
+
+ def start(group: Int): Int = {
+ if (group == 0) start()
+ else {
+ val last = ensureLastMatch
+ // not provided by JS RegExp, so we make up something that at least
+ // will have some sound behavior from scala.util.matching.Regex
+ last(group).fold(-1) {
+ groupStr => inputstr.indexOf(groupStr, last.index)
+ }
+ }
+ }
+
+ def end(group: Int): Int = {
+ val s = start(group)
+ if (s == -1) -1
+ else s + this.group(group).length
+ }
+
+ def group(group: Int): String = ensureLastMatch(group).orNull
+
+ // Seal the state
+
+ def toMatchResult(): MatchResult = new SealedResult(inputstr, lastMatch)
+
+ // Other query state methods
+
+ def hitEnd(): Boolean =
+ lastMatchIsValid && (lastMatch == null || end() == inputstr.length)
+
+ //def requireEnd(): Boolean // I don't understand the spec
+
+ // Stub methods for region management
+
+ def regionStart(): Int = regionStart0
+ def regionEnd(): Int = regionEnd0
+ def region(start: Int, end: Int): Matcher =
+ new Matcher(pattern0, input0, start, end)
+
+ def hasTransparentBounds(): Boolean = false
+ //def useTransparentBounds(b: Boolean): Matcher
+
+ def hasAnchoringBounds(): Boolean = true
+ //def useAnchoringBounds(b: Boolean): Matcher
+}
+
+object Matcher {
+ def quoteReplacement(s: String): String = {
+ var result = ""
+ var i = 0
+ while (i < s.length) {
+ val c = s.charAt(i)
+ result += ((c: @switch) match {
+ case '\\' | '$' => "\\"+c
+ case _ => c
+ })
+ i += 1
+ }
+ result
+ }
+
+ private final class SealedResult(inputstr: String,
+ lastMatch: js.RegExp.ExecResult) extends MatchResult {
+
+ def groupCount(): Int = ensureLastMatch.length-1
+
+ def start(): Int = ensureLastMatch.index
+ def end(): Int = start() + group().length
+ def group(): String = ensureLastMatch(0).get
+
+ def start(group: Int): Int = {
+ if (group == 0) start()
+ else {
+ val last = ensureLastMatch
+
+ // not provided by JS RegExp, so we make up something that at least
+ // will have some sound behavior from scala.util.matching.Regex
+ last(group).fold(-1) {
+ groupStr => inputstr.indexOf(groupStr, last.index)
+ }
+ }
+ }
+
+ def end(group: Int): Int = {
+ val s = start(group)
+ if (s == -1) -1
+ else s + this.group(group).length
+ }
+
+ def group(group: Int): String = ensureLastMatch(group).orNull
+
+ private def ensureLastMatch: js.RegExp.ExecResult = {
+ if (lastMatch == null)
+ throw new IllegalStateException("No match available")
+ lastMatch
+ }
+ }
+}
diff --git a/javalib/src/main/scala/java/util/regex/Pattern.scala b/javalib/src/main/scala/java/util/regex/Pattern.scala
new file mode 100644
index 0000000..fda103f
--- /dev/null
+++ b/javalib/src/main/scala/java/util/regex/Pattern.scala
@@ -0,0 +1,154 @@
+package java.util.regex
+
+import scala.annotation.switch
+
+import scala.scalajs.js
+
+final class Pattern private (pattern0: String, flags0: Int)
+ extends Serializable {
+
+ import Pattern._
+
+ def pattern(): String = pattern0
+ def flags(): Int = flags1
+
+ private[regex] val (jspattern, flags1) = {
+ if ((flags0 & LITERAL) != 0) (quote(pattern0), flags0)
+ else {
+ trySplitHack(pattern0, flags0) orElse
+ tryFlagHack(pattern0, flags0) getOrElse
+ (pattern0, flags0)
+ }
+ }
+
+ private[regex] val jsflags = {
+ var f = "g"
+ if ((flags & CASE_INSENSITIVE) != 0)
+ f += "i"
+ if ((flags & MULTILINE) != 0)
+ f += "m"
+ f
+ }
+
+ override def toString(): String = pattern0
+
+ def matcher(input: CharSequence): Matcher =
+ new Matcher(this, input, 0, input.length)
+
+ def split(input: CharSequence): Array[String] =
+ split(input, 0)
+
+ def split(input: CharSequence, limit: Int): Array[String] = {
+ val lim = if (limit > 0) limit else Int.MaxValue
+
+ val result = js.Array[String]()
+ val inputStr = input.toString
+ val matcher = this.matcher(inputStr)
+ var prevEnd = 0
+
+ // Actually split original string
+ while ((result.length < lim-1) && matcher.find()) {
+ result.push(inputStr.substring(prevEnd, matcher.start))
+ prevEnd = matcher.end
+ }
+ result.push(inputStr.substring(prevEnd))
+
+ // Remove a leading empty element iff the first match was zero-length
+ // and there is no other place the regex matches
+ if (prevEnd == 0 && result.length == 2 && (lim > 2 || !matcher.find())) {
+ Array(inputStr)
+ } else {
+ var len = result.length
+ if (limit == 0) {
+ while (len > 1 && result(len-1).isEmpty)
+ len -= 1
+ }
+
+ val actualResult = new Array[String](len)
+ result.copyToArray(actualResult)
+ actualResult
+ }
+ }
+}
+
+object Pattern {
+ final val UNIX_LINES = 0x01
+ final val CASE_INSENSITIVE = 0x02
+ final val COMMENTS = 0x04
+ final val MULTILINE = 0x08
+ final val LITERAL = 0x10
+ final val DOTALL = 0x20
+ final val UNICODE_CASE = 0x40
+ final val CANON_EQ = 0x80
+ final val UNICODE_CHARACTER_CLASS = 0x100
+
+ def compile(regex: String, flags: Int): Pattern =
+ new Pattern(regex, flags)
+
+ def compile(regex: String): Pattern =
+ new Pattern(regex, 0)
+
+ def matches(regex: String, input: CharSequence): Boolean =
+ compile(regex).matcher(input).matches()
+
+ def quote(s: String): String = {
+ var result = ""
+ var i = 0
+ while (i < s.length) {
+ val c = s.charAt(i)
+ result += ((c: @switch) match {
+ case '\\' | '.' | '(' | ')' | '[' | ']' | '{' | '}' | '|'
+ | '?' | '*' | '+' | '^' | '$' => "\\"+c
+ case _ => c
+ })
+ i += 1
+ }
+ result
+ }
+
+ /** This is a hack to support StringLike.split().
+ * It replaces occurrences of \Q<char>\E by quoted(<char>)
+ */
+ @inline
+ private def trySplitHack(pat: String, flags: Int) = {
+ val m = splitHackPat.exec(pat)
+ if (m != null)
+ Some((quote(m(1).get), flags))
+ else
+ None
+ }
+
+ @inline
+ private def tryFlagHack(pat: String, flags0: Int) = {
+ val m = flagHackPat.exec(pat)
+ if (m != null) {
+ val newPat = pat.substring(m(0).get.length) // cut off the flag specifiers
+ val flags1 = m(1).fold(flags0) { chars =>
+ chars.foldLeft(flags0) { (f, c) => f | charToFlag(c) }
+ }
+ val flags2 = m(2).fold(flags1) { chars =>
+ chars.foldLeft(flags1) { (f, c) => f & ~charToFlag(c) }
+ }
+ Some((newPat, flags2))
+ } else
+ None
+ }
+
+ private def charToFlag(c: Char) = (c: @switch) match {
+ case 'i' => CASE_INSENSITIVE
+ case 'd' => UNIX_LINES
+ case 'm' => MULTILINE
+ case 's' => DOTALL
+ case 'u' => UNICODE_CASE
+ case 'x' => COMMENTS
+ case 'U' => UNICODE_CHARACTER_CLASS
+ case _ => sys.error("bad in-pattern flag")
+ }
+
+ /** matches \Q<char>\E to support StringLike.split */
+ private val splitHackPat = new js.RegExp("^\\\\Q(.|\\n|\\r)\\\\E$")
+
+ /** regex to match flag specifiers in regex. E.g. (?u), (?-i), (?U-i) */
+ private val flagHackPat =
+ new js.RegExp("^\\(\\?([idmsuxU]*)(?:-([idmsuxU]*))?\\)")
+}