Skip to content

Implement most of ZoneMap #90

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 4, 2018
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
127 changes: 94 additions & 33 deletions core/shared/src/main/scala/org/threeten/bp/zone/ZoneMap.scala
Original file line number Diff line number Diff line change
@@ -1,67 +1,122 @@
package org.threeten.bp.zone

import java.util
import java.util.{Collection => JCollection, Map => JMap, Set => JSet}
import java.util.{Collection => JCollection, Map => JMap, SortedMap => JSortedMap, Set => JSet}
import java.util.AbstractMap
import java.util.AbstractMap.SimpleEntry
import java.util.Comparator

import scala.reflect.ClassTag
import scala.collection.JavaConverters._
import scala.collection.immutable

// TreeMap is not available in Scala.js however it is needed for Time Zone support
// This is a simple implementation of NavigableMap
private[zone] class ZoneMap[K: ClassTag, V](implicit ordering: Ordering[K]) extends AbstractMap[K, V] with java.util.NavigableMap[K, V] {
// Use an immutable TreeMap and replace it completely on updates
private var map = scala.collection.immutable.TreeMap[K, V]()

override def descendingMap() = ???
// This is a simple implementation of NavigableMap, performance is likely terrible
private[bp] class ZoneMap[K: ClassTag, V] private[bp] (var map: immutable.TreeMap[K, V])(implicit ordering: Ordering[K]) extends AbstractMap[K, V] with java.util.NavigableMap[K, V] {
def this()(implicit ordering: Ordering[K]) =
this(immutable.TreeMap[K, V]())

override def descendingMap(): java.util.NavigableMap[K, V] = {
val ord = ordering.reverse
new ZoneMap[K, V](map)
}

override def firstEntry(): java.util.Map.Entry[K, V] = {
val fk = firstKey()
map.get(fk).map(new SimpleEntry(firstKey(), _)).getOrElse(null.asInstanceOf[java.util.Map.Entry[K, V]])
map.get(fk).map(new SimpleEntry(fk, _)).getOrElse(null.asInstanceOf[java.util.Map.Entry[K, V]])
}

override def navigableKeySet() = ???
override def higherEntry(key: K): java.util.Map.Entry[K, V] = {
val k = map.filterKeys(x => ordering.compare(x, key) > 0)
if (k.isEmpty) null.asInstanceOf[java.util.Map.Entry[K, V]] else new SimpleEntry(k.head._1, k.head._2)
}

override def subMap(fromKey: K, fromInclusive: Boolean, toKey: K, toInclusive: Boolean) = ???
override def ceilingEntry(key: K): java.util.Map.Entry[K, V] = {
val k = map.filterKeys(x => ordering.compare(x, key) >= 0)
if (k.isEmpty) null.asInstanceOf[java.util.Map.Entry[K, V]] else new SimpleEntry(k.head._1, k.head._2)
}

override def subMap(fromKey: K, toKey: K) = ???
override def pollFirstEntry(): java.util.Map.Entry[K, V] = {
val fk = firstKey()
val entry = map.get(fk).map(new SimpleEntry(fk, _)).getOrElse(null.asInstanceOf[java.util.Map.Entry[K, V]])
map -= fk
entry
}

override def headMap(toKey: K, inclusive: Boolean) = ???
override def floorEntry(key: K): java.util.Map.Entry[K, V] = {
val k = map.filterKeys(x => ordering.compare(x, key) <= 0)
if (k.isEmpty) null.asInstanceOf[java.util.Map.Entry[K, V]] else new SimpleEntry(k.last._1, k.last._2)
}

override def headMap(toKey: K) = ???
override def lowerEntry(key: K): java.util.Map.Entry[K, V] = {
val k = map.filterKeys(x => ordering.compare(x, key) < 0)
if (k.isEmpty) null.asInstanceOf[java.util.Map.Entry[K, V]] else new SimpleEntry(k.last._1, k.last._2)
}

override def ceilingKey(key: K) = ???
override def pollLastEntry(): java.util.Map.Entry[K, V] = {
val lk = lastKey()
val entry = map.get(lk).map(new SimpleEntry(lk, _)).getOrElse(null.asInstanceOf[java.util.Map.Entry[K, V]])
map -= lk
entry
}

override def higherEntry(key: K) = ???
override def lastEntry(): java.util.Map.Entry[K, V] = {
val lk = lastKey()
map.get(lk).map(new SimpleEntry(lk, _)).getOrElse(null.asInstanceOf[java.util.Map.Entry[K, V]])
}

override def ceilingEntry(key: K) = ???
// Will not be implemented. It needs NavigableSet
override def navigableKeySet() = ???

override def pollFirstEntry() = ???
override def subMap(fromKey: K, fromInclusive: Boolean, toKey: K, toInclusive: Boolean) = {
val hk = if (toInclusive) map.filterKeys(x => ordering.compare(x, toKey) <= 0) else
map.filterKeys(x => ordering.compare(x, toKey) < 0)
val fk = if (fromInclusive) map.filterKeys(x => ordering.compare(x, fromKey) >= 0) else
map.filterKeys(x => ordering.compare(x, fromKey) > 0)
val intersect = hk.keySet.intersect(fk.keySet).map(k => k -> hk.get(k).getOrElse(fk(k))).toMap
new ZoneMap(immutable.TreeMap(intersect.toSeq: _*))
}

override def floorKey(key: K) = ???
override def subMap(fromKey: K, toKey: K) = subMap(fromKey, true, toKey, false)

override def floorEntry(key: K) = ???
override def headMap(toKey: K, inclusive: Boolean): java.util.NavigableMap[K, V] = {
val k = if (inclusive) map.filterKeys(x => ordering.compare(x, toKey) <= 0) else
map.filterKeys(x => ordering.compare(x, toKey) < 0)
if (k.isEmpty) new ZoneMap(immutable.TreeMap()) else new ZoneMap(immutable.TreeMap(k.toSeq: _*))
}

override def lowerEntry(key: K) = ???
override def headMap(toKey: K): JSortedMap[K, V] = headMap(toKey, false)

override def pollLastEntry() = ???
override def ceilingKey(key: K): K = {
val k = map.filterKeys(x => ordering.compare(x, key) >= 0)
if (k.isEmpty) null.asInstanceOf[K] else k.head._1
}

override def descendingKeySet() = ???
override def floorKey(key: K): K = {
val k = map.filterKeys(x => ordering.compare(x, key) <= 0)
if (k.isEmpty) null.asInstanceOf[K] else k.last._1
}

override def lastEntry(): java.util.Map.Entry[K, V] = {
val fk = firstKey()
map.get(fk).map(new SimpleEntry(lastKey(), _)).getOrElse(null.asInstanceOf[java.util.Map.Entry[K, V]])
}
// Will not be implemented. It needs NavigableSet
override def descendingKeySet() = ???

override def tailMap(fromKey: K, inclusive: Boolean) = ???
override def tailMap(fromKey: K, inclusive: Boolean): java.util.NavigableMap[K, V]= {
val k = if (inclusive) map.filterKeys(x => ordering.compare(x, fromKey) >= 0) else
map.filterKeys(x => ordering.compare(x, fromKey) > 0)
if (k.isEmpty) new ZoneMap(immutable.TreeMap()) else new ZoneMap(immutable.TreeMap(k.toSeq: _*))
}

override def tailMap(fromKey: K) = ???
override def tailMap(fromKey: K): JSortedMap[K, V] = tailMap(fromKey, true)

override def lowerKey(key: K) = ???
override def lowerKey(key: K): K = {
val k = map.filterKeys(x => ordering.compare(x, key) < 0)
if (k.isEmpty) null.asInstanceOf[K] else k.last._1
}

override def higherKey(key: K) = ???
override def higherKey(key: K) = {
val k = map.filterKeys(x => ordering.compare(x, key) > 0)
if (k.isEmpty) null.asInstanceOf[K] else k.head._1
}

override def firstKey(): K = map.firstKey

Expand All @@ -72,16 +127,22 @@ private[zone] class ZoneMap[K: ClassTag, V](implicit ordering: Ordering[K]) exte
override def values(): JCollection[V] = map.values.asJavaCollection

override def put(key: K, value: V): V = {
map = map + ((key, value))
value
val prev: Option[V] = map.get(key)
map += ((key, value))
prev.getOrElse(null.asInstanceOf[V])
}

override def clear(): Unit =
map = scala.collection.immutable.TreeMap[K, V]()
map = immutable.TreeMap()

override def entrySet(): JSet[JMap.Entry[K, V]] = {
map.map {
case (k, v) => new SimpleEntry[K, V](k, v): JMap.Entry[K, V]
}.toSet.asJava
}
}

object ZoneMap {

def apply[K: ClassTag, V](map: immutable.TreeMap[K, V])(implicit ordering: Ordering[K]): java.util.NavigableMap[K, V] = new ZoneMap[K, V](map)
}
4 changes: 4 additions & 0 deletions tests/js/src/test/scala/org/threeten/bp/Platform.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.threeten.bp

import java.util.NavigableMap
import org.threeten.bp.zone.ZoneMap

object Platform {
type NPE = scala.scalajs.js.JavaScriptException
type DFE = scala.scalajs.runtime.UndefinedBehaviorError
Expand All @@ -19,4 +22,5 @@ object Platform {
}
}

def zoneMap(m: scala.collection.immutable.TreeMap[Int, String]): NavigableMap[Int, String] = ZoneMap(m)
}
6 changes: 6 additions & 0 deletions tests/jvm/src/test/scala/org/threeten/bp/Platform.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package org.threeten.bp

