Skip to content

Commit 26b8c23

Browse files
authored
Merge pull request #90 from cquiroz/zonemap
Implement most of ZoneMap
2 parents 1067b1d + 2da0816 commit 26b8c23

File tree

4 files changed

+257
-33
lines changed

4 files changed

+257
-33
lines changed
Lines changed: 94 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,122 @@
11
package org.threeten.bp.zone
22

33
import java.util
4-
import java.util.{Collection => JCollection, Map => JMap, Set => JSet}
4+
import java.util.{Collection => JCollection, Map => JMap, SortedMap => JSortedMap, Set => JSet}
55
import java.util.AbstractMap
66
import java.util.AbstractMap.SimpleEntry
77
import java.util.Comparator
88

99
import scala.reflect.ClassTag
1010
import scala.collection.JavaConverters._
11+
import scala.collection.immutable
1112

1213
// TreeMap is not available in Scala.js however it is needed for Time Zone support
13-
// This is a simple implementation of NavigableMap
14-
private[zone] class ZoneMap[K: ClassTag, V](implicit ordering: Ordering[K]) extends AbstractMap[K, V] with java.util.NavigableMap[K, V] {
15-
// Use an immutable TreeMap and replace it completely on updates
16-
private var map = scala.collection.immutable.TreeMap[K, V]()
17-
18-
override def descendingMap() = ???
14+
// This is a simple implementation of NavigableMap, performance is likely terrible
15+
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] {
16+
def this()(implicit ordering: Ordering[K]) =
17+
this(immutable.TreeMap[K, V]())
18+
19+
override def descendingMap(): java.util.NavigableMap[K, V] = {
20+
val ord = ordering.reverse
21+
new ZoneMap[K, V](map)
22+
}
1923

2024
override def firstEntry(): java.util.Map.Entry[K, V] = {
2125
val fk = firstKey()
22-
map.get(fk).map(new SimpleEntry(firstKey(), _)).getOrElse(null.asInstanceOf[java.util.Map.Entry[K, V]])
26+
map.get(fk).map(new SimpleEntry(fk, _)).getOrElse(null.asInstanceOf[java.util.Map.Entry[K, V]])
2327
}
2428

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

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

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

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

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

35-
override def ceilingKey(key: K) = ???
56+
override def pollLastEntry(): java.util.Map.Entry[K, V] = {
57+
val lk = lastKey()
58+
val entry = map.get(lk).map(new SimpleEntry(lk, _)).getOrElse(null.asInstanceOf[java.util.Map.Entry[K, V]])
59+
map -= lk
60+
entry
61+
}
3662

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

39-
override def ceilingEntry(key: K) = ???
68+
// Will not be implemented. It needs NavigableSet
69+
override def navigableKeySet() = ???
4070

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

43-
override def floorKey(key: K) = ???
80+
override def subMap(fromKey: K, toKey: K) = subMap(fromKey, true, toKey, false)
4481

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

47-
override def lowerEntry(key: K) = ???
88+
override def headMap(toKey: K): JSortedMap[K, V] = headMap(toKey, false)
4889

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

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

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

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

60-
override def tailMap(fromKey: K) = ???
109+
override def tailMap(fromKey: K): JSortedMap[K, V] = tailMap(fromKey, true)
61110

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

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

66121
override def firstKey(): K = map.firstKey
67122

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

74129
override def put(key: K, value: V): V = {
75-
map = map + ((key, value))
76-
value
130+
val prev: Option[V] = map.get(key)
131+
map += ((key, value))
132+
prev.getOrElse(null.asInstanceOf[V])
77133
}
78134

79135
override def clear(): Unit =
80-
map = scala.collection.immutable.TreeMap[K, V]()
136+
map = immutable.TreeMap()
81137

82138
override def entrySet(): JSet[JMap.Entry[K, V]] = {
83139
map.map {
84140
case (k, v) => new SimpleEntry[K, V](k, v): JMap.Entry[K, V]
85141
}.toSet.asJava
86142
}
87143
}
144+
145+
object ZoneMap {
146+
147+
def apply[K: ClassTag, V](map: immutable.TreeMap[K, V])(implicit ordering: Ordering[K]): java.util.NavigableMap[K, V] = new ZoneMap[K, V](map)
148+
}

