-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
/
TypedMap.scala
196 lines (173 loc) · 6.78 KB
/
TypedMap.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/*
* Copyright (C) Lightbend Inc. <https://www.lightbend.com>
*/
package play.api.libs.typedmap
import scala.collection.immutable
/**
* A TypedMap is an immutable map containing typed values. Each entry is
* associated with a [[TypedKey]] that can be used to look up the value. A
* `TypedKey` also defines the type of the value, e.g. a `TypedKey[String]`
* would be associated with a `String` value.
*
* Instances of this class are created with the `TypedMap.empty` method.
*
* The elements inside TypedMaps cannot be enumerated. This is a decision
* designed to enforce modularity. It's not possible to accidentally or
* intentionally access a value in a TypedMap without holding the
* corresponding [[TypedKey]].
*/
trait TypedMap {
/**
* Get a value from the map, throwing an exception if it is not present.
*
* @param key The key for the value to retrieve.
* @tparam A The type of value to retrieve.
* @return The value, if it is present in the map.
* @throws scala.NoSuchElementException If the value isn't present in the map.
*/
def apply[A](key: TypedKey[A]): A
/**
* Get a value from the map, returning `None` if it is not present.
*
* @param key The key for the value to retrieve.
* @tparam A The type of value to retrieve.
* @return `Some` value, if it is present in the map, otherwise `None`.
*/
def get[A](key: TypedKey[A]): Option[A]
/**
* Check if the map contains a value with the given key.
*
* @param key The key to check for.
* @return True if the value is present, false otherwise.
*/
def contains(key: TypedKey[_]): Boolean
/**
* Update the map with the given key and value, returning a new instance of the map.
*
* @param key The key to set.
* @param value The value to use.
* @tparam A The type of value.
* @return A new instance of the map with the new entry added.
*/
def updated[A](key: TypedKey[A], value: A): TypedMap
/**
* Update the map with one entry, returning a new instance of the map.
*
* @param e1 The new entry to add to the map.
* @return A new instance of the map with the new entry added.
*/
def +(e1: TypedEntry[_]): TypedMap
/**
* Update the map with two entries, returning a new instance of the map.
*
* @param e1 The first new entry to add to the map.
* @param e2 The second new entry to add to the map.
* @return A new instance of the map with the new entries added.
*/
def +(e1: TypedEntry[_], e2: TypedEntry[_]): TypedMap
/**
* Update the map with three entries, returning a new instance of the map.
*
* @param e1 The first new entry to add to the map.
* @param e2 The second new entry to add to the map.
* @param e3 The third new entry to add to the map.
* @return A new instance of the map with the new entries added.
*/
def +(e1: TypedEntry[_], e2: TypedEntry[_], e3: TypedEntry[_]): TypedMap
/**
* Update the map with several entries, returning a new instance of the map.
*
* @param entries The new entries to add to the map.
* @return A new instance of the map with the new entries added.
*/
def +(entries: TypedEntry[_]*): TypedMap
/**
* Removes a key from the map, returning a new instance of the map.
*
* @param e1 The key to remove.
* @return A new instance of the map with the entry removed.
*/
def -(e1: TypedKey[_]): TypedMap
/**
* Removes two keys from the map, returning a new instance of the map.
*
* @param e1 The first key to remove.
* @param e2 The second key to remove.
* @return A new instance of the map with the entries removed.
*/
def -(e1: TypedKey[_], e2: TypedKey[_]): TypedMap
/**
* Removes three keys from the map, returning a new instance of the map.
*
* @param e1 The first key to remove.
* @param e2 The second key to remove.
* @param e3 The third key to remove.
* @return A new instance of the map with the entries removed.
*/
def -(e1: TypedKey[_], e2: TypedKey[_], e3: TypedKey[_]): TypedMap
/**
* Removes keys from the map, returning a new instance of the map.
*
* @param keys The keys to remove.
* @return A new instance of the map with the entries removed.
*/
def -(keys: TypedKey[_]*): TypedMap
/**
* @return The Java version for this map.
*/
def asJava: play.libs.typedmap.TypedMap = new play.libs.typedmap.TypedMap(this)
}
object TypedMap {
/**
* The empty [[TypedMap]] instance.
*/
val empty = new DefaultTypedMap(immutable.Map.empty)
/**
* Builds a [[TypedMap]] from an entry of key and value.
*/
def apply(e1: TypedEntry[_]): TypedMap = TypedMap.empty + e1
/**
* Builds a [[TypedMap]] from two entries of keys and values.
*/
def apply(e1: TypedEntry[_], e2: TypedEntry[_]): TypedMap = TypedMap.empty + (e1, e2)
/**
* Builds a [[TypedMap]] from three entries of keys and values.
*/
def apply(e1: TypedEntry[_], e2: TypedEntry[_], e3: TypedEntry[_]): TypedMap = TypedMap.empty + (e1, e2, e3)
/**
* Builds a [[TypedMap]] from a list of keys and values.
*/
def apply(entries: TypedEntry[_]*): TypedMap = {
TypedMap.empty.+(entries: _*)
}
}
/**
* An implementation of `TypedMap` that wraps a standard Scala [[Map]].
*/
private[typedmap] final class DefaultTypedMap private[typedmap] (m: immutable.Map[TypedKey[_], Any]) extends TypedMap {
override def apply[A](key: TypedKey[A]): A = m.apply(key).asInstanceOf[A]
override def get[A](key: TypedKey[A]): Option[A] = m.get(key).asInstanceOf[Option[A]]
override def contains(key: TypedKey[_]): Boolean = m.contains(key)
override def updated[A](key: TypedKey[A], value: A): TypedMap = new DefaultTypedMap(m.updated(key, value))
override def +(e1: TypedEntry[_]): TypedMap = new DefaultTypedMap(m.updated(e1.key, e1.value))
override def +(e1: TypedEntry[_], e2: TypedEntry[_]): TypedMap =
new DefaultTypedMap(m + (e1.key -> e1.value) + (e2.key -> e2.value))
override def +(e1: TypedEntry[_], e2: TypedEntry[_], e3: TypedEntry[_]): TypedMap =
new DefaultTypedMap(m + (e1.key -> e1.value) + (e2.key -> e2.value) + (e3.key -> e3.value))
override def +(entries: TypedEntry[_]*): TypedMap = {
val m2 = entries.foldLeft(m) {
case (m1, e) => m1.updated(e.key, e.value)
}
new DefaultTypedMap(m2)
}
override def -(k1: TypedKey[_]): TypedMap = new DefaultTypedMap(m - k1)
override def -(k1: TypedKey[_], k2: TypedKey[_]): TypedMap = new DefaultTypedMap(m - k1 - k2)
override def -(k1: TypedKey[_], k2: TypedKey[_], k3: TypedKey[_]): TypedMap = new DefaultTypedMap(m - k1 - k2 - k3)
override def -(keys: TypedKey[_]*): TypedMap = {
val m2 = keys.foldLeft(m) {
case (m1, k) => m1 - k
}
new DefaultTypedMap(m2)
}
override def toString: String = m.mkString("{", ", ", "}")
}