import java.util.NavigableMap
import java.util.TreeMap
import scala.collection.JavaConverters._

object Platform {
type NPE = NullPointerException
type DFE = IndexOutOfBoundsException
Expand All @@ -11,4 +15,6 @@ object Platform {
final val executingInJVM = false

def setupLocales(): Unit = ()

def zoneMap(m: scala.collection.immutable.TreeMap[Int, String]): NavigableMap[Int, String] = new TreeMap(m.asJava)
}
153 changes: 153 additions & 0 deletions tests/shared/src/test/scala/java/util/TestZoneMap.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package org.threeten.bp

import org.scalatest.FunSuite
import org.threeten.bp.Platform.zoneMap
import scala.collection.mutable
import scala.collection.immutable.TreeMap
import java.util.AbstractMap.SimpleEntry

class TestZoneMap extends FunSuite with AssertionsHelper {
val m = TreeMap(0 -> "0", 1 -> "1", 3 -> "3", 2 -> "2")
val r = TreeMap(0 -> "0", 1 -> "1", 3 -> "3", 2 -> "2")(implicitly[Ordering[Int]].reverse)

test("creation") {
assertNotNull(zoneMap(m))
assertEquals(4, zoneMap(m).size)
val n = TreeMap(0 -> "0", 1 -> "1", 3 -> "3", 2 -> "2")
val cleared = zoneMap(n)
cleared.clear()
assertEquals(0, cleared.size)
}

test("firstKey") {
assertEquals(0, zoneMap(m).firstKey)
}

test("lastKey") {
assertEquals(3, zoneMap(m).lastKey)
}

test("firstEntry") {
assertEquals(new SimpleEntry(0, "0"), zoneMap(m).firstEntry)
}

test("lastEntry") {
assertEquals(new SimpleEntry(3, "3"), zoneMap(m).lastEntry)
}

test("descendingMap") {
assertEquals(zoneMap(r), zoneMap(m).descendingMap)
}

test("lowerKey") {
assertEquals(0, zoneMap(m).lowerKey(1))
assertEquals(2, zoneMap(m).lowerKey(3))
assertNull(zoneMap(m).lowerKey(-1))
}

test("lowerEntry") {
assertEquals(new SimpleEntry(0, "0"), zoneMap(m).lowerEntry(1))
assertEquals(new SimpleEntry(2, "2"), zoneMap(m).lowerEntry(3))
assertNull(zoneMap(m).lowerEntry(-1))
}

test("higherKey") {
assertEquals(2, zoneMap(m).higherKey(1))
assertNull(zoneMap(m).higherKey(3))
assertEquals(0, zoneMap(m).higherKey(-1))
}

test("higherEntry") {
assertEquals(new SimpleEntry(2, "2"), zoneMap(m).higherEntry(1))
assertNull(zoneMap(m).higherEntry(3))
assertEquals(new SimpleEntry(0, "0"), zoneMap(m).higherEntry(-1))
}

test("floorKey") {
assertEquals(1, zoneMap(m).floorKey(1))
assertEquals(3, zoneMap(m).floorKey(3))
assertNull(zoneMap(m).floorKey(-1))
}

test("floorEntry") {
assertEquals(new SimpleEntry(1, "1"), zoneMap(m).floorEntry(1))
assertEquals(new SimpleEntry(3, "3"), zoneMap(m).floorEntry(3))
assertNull(zoneMap(m).floorEntry(-1))
}

test("ceilingKey") {
assertEquals(1, zoneMap(m).ceilingKey(1))
assertEquals(3, zoneMap(m).ceilingKey(3))
assertEquals(0, zoneMap(m).ceilingKey(-1))
assertNull(zoneMap(m).ceilingKey(4))
}

test("ceilingEntry") {
assertEquals(new SimpleEntry(1, "1"), zoneMap(m).ceilingEntry(1))
assertEquals(new SimpleEntry(3, "3"), zoneMap(m).ceilingEntry(3))
assertEquals(new SimpleEntry(0, "0"), zoneMap(m).ceilingEntry(-1))
assertNull(zoneMap(m).ceilingEntry(4))
}

test("pollFirstEntry") {
val map = zoneMap(m)
assertEquals(new SimpleEntry(0, "0"), map.pollFirstEntry)
assertEquals(3, map.size)
}

test("pollLastEntry") {
val map = zoneMap(m)
assertEquals(new SimpleEntry(3, "3"), map.pollLastEntry)
assertEquals(3, map.size)
}

test("put") {
val map = zoneMap(m)
assertEquals("0", map.put(0, "5"))
assertEquals(4, map.size)
assertNull(map.put(5, "5"))
assertEquals(5, map.size)
}

test("tailMap") {
val map = zoneMap(m)
assertEquals(4, map.tailMap(0).size)
assertEquals(0, map.tailMap(5).size)
assertEquals(map, map.tailMap(-1))
}

test("tailMapI") {
val map = zoneMap(m)
assertEquals(4, map.tailMap(0, true).size)
assertEquals(3, map.tailMap(0, false).size)
assertEquals(0, map.tailMap(5, true).size)
assertEquals(0, map.tailMap(5, false).size)
assertEquals(map, map.tailMap(-1, true))
assertEquals(map, map.tailMap(-1, false))
}

test("headMap") {
val map = zoneMap(m)
assertEquals(0, map.headMap(0).size)
assertEquals(4, map.headMap(4).size)
assertEquals(map, map.headMap(6))
}

test("headMapI") {
val map = zoneMap(m)
assertEquals(0, map.headMap(0, false).size)
assertEquals(1, map.headMap(0, true).size)
assertEquals(4, map.headMap(4, false).size)
assertEquals(4, map.headMap(4, true).size)
assertEquals(map, map.headMap(6, false))
assertEquals(map, map.headMap(6, true))
}

test("subMap") {
val map = zoneMap(m)
assertEquals(4, map.subMap(0, 5).size)
assertEquals(0, map.subMap(4, 4).size)
assertEquals(map, map.subMap(0, 6))
}

}