1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one or more
3
+ * contributor license agreements. See the NOTICE file distributed with
4
+ * this work for additional information regarding copyright ownership.
5
+ * The ASF licenses this file to You under the Apache License, Version 2.0
6
+ * (the "License"); you may not use this file except in compliance with
7
+ * the License. You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
1
18
package org .apache .spark .sql .catalyst .util
2
19
3
20
import scala .collection .mutable
4
21
5
22
import org .json4s ._
6
23
import org .json4s .jackson .JsonMethods ._
7
24
8
- import scala .reflect .ClassTag
9
-
10
- sealed class Metadata private [util] (val map : Map [String , Any ]) extends Serializable {
11
-
12
- def getInt (key : String ): Int = get(key)
13
-
25
+ /**
26
+ * Metadata is a wrapper over Map[String, Any] that limits the value type to simple ones: Boolean,
27
+ * Long, Double, String, Metadata, Array[Boolean], Array[Long], Array[Double], Array[String], and
28
+ * Array[Metadata]. JSON is used for serialization.
29
+ *
30
+ * The default constructor is private. User should use either [[MetadataBuilder ]] or
31
+ * [[Metadata$#fromJson ]] to create Metadata instances.
32
+ *
33
+ * @param map an immutable map that stores the data
34
+ */
35
+ sealed class Metadata private [util] (private [util] val map : Map [String , Any ]) extends Serializable {
36
+
37
+ /** Gets a Long. */
38
+ def getLong (key : String ): Long = get(key)
39
+
40
+ /** Gets a Double. */
14
41
def getDouble (key : String ): Double = get(key)
15
42
43
+ /** Gets a Boolean. */
16
44
def getBoolean (key : String ): Boolean = get(key)
17
45
46
+ /** Gets a String. */
18
47
def getString (key : String ): String = get(key)
19
48
49
+ /** Gets a Metadata. */
20
50
def getMetadata (key : String ): Metadata = get(key)
21
51
22
- def getIntArray (key : String ): Array [Int ] = getArray(key)
52
+ /** Gets a Long array. */
53
+ def getLongArray (key : String ): Array [Long ] = get(key)
23
54
24
- def getDoubleArray (key : String ): Array [Double ] = getArray(key)
55
+ /** Gets a Double array. */
56
+ def getDoubleArray (key : String ): Array [Double ] = get(key)
25
57
26
- def getBooleanArray (key : String ): Array [Boolean ] = getArray(key)
58
+ /** Gets a Boolean array. */
59
+ def getBooleanArray (key : String ): Array [Boolean ] = get(key)
27
60
28
- def getStringArray (key : String ): Array [String ] = getArray(key)
61
+ /** Gets a String array. */
62
+ def getStringArray (key : String ): Array [String ] = get(key)
29
63
30
- def getMetadataArray (key : String ): Array [Metadata ] = getArray(key)
64
+ /** Gets a Metadata array. */
65
+ def getMetadataArray (key : String ): Array [Metadata ] = get(key)
31
66
67
+ /** Converts to its JSON representation. */
32
68
def toJson : String = {
33
69
compact(render(Metadata .toJValue(this )))
34
70
}
35
71
36
- private def get [T ](key : String ): T = {
37
- map(key).asInstanceOf [T ]
38
- }
72
+ override def toString : String = toJson
39
73
40
- private def getArray [T : ClassTag ](key : String ): Array [T ] = {
41
- map(key).asInstanceOf [Seq [T ]].toArray
74
+ override def equals (obj : Any ): Boolean = {
75
+ obj match {
76
+ case that : Metadata =>
77
+ if (map.keySet == that.map.keySet) {
78
+ map.keys.forall { k =>
79
+ (map(k), that.map(k)) match {
80
+ case (v0 : Array [_], v1 : Array [_]) =>
81
+ v0.view == v1.view
82
+ case (v0, v1) =>
83
+ v0 == v1
84
+ }
85
+ }
86
+ } else {
87
+ false
88
+ }
89
+ case other =>
90
+ false
91
+ }
42
92
}
43
93
44
- override def toString : String = toJson
94
+ override def hashCode : Int = Metadata .hash(this )
95
+
96
+ private def get [T ](key : String ): T = {
97
+ map(key).asInstanceOf [T ]
98
+ }
45
99
}
46
100
47
101
object Metadata {
48
102
103
+ /** Returns an empty Metadata. */
49
104
def empty : Metadata = new Metadata (Map .empty)
50
105
106
+ /** Creates a Metadata instance from JSON. */
51
107
def fromJson (json : String ): Metadata = {
52
108
val map = parse(json).values.asInstanceOf [Map [String , Any ]]
53
109
fromMap(map.toMap)
54
110
}
55
111
112
+ /** Creates a Metadata instance from Map[String, Any]. */
56
113
private def fromMap (map : Map [String , Any ]): Metadata = {
57
114
val builder = new MetadataBuilder
58
115
map.foreach {
59
- case (key, value : Int ) =>
60
- builder.putInt(key, value)
61
116
case (key, value : BigInt ) =>
62
- builder.putInt (key, value.toInt )
117
+ builder.putLong (key, value.toLong )
63
118
case (key, value : Double ) =>
64
119
builder.putDouble(key, value)
65
120
case (key, value : Boolean ) =>
@@ -70,22 +125,21 @@ object Metadata {
70
125
builder.putMetadata(key, fromMap(value.asInstanceOf [Map [String , Any ]]))
71
126
case (key, value : Seq [_]) =>
72
127
if (value.isEmpty) {
73
- builder.putIntArray(key, Seq .empty)
128
+ // If it is an empty array, we cannot infer its element type. We put an empty Array[Long].
129
+ builder.putLongArray(key, Array .empty)
74
130
} else {
75
131
value.head match {
76
- case _ : Int =>
77
- builder.putIntArray(key, value.asInstanceOf [Seq [Int ]].toSeq)
78
132
case _ : BigInt =>
79
- builder.putIntArray (key, value.asInstanceOf [Seq [BigInt ]].map(_.toInt).toSeq )
133
+ builder.putLongArray (key, value.asInstanceOf [Seq [BigInt ]].map(_.toLong).toArray )
80
134
case _ : Double =>
81
- builder.putDoubleArray(key, value.asInstanceOf [Seq [Double ]].toSeq )
135
+ builder.putDoubleArray(key, value.asInstanceOf [Seq [Double ]].toArray )
82
136
case _ : Boolean =>
83
- builder.putBooleanArray(key, value.asInstanceOf [Seq [Boolean ]].toSeq )
137
+ builder.putBooleanArray(key, value.asInstanceOf [Seq [Boolean ]].toArray )
84
138
case _ : String =>
85
- builder.putStringArray(key, value.asInstanceOf [Seq [String ]].toSeq)
86
- case _ : Map [String , Any ] =>
139
+ builder.putStringArray(key, value.asInstanceOf [Seq [String ]].toSeq.toArray )
140
+ case _ : Map [_, _ ] =>
87
141
builder.putMetadataArray(
88
- key, value.asInstanceOf [Seq [Map [String , Any ]]].map(fromMap).toSeq )
142
+ key, value.asInstanceOf [Seq [Map [String , Any ]]].map(fromMap).toArray )
89
143
case other =>
90
144
throw new RuntimeException (s " Do not support array of type ${other.getClass}. " )
91
145
}
@@ -96,15 +150,16 @@ object Metadata {
96
150
builder.build()
97
151
}
98
152
153
+ /** Converts to JSON AST. */
99
154
private def toJValue (obj : Any ): JValue = {
100
155
obj match {
101
156
case map : Map [_, _] =>
102
- val fields = map.toList.map { case (k : String , v) => (k, toJValue(v)) }
157
+ val fields = map.toList.map { case (k : String , v) => (k, toJValue(v))}
103
158
JObject (fields)
104
- case arr : Seq [_] =>
159
+ case arr : Array [_] =>
105
160
val values = arr.toList.map(toJValue)
106
161
JArray (values)
107
- case x : Int =>
162
+ case x : Long =>
108
163
JInt (x)
109
164
case x : Double =>
110
165
JDouble (x)
@@ -118,37 +173,75 @@ object Metadata {
118
173
throw new RuntimeException (s " Do not support type ${other.getClass}. " )
119
174
}
120
175
}
176
+
177
+ /** Computes the hash code for the types we support. */
178
+ private def hash (obj : Any ): Int = {
179
+ obj match {
180
+ case map : Map [_, _] =>
181
+ map.mapValues(hash).##
182
+ case arr : Array [_] =>
183
+ // Seq.empty[T] has the same hashCode regardless of T.
184
+ arr.toSeq.map(hash).##
185
+ case x : Long =>
186
+ x.##
187
+ case x : Double =>
188
+ x.##
189
+ case x : Boolean =>
190
+ x.##
191
+ case x : String =>
192
+ x.##
193
+ case x : Metadata =>
194
+ hash(x.map)
195
+ case other =>
196
+ throw new RuntimeException (s " Do not support type ${other.getClass}. " )
197
+ }
198
+ }
121
199
}
122
200
201
+ /**
202
+ * Builder for [[Metadata ]]. If there is a key collision, the latter will overwrite the former.
203
+ */
123
204
class MetadataBuilder {
124
205
125
206
private val map : mutable.Map [String , Any ] = mutable.Map .empty
126
207
208
+ /** Include the content of an existing [[Metadata ]] instance. */
127
209
def withMetadata (metadata : Metadata ): this .type = {
128
210
map ++= metadata.map
129
211
this
130
212
}
131
213
132
- def putInt (key : String , value : Int ): this .type = put(key, value)
214
+ /** Puts a Long. */
215
+ def putLong (key : String , value : Long ): this .type = put(key, value)
133
216
217
+ /** Puts a Double. */
134
218
def putDouble (key : String , value : Double ): this .type = put(key, value)
135
219
220
+ /** Puts a Boolean. */
136
221
def putBoolean (key : String , value : Boolean ): this .type = put(key, value)
137
222
223
+ /** Puts a String. */
138
224
def putString (key : String , value : String ): this .type = put(key, value)
139
225
226
+ /** Puts a [[Metadata ]]. */
140
227
def putMetadata (key : String , value : Metadata ): this .type = put(key, value)
141
228
142
- def putIntArray (key : String , value : Seq [Int ]): this .type = put(key, value)
229
+ /** Puts a Long array. */
230
+ def putLongArray (key : String , value : Array [Long ]): this .type = put(key, value)
143
231
144
- def putDoubleArray (key : String , value : Seq [Double ]): this .type = put(key, value)
232
+ /** Puts a Double array. */
233
+ def putDoubleArray (key : String , value : Array [Double ]): this .type = put(key, value)
145
234
146
- def putBooleanArray (key : String , value : Seq [Boolean ]): this .type = put(key, value)
235
+ /** Puts a Boolean array. */
236
+ def putBooleanArray (key : String , value : Array [Boolean ]): this .type = put(key, value)
147
237
148
- def putStringArray (key : String , value : Seq [String ]): this .type = put(key, value)
238
+ /** Puts a String array. */
239
+ def putStringArray (key : String , value : Array [String ]): this .type = put(key, value)
149
240
150
- def putMetadataArray (key : String , value : Seq [Metadata ]): this .type = put(key, value)
241
+ /** Puts a [[Metadata ]] array. */
242
+ def putMetadataArray (key : String , value : Array [Metadata ]): this .type = put(key, value)
151
243
244
+ /** Builds the [[Metadata ]] instance. */
152
245
def build (): Metadata = {
153
246
new Metadata (map.toMap)
154
247
}
0 commit comments