Skip to content

Commit f7d045f

Browse files
committed
feat: 🚀 Remove usage of Manifest in ScalaObjectMapper
Replaced with typeclasses
1 parent 546cdf2 commit f7d045f

File tree

1 file changed

+127
-70
lines changed

1 file changed

+127
-70
lines changed

src/main/scala/com/fasterxml/jackson/module/scala/ScalaObjectMapper.scala

Lines changed: 127 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,21 @@ package com.fasterxml.jackson.module.scala
22

33
import com.fasterxml.jackson.core.{JsonParser, TreeNode}
44
import com.fasterxml.jackson.databind._
5+
import com.fasterxml.jackson.databind.`type`.TypeFactory
56
import com.fasterxml.jackson.databind.json.JsonMapper
67
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper
78

89
import java.io.{File, InputStream, Reader}
910
import java.net.URL
11+
import scala.reflect.{ClassTag, classTag}
1012

1113
object ScalaObjectMapper {
1214
def ::(o: JsonMapper) = new Mixin(o)
1315
final class Mixin private[ScalaObjectMapper](mapper: JsonMapper)
1416
extends JsonMapper(mapper.rebuild()) with ScalaObjectMapper
1517
}
1618

17-
@deprecated("ScalaObjectMapper is deprecated because Manifests are not supported in Scala3", "2.12.1")
19+
//@deprecated("ScalaObjectMapper is deprecated because Manifests are not supported in Scala3", "2.12.1")
1820
trait ScalaObjectMapper {
1921
self: ObjectMapper =>
2022

@@ -29,36 +31,8 @@ trait ScalaObjectMapper {
2931
* type (typically <code>java.lang.Class</code>), but without explicit
3032
* context.
3133
*/
32-
def constructType[T](implicit m: Manifest[T]): JavaType = {
33-
val clazz = m.runtimeClass
34-
if (isArray(clazz)) {
35-
val typeArguments = m.typeArguments.map(constructType(_)).toArray
36-
if (typeArguments.length != 1) {
37-
throw new IllegalArgumentException("Need exactly 1 type parameter for array like types ("+clazz.getName+")")
38-
}
39-
getTypeFactory.constructArrayType(typeArguments(0))
40-
} else if (isMapLike(clazz)) {
41-
val typeArguments = m.typeArguments.map(constructType(_)).toArray
42-
if (typeArguments.length != 2) {
43-
throw new IllegalArgumentException("Need exactly 2 type parameters for map like types ("+clazz.getName+")")
44-
}
45-
getTypeFactory.constructMapLikeType(clazz, typeArguments(0), typeArguments(1))
46-
} else if (isReference(clazz)) { // Option is a subclss of IterableOnce, so check it first
47-
val typeArguments = m.typeArguments.map(constructType(_)).toArray
48-
if (typeArguments.length != 1) {
49-
throw new IllegalArgumentException("Need exactly 1 type parameter for reference types ("+clazz.getName+")")
50-
}
51-
getTypeFactory.constructReferenceType(clazz, typeArguments(0))
52-
} else if (isCollectionLike(clazz)) {
53-
val typeArguments = m.typeArguments.map(constructType(_)).toArray
54-
if (typeArguments.length != 1) {
55-
throw new IllegalArgumentException("Need exactly 1 type parameter for collection like types ("+clazz.getName+")")
56-
}
57-
getTypeFactory.constructCollectionLikeType(clazz, typeArguments(0))
58-
} else {
59-
val typeArguments = m.typeArguments.map(constructType(_)).toArray
60-
getTypeFactory.constructParametricType(clazz, typeArguments: _*)
61-
}
34+
def constructType[T](implicit m: JavaTypeable[T]): JavaType = {
35+
m.asJavaType(getTypeFactory)
6236
}
6337

6438
/*
@@ -76,7 +50,7 @@ trait ScalaObjectMapper {
7650
* and specifically needs to be used if the root type is a
7751
* parameterized (generic) container type.
7852
*/
79-
def readValue[T: Manifest](jp: JsonParser): T = {
53+
def readValue[T: JavaTypeable](jp: JsonParser): T = {
8054
readValue(jp, constructType[T])
8155
}
8256

@@ -92,7 +66,7 @@ trait ScalaObjectMapper {
9266
* <p>
9367
* Note that [[com.fasterxml.jackson.databind.ObjectReader]] has more complete set of variants.
9468
*/
95-
def readValues[T: Manifest](jp: JsonParser): MappingIterator[T] = {
69+
def readValues[T: JavaTypeable](jp: JsonParser): MappingIterator[T] = {
9670
readValues(jp, constructType[T])
9771
}
9872

@@ -111,8 +85,8 @@ trait ScalaObjectMapper {
11185
* objectMapper.convertValue(n, valueClass);
11286
* </pre>
11387
*/
114-
def treeToValue[T: Manifest](n: TreeNode): T = {
115-
treeToValue(n, manifest[T].runtimeClass).asInstanceOf[T]
88+
def treeToValue[T: ClassTag](n: TreeNode): T = {
89+
treeToValue(n, classTag[T].runtimeClass).asInstanceOf[T]
11690
}
11791

11892
/*
@@ -127,63 +101,63 @@ trait ScalaObjectMapper {
127101
* convenience methods
128102
**********************************************************
129103
*/
130-
def readValue[T: Manifest](src: File): T = {
104+
def readValue[T: JavaTypeable](src: File): T = {
131105
readValue(src, constructType[T])
132106
}
133107

134-
def readValue[T: Manifest](src: URL): T = {
108+
def readValue[T: JavaTypeable](src: URL): T = {
135109
readValue(src, constructType[T])
136110
}
137111

138-
def readValue[T: Manifest](content: String): T = {
112+
def readValue[T: JavaTypeable](content: String): T = {
139113
readValue(content, constructType[T])
140114
}
141115

142-
def readValue[T: Manifest](src: Reader): T = {
116+
def readValue[T: JavaTypeable](src: Reader): T = {
143117
readValue(src, constructType[T])
144118
}
145119

146-
def readValue[T: Manifest](src: InputStream): T = {
120+
def readValue[T: JavaTypeable](src: InputStream): T = {
147121
readValue(src, constructType[T])
148122
}
149123

150-
def readValue[T: Manifest](src: Array[Byte]): T = {
124+
def readValue[T: JavaTypeable](src: Array[Byte]): T = {
151125
readValue(src, constructType[T])
152126
}
153127

154-
def readValue[T: Manifest](src: Array[Byte], offset: Int, len: Int): T = {
128+
def readValue[T: JavaTypeable](src: Array[Byte], offset: Int, len: Int): T = {
155129
readValue(src, offset, len, constructType[T])
156130
}
157131

158-
def updateValue[T: Manifest](valueToUpdate: T, src: File): T = {
132+
def updateValue[T: JavaTypeable](valueToUpdate: T, src: File): T = {
159133
objectReaderFor(valueToUpdate).readValue(src)
160134
}
161135

162-
def updateValue[T: Manifest](valueToUpdate: T, src: URL): T = {
136+
def updateValue[T: JavaTypeable](valueToUpdate: T, src: URL): T = {
163137
objectReaderFor(valueToUpdate).readValue(src)
164138
}
165139

166-
def updateValue[T: Manifest](valueToUpdate: T, content: String): T = {
140+
def updateValue[T: JavaTypeable](valueToUpdate: T, content: String): T = {
167141
objectReaderFor(valueToUpdate).readValue(content)
168142
}
169143

170-
def updateValue[T: Manifest](valueToUpdate: T, src: Reader): T = {
144+
def updateValue[T: JavaTypeable](valueToUpdate: T, src: Reader): T = {
171145
objectReaderFor(valueToUpdate).readValue(src)
172146
}
173147

174-
def updateValue[T: Manifest](valueToUpdate: T, src: InputStream): T = {
148+
def updateValue[T: JavaTypeable](valueToUpdate: T, src: InputStream): T = {
175149
objectReaderFor(valueToUpdate).readValue(src)
176150
}
177151

178-
def updateValue[T: Manifest](valueToUpdate: T, src: Array[Byte]): T = {
152+
def updateValue[T: JavaTypeable](valueToUpdate: T, src: Array[Byte]): T = {
179153
objectReaderFor(valueToUpdate).readValue(src)
180154
}
181155

182-
def updateValue[T: Manifest](valueToUpdate: T, src: Array[Byte], offset: Int, len: Int): T = {
156+
def updateValue[T: JavaTypeable](valueToUpdate: T, src: Array[Byte], offset: Int, len: Int): T = {
183157
objectReaderFor(valueToUpdate).readValue(src, offset, len)
184158
}
185159

186-
private def objectReaderFor[T: Manifest](valueToUpdate: T): ObjectReader = {
160+
private def objectReaderFor[T: JavaTypeable](valueToUpdate: T): ObjectReader = {
187161
readerForUpdating(valueToUpdate).forType(constructType[T])
188162
}
189163

@@ -198,8 +172,8 @@ trait ScalaObjectMapper {
198172
* Factory method for constructing [[com.fasterxml.jackson.databind.ObjectWriter]] that will
199173
* serialize objects using specified JSON View (filter).
200174
*/
201-
def writerWithView[T: Manifest]: ObjectWriter = {
202-
writerWithView(manifest[T].runtimeClass)
175+
def writerWithView[T: ClassTag]: ObjectWriter = {
176+
writerWithView(classTag[T].runtimeClass)
203177
}
204178

205179
/**
@@ -213,7 +187,7 @@ trait ScalaObjectMapper {
213187
*
214188
* @since 2.5
215189
*/
216-
def writerFor[T: Manifest]: ObjectWriter = {
190+
def writerFor[T: JavaTypeable]: ObjectWriter = {
217191
writerFor(constructType[T])
218192
}
219193

@@ -228,16 +202,16 @@ trait ScalaObjectMapper {
228202
* Factory method for constructing [[com.fasterxml.jackson.databind.ObjectReader]] that will
229203
* read or update instances of specified type
230204
*/
231-
def readerFor[T: Manifest]: ObjectReader = {
205+
def readerFor[T: JavaTypeable]: ObjectReader = {
232206
readerFor(constructType[T])
233207
}
234208

235209
/**
236210
* Factory method for constructing [[com.fasterxml.jackson.databind.ObjectReader]] that will
237211
* deserialize objects using specified JSON View (filter).
238212
*/
239-
def readerWithView[T: Manifest]: ObjectReader = {
240-
readerWithView(manifest[T].runtimeClass)
213+
def readerWithView[T: ClassTag]: ObjectReader = {
214+
readerWithView(classTag[T].runtimeClass)
241215
}
242216

243217
/*
@@ -258,7 +232,7 @@ trait ScalaObjectMapper {
258232
* if so, root cause will contain underlying checked exception data binding
259233
* functionality threw
260234
*/
261-
def convertValue[T: Manifest](fromValue: Any): T = {
235+
def convertValue[T: JavaTypeable](fromValue: Any): T = {
262236
convertValue(fromValue, constructType[T])
263237
}
264238

@@ -279,26 +253,109 @@ trait ScalaObjectMapper {
279253
*
280254
* @since 2.1
281255
*/
282-
def acceptJsonFormatVisitor[T: Manifest](visitor: JsonFormatVisitorWrapper): Unit = {
283-
acceptJsonFormatVisitor(manifest[T].runtimeClass, visitor)
256+
def acceptJsonFormatVisitor[T: ClassTag](visitor: JsonFormatVisitorWrapper): Unit = {
257+
acceptJsonFormatVisitor(classTag[T].runtimeClass, visitor)
284258
}
285259

286-
private def isArray(c: Class[_]): Boolean = {
287-
c.isArray
260+
}
261+
262+
trait JavaTypeable[T] {
263+
def asJavaType(typeFactory: TypeFactory): JavaType
264+
}
265+
266+
object JavaTypeable {
267+
268+
implicit val anyJavaTypeable: JavaTypeable[Any] = {
269+
new JavaTypeable[Any] {
270+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
271+
val typeArgs: Array[JavaType] = Array()
272+
typeFactory.constructParametricType(classOf[Object], typeArgs: _*)
273+
}
274+
}
288275
}
289276

290-
private val MAP = classOf[collection.Map[_,_]]
291-
private def isMapLike(c: Class[_]): Boolean = {
292-
MAP.isAssignableFrom(c)
277+
implicit def optionJavaTypeable[T : JavaTypeable]: JavaTypeable[Option[T]] = {
278+
new JavaTypeable[Option[T]] {
279+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
280+
val typeArg0 = implicitly[JavaTypeable[T]].asJavaType(typeFactory)
281+
typeFactory.constructReferenceType(classOf[Option[_]], typeArg0)
282+
}
283+
}
293284
}
294285

295-
private val OPTION = classOf[Option[_]]
296-
private def isReference(c: Class[_]): Boolean = {
297-
OPTION.isAssignableFrom(c)
286+
implicit def arrayJavaTypeable[T : JavaTypeable]: JavaTypeable[Array[T]] = {
287+
new JavaTypeable[Array[T]] {
288+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
289+
val typeArg0 = implicitly[JavaTypeable[T]].asJavaType(typeFactory)
290+
typeFactory.constructArrayType(typeArg0)
291+
}
292+
}
298293
}
299294

300-
private val ITERABLE = classOf[collection.Iterable[_]]
301-
private def isCollectionLike(c: Class[_]): Boolean = {
302-
ITERABLE.isAssignableFrom(c)
295+
implicit def mapJavaTypeable[M[_,_] <: Map[_,_], K : JavaTypeable, V: JavaTypeable](implicit ct: ClassTag[M[K,V]]): JavaTypeable[M[K, V]] = {
296+
new JavaTypeable[M[K, V]] {
297+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
298+
val typeArg0 = implicitly[JavaTypeable[K]].asJavaType(typeFactory)
299+
val typeArg1 = implicitly[JavaTypeable[V]].asJavaType(typeFactory)
300+
typeFactory.constructMapLikeType(ct.runtimeClass, typeArg0, typeArg1)
301+
}
302+
}
303303
}
304+
305+
implicit def collectionJavaTypeable[I[_] <: Iterable[_], T : JavaTypeable](implicit ct: ClassTag[I[T]]): JavaTypeable[I[T]] = {
306+
new JavaTypeable[I[T]] {
307+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
308+
val typeArg0 = implicitly[JavaTypeable[T]].asJavaType(typeFactory)
309+
typeFactory.constructCollectionLikeType(ct.runtimeClass, typeArg0)
310+
}
311+
}
312+
}
313+
314+
// TODO generate genX for X up to a large enough number, 10? 22?
315+
316+
implicit def gen3JavaTypeable[T[_, _, _], A: JavaTypeable, B: JavaTypeable, C: JavaTypeable](implicit ct: ClassTag[T[A, B, C]]): JavaTypeable[T[A, B, C]] = {
317+
new JavaTypeable[T[A, B, C]] {
318+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
319+
val typeArgs: Array[JavaType] = Array(
320+
implicitly[JavaTypeable[A]].asJavaType(typeFactory),
321+
implicitly[JavaTypeable[B]].asJavaType(typeFactory),
322+
implicitly[JavaTypeable[C]].asJavaType(typeFactory)
323+
)
324+
typeFactory.constructParametricType(ct.runtimeClass, typeArgs: _*)
325+
}
326+
}
327+
}
328+
329+
implicit def gen2JavaTypeable[T[_, _], A: JavaTypeable, B: JavaTypeable](implicit ct: ClassTag[T[A, B]]): JavaTypeable[T[A, B]] = {
330+
new JavaTypeable[T[A, B]] {
331+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
332+
val typeArgs: Array[JavaType] = Array(
333+
implicitly[JavaTypeable[A]].asJavaType(typeFactory),
334+
implicitly[JavaTypeable[B]].asJavaType(typeFactory)
335+
)
336+
typeFactory.constructParametricType(ct.runtimeClass, typeArgs: _*)
337+
}
338+
}
339+
}
340+
341+
implicit def gen1JavaTypeable[T[_], A: JavaTypeable](implicit ct: ClassTag[T[A]]): JavaTypeable[T[A]] = {
342+
new JavaTypeable[T[A]] {
343+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
344+
val typeArgs: Array[JavaType] = Array(
345+
implicitly[JavaTypeable[A]].asJavaType(typeFactory)
346+
)
347+
typeFactory.constructParametricType(ct.runtimeClass, typeArgs: _*)
348+
}
349+
}
350+
}
351+
352+
implicit def gen0JavaTypeable[T](implicit ct: ClassTag[T]): JavaTypeable[T] = {
353+
new JavaTypeable[T] {
354+
override def asJavaType(typeFactory: TypeFactory): JavaType = {
355+
val typeArgs: Array[JavaType] = Array()
356+
typeFactory.constructParametricType(ct.runtimeClass, typeArgs: _*)
357+
}
358+
}
359+
}
360+
304361
}

0 commit comments

Comments
 (0)