tests/js/src/test/scala/org/threeten/bp/Platform.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package org.threeten.bp
22

3+
import java.util.NavigableMap
4+
import org.threeten.bp.zone.ZoneMap
5+
36
object Platform {
47
type NPE = scala.scalajs.js.JavaScriptException
58
type DFE = scala.scalajs.runtime.UndefinedBehaviorError
@@ -19,4 +22,5 @@ object Platform {
1922
}
2023
}
2124

25+
def zoneMap(m: scala.collection.immutable.TreeMap[Int, String]): NavigableMap[Int, String] = ZoneMap(m)
2226
}

tests/jvm/src/test/scala/org/threeten/bp/Platform.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package org.threeten.bp
22

3+
import java.util.NavigableMap
4+
import java.util.TreeMap
5+
import scala.collection.JavaConverters._
6+
37
object Platform {
48
type NPE = NullPointerException
59
type DFE = IndexOutOfBoundsException
@@ -11,4 +15,6 @@ object Platform {
1115
final val executingInJVM = false
1216

1317
def setupLocales(): Unit = ()
18+
19+
def zoneMap(m: scala.collection.immutable.TreeMap[Int, String]): NavigableMap[Int, String] = new TreeMap(m.asJava)
1420
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
package org.threeten.bp
2+
3+
import org.scalatest.FunSuite
4+
import org.threeten.bp.Platform.zoneMap
5+
import scala.collection.mutable
6+
import scala.collection.immutable.TreeMap
7+
import java.util.AbstractMap.SimpleEntry
8+
9+
class TestZoneMap extends FunSuite with AssertionsHelper {
10+
val m = TreeMap(0 -> "0", 1 -> "1", 3 -> "3", 2 -> "2")
11+
val r = TreeMap(0 -> "0", 1 -> "1", 3 -> "3", 2 -> "2")(implicitly[Ordering[Int]].reverse)
12+
13+
test("creation") {
14+
assertNotNull(zoneMap(m))
15+
assertEquals(4, zoneMap(m).size)
16+
val n = TreeMap(0 -> "0", 1 -> "1", 3 -> "3", 2 -> "2")
17+
val cleared = zoneMap(n)
18+
cleared.clear()
19+
assertEquals(0, cleared.size)
20+
}
21+
22+
test("firstKey") {
23+
assertEquals(0, zoneMap(m).firstKey)
24+
}
25+
26+
test("lastKey") {
27+
assertEquals(3, zoneMap(m).lastKey)
28+
}
29+
30+
test("firstEntry") {
31+
assertEquals(new SimpleEntry(0, "0"), zoneMap(m).firstEntry)
32+
}
33+
34+
test("lastEntry") {
35+
assertEquals(new SimpleEntry(3, "3"), zoneMap(m).lastEntry)
36+
}
37+
38+
test("descendingMap") {
39+
assertEquals(zoneMap(r), zoneMap(m).descendingMap)
40+
}
41+
42+
test("lowerKey") {
43+
assertEquals(0, zoneMap(m).lowerKey(1))
44+
assertEquals(2, zoneMap(m).lowerKey(3))
45+
assertNull(zoneMap(m).lowerKey(-1))
46+
}
47+
48+
test("lowerEntry") {
49+
assertEquals(new SimpleEntry(0, "0"), zoneMap(m).lowerEntry(1))
50+
assertEquals(new SimpleEntry(2, "2"), zoneMap(m).lowerEntry(3))
51+
assertNull(zoneMap(m).lowerEntry(-1))
52+
}
53+
54+
test("higherKey") {
55+
assertEquals(2, zoneMap(m).higherKey(1))
56+
assertNull(zoneMap(m).higherKey(3))
57+
assertEquals(0, zoneMap(m).higherKey(-1))
58+
}
59+
60+
test("higherEntry") {
61+
assertEquals(new SimpleEntry(2, "2"), zoneMap(m).higherEntry(1))
62+
assertNull(zoneMap(m).higherEntry(3))
63+
assertEquals(new SimpleEntry(0, "0"), zoneMap(m).higherEntry(-1))
64+
}
65+
66+
test("floorKey") {
67+
assertEquals(1, zoneMap(m).floorKey(1))
68+
assertEquals(3, zoneMap(m).floorKey(3))
69+
assertNull(zoneMap(m).floorKey(-1))
70+
}
71+
72+
test("floorEntry") {
73+
assertEquals(new SimpleEntry(1, "1"), zoneMap(m).floorEntry(1))
74+
assertEquals(new SimpleEntry(3, "3"), zoneMap(m).floorEntry(3))
75+
assertNull(zoneMap(m).floorEntry(-1))
76+
}
77+
78+
test("ceilingKey") {
79+
assertEquals(1, zoneMap(m).ceilingKey(1))
80+
assertEquals(3, zoneMap(m).ceilingKey(3))
81+
assertEquals(0, zoneMap(m).ceilingKey(-1))
82+
assertNull(zoneMap(m).ceilingKey(4))
83+
}
84+
85+
test("ceilingEntry") {
86+
assertEquals(new SimpleEntry(1, "1"), zoneMap(m).ceilingEntry(1))
87+
assertEquals(new SimpleEntry(3, "3"), zoneMap(m).ceilingEntry(3))
88+
assertEquals(new SimpleEntry(0, "0"), zoneMap(m).ceilingEntry(-1))
89+
assertNull(zoneMap(m).ceilingEntry(4))
90+
}
91+
92+
test("pollFirstEntry") {
93+
val map = zoneMap(m)
94+
assertEquals(new SimpleEntry(0, "0"), map.pollFirstEntry)
95+
assertEquals(3, map.size)
96+
}
97+
98+
test("pollLastEntry") {
99+
val map = zoneMap(m)
100+
assertEquals(new SimpleEntry(3, "3"), map.pollLastEntry)
101+
assertEquals(3, map.size)
102+
}
103+
104+
test("put") {
105+
val map = zoneMap(m)
106+
assertEquals("0", map.put(0, "5"))
107+
assertEquals(4, map.size)
108+
assertNull(map.put(5, "5"))
109+
assertEquals(5, map.size)
110+
}
111+
112+
test("tailMap") {
113+
val map = zoneMap(m)
114+
assertEquals(4, map.tailMap(0).size)
115+
assertEquals(0, map.tailMap(5).size)
116+
assertEquals(map, map.tailMap(-1))
117+
}
118+
119+
test("tailMapI") {
120+
val map = zoneMap(m)
121+
assertEquals(4, map.tailMap(0, true).size)
122+
assertEquals(3, map.tailMap(0, false).size)
123+
assertEquals(0, map.tailMap(5, true).size)
124+
assertEquals(0, map.tailMap(5, false).size)
125+
assertEquals(map, map.tailMap(-1, true))
126+
assertEquals(map, map.tailMap(-1, false))
127+
}
128+
129+
test("headMap") {
130+
val map = zoneMap(m)
131+
assertEquals(0, map.headMap(0).size)
132+
assertEquals(4, map.headMap(4).size)
133+
assertEquals(map, map.headMap(6))
134+
}
135+
136+
test("headMapI") {
137+
val map = zoneMap(m)
138+
assertEquals(0, map.headMap(0, false).size)
139+
assertEquals(1, map.headMap(0, true).size)
140+
assertEquals(4, map.headMap(4, false).size)
141+
assertEquals(4, map.headMap(4, true).size)
142+
assertEquals(map, map.headMap(6, false))
143+
assertEquals(map, map.headMap(6, true))
144+
}
145+
146+
test("subMap") {
147+
val map = zoneMap(m)
148+
assertEquals(4, map.subMap(0, 5).size)
149+
assertEquals(0, map.subMap(4, 4).size)
150+
assertEquals(map, map.subMap(0, 6))
151+
}
152+
153+
}

0 commit comments

Comments
 (0)