From d06e656a20656252cad8c374269d1ad05c1b76fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Jourdan-Weil?= Date: Wed, 19 May 2021 17:45:54 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=9A=80=20Remove=20usage=20of=20Ma?= =?UTF-8?q?nifest=20in=20ScalaObjectMapper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaced with typeclasses --- .../module/scala/ScalaObjectMapper.scala | 169 +++++++++++++----- 1 file changed, 129 insertions(+), 40 deletions(-) diff --git a/src/main/scala-2.+/com/fasterxml/jackson/module/scala/ScalaObjectMapper.scala b/src/main/scala-2.+/com/fasterxml/jackson/module/scala/ScalaObjectMapper.scala index b480e9a6f..2fcda6877 100644 --- a/src/main/scala-2.+/com/fasterxml/jackson/module/scala/ScalaObjectMapper.scala +++ b/src/main/scala-2.+/com/fasterxml/jackson/module/scala/ScalaObjectMapper.scala @@ -7,7 +7,11 @@ import com.fasterxml.jackson.core.{JsonParser, TreeNode} import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper import com.fasterxml.jackson.databind.jsonschema.JsonSchema import com.fasterxml.jackson.databind._ +import com.fasterxml.jackson.databind.`type`.TypeFactory import com.fasterxml.jackson.databind.json.JsonMapper +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper + +import scala.reflect.{ClassTag, classTag} object ScalaObjectMapper { def ::(o: JsonMapper) = new Mixin(o) @@ -15,7 +19,7 @@ object ScalaObjectMapper { extends JsonMapper(mapper.rebuild().build()) with ScalaObjectMapper } -@deprecated("ScalaObjectMapper is deprecated because Manifests are not supported in Scala3", "2.12.1") +//@deprecated("ScalaObjectMapper is deprecated because Manifests are not supported in Scala3", "2.12.1") trait ScalaObjectMapper { self: ObjectMapper => @@ -64,6 +68,8 @@ trait ScalaObjectMapper { * type (typically java.lang.Class), but without explicit * context. */ + def constructType[T](implicit m: JavaTypeable[T]): JavaType = { + m.asJavaType(getTypeFactory) def constructType[T](implicit m: Manifest[T]): JavaType = { val clazz = m.runtimeClass if (isArray(clazz)) { @@ -111,7 +117,7 @@ trait ScalaObjectMapper { * and specifically needs to be used if the root type is a * parameterized (generic) container type. */ - def readValue[T: Manifest](jp: JsonParser): T = { + def readValue[T: JavaTypeable](jp: JsonParser): T = { readValue(jp, constructType[T]) } @@ -127,7 +133,7 @@ trait ScalaObjectMapper { *

* Note that [[com.fasterxml.jackson.databind.ObjectReader]] has more complete set of variants. */ - def readValues[T: Manifest](jp: JsonParser): MappingIterator[T] = { + def readValues[T: JavaTypeable](jp: JsonParser): MappingIterator[T] = { readValues(jp, constructType[T]) } @@ -146,8 +152,8 @@ trait ScalaObjectMapper { * objectMapper.convertValue(n, valueClass); * */ - def treeToValue[T: Manifest](n: TreeNode): T = { - treeToValue(n, manifest[T].runtimeClass).asInstanceOf[T] + def treeToValue[T: ClassTag](n: TreeNode): T = { + treeToValue(n, classTag[T].runtimeClass).asInstanceOf[T] } /* @@ -194,63 +200,63 @@ trait ScalaObjectMapper { * convenience methods ********************************************************** */ - def readValue[T: Manifest](src: File): T = { + def readValue[T: JavaTypeable](src: File): T = { readValue(src, constructType[T]) } - def readValue[T: Manifest](src: URL): T = { + def readValue[T: JavaTypeable](src: URL): T = { readValue(src, constructType[T]) } - def readValue[T: Manifest](content: String): T = { + def readValue[T: JavaTypeable](content: String): T = { readValue(content, constructType[T]) } - def readValue[T: Manifest](src: Reader): T = { + def readValue[T: JavaTypeable](src: Reader): T = { readValue(src, constructType[T]) } - def readValue[T: Manifest](src: InputStream): T = { + def readValue[T: JavaTypeable](src: InputStream): T = { readValue(src, constructType[T]) } - def readValue[T: Manifest](src: Array[Byte]): T = { + def readValue[T: JavaTypeable](src: Array[Byte]): T = { readValue(src, constructType[T]) } - def readValue[T: Manifest](src: Array[Byte], offset: Int, len: Int): T = { + def readValue[T: JavaTypeable](src: Array[Byte], offset: Int, len: Int): T = { readValue(src, offset, len, constructType[T]) } - def updateValue[T: Manifest](valueToUpdate: T, src: File): T = { + def updateValue[T: JavaTypeable](valueToUpdate: T, src: File): T = { objectReaderFor(valueToUpdate).readValue(src) } - def updateValue[T: Manifest](valueToUpdate: T, src: URL): T = { + def updateValue[T: JavaTypeable](valueToUpdate: T, src: URL): T = { objectReaderFor(valueToUpdate).readValue(src) } - def updateValue[T: Manifest](valueToUpdate: T, content: String): T = { + def updateValue[T: JavaTypeable](valueToUpdate: T, content: String): T = { objectReaderFor(valueToUpdate).readValue(content) } - def updateValue[T: Manifest](valueToUpdate: T, src: Reader): T = { + def updateValue[T: JavaTypeable](valueToUpdate: T, src: Reader): T = { objectReaderFor(valueToUpdate).readValue(src) } - def updateValue[T: Manifest](valueToUpdate: T, src: InputStream): T = { + def updateValue[T: JavaTypeable](valueToUpdate: T, src: InputStream): T = { objectReaderFor(valueToUpdate).readValue(src) } - def updateValue[T: Manifest](valueToUpdate: T, src: Array[Byte]): T = { + def updateValue[T: JavaTypeable](valueToUpdate: T, src: Array[Byte]): T = { objectReaderFor(valueToUpdate).readValue(src) } - def updateValue[T: Manifest](valueToUpdate: T, src: Array[Byte], offset: Int, len: Int): T = { + def updateValue[T: JavaTypeable](valueToUpdate: T, src: Array[Byte], offset: Int, len: Int): T = { objectReaderFor(valueToUpdate).readValue(src, offset, len) } - private def objectReaderFor[T: Manifest](valueToUpdate: T): ObjectReader = { + private def objectReaderFor[T: JavaTypeable](valueToUpdate: T): ObjectReader = { readerForUpdating(valueToUpdate).forType(constructType[T]) } @@ -265,8 +271,8 @@ trait ScalaObjectMapper { * Factory method for constructing [[com.fasterxml.jackson.databind.ObjectWriter]] that will * serialize objects using specified JSON View (filter). */ - def writerWithView[T: Manifest]: ObjectWriter = { - writerWithView(manifest[T].runtimeClass) + def writerWithView[T: ClassTag]: ObjectWriter = { + writerWithView(classTag[T].runtimeClass) } /** @@ -288,7 +294,7 @@ trait ScalaObjectMapper { * * @since 2.5 */ - def writerFor[T: Manifest]: ObjectWriter = { + def writerFor[T: JavaTypeable]: ObjectWriter = { writerFor(constructType[T]) } @@ -312,7 +318,7 @@ trait ScalaObjectMapper { * Factory method for constructing [[com.fasterxml.jackson.databind.ObjectReader]] that will * read or update instances of specified type */ - def readerFor[T: Manifest]: ObjectReader = { + def readerFor[T: JavaTypeable]: ObjectReader = { readerFor(constructType[T]) } @@ -320,8 +326,8 @@ trait ScalaObjectMapper { * Factory method for constructing [[com.fasterxml.jackson.databind.ObjectReader]] that will * deserialize objects using specified JSON View (filter). */ - def readerWithView[T: Manifest]: ObjectReader = { - readerWithView(manifest[T].runtimeClass) + def readerWithView[T: ClassTag]: ObjectReader = { + readerWithView(classTag[T].runtimeClass) } /* @@ -342,7 +348,7 @@ trait ScalaObjectMapper { * if so, root cause will contain underlying checked exception data binding * functionality threw */ - def convertValue[T: Manifest](fromValue: Any): T = { + def convertValue[T: JavaTypeable](fromValue: Any): T = { convertValue(fromValue, constructType[T]) } @@ -375,26 +381,109 @@ trait ScalaObjectMapper { * * @since 2.1 */ - def acceptJsonFormatVisitor[T: Manifest](visitor: JsonFormatVisitorWrapper): Unit = { - acceptJsonFormatVisitor(manifest[T].runtimeClass, visitor) + def acceptJsonFormatVisitor[T: ClassTag](visitor: JsonFormatVisitorWrapper): Unit = { + acceptJsonFormatVisitor(classTag[T].runtimeClass, visitor) } - private def isArray(c: Class[_]): Boolean = { - c.isArray +} + +trait JavaTypeable[T] { + def asJavaType(typeFactory: TypeFactory): JavaType +} + +object JavaTypeable { + + implicit val anyJavaTypeable: JavaTypeable[Any] = { + new JavaTypeable[Any] { + override def asJavaType(typeFactory: TypeFactory): JavaType = { + val typeArgs: Array[JavaType] = Array() + typeFactory.constructParametricType(classOf[Object], typeArgs: _*) + } + } } - private val MAP = classOf[collection.Map[_,_]] - private def isMapLike(c: Class[_]): Boolean = { - MAP.isAssignableFrom(c) + implicit def optionJavaTypeable[T : JavaTypeable]: JavaTypeable[Option[T]] = { + new JavaTypeable[Option[T]] { + override def asJavaType(typeFactory: TypeFactory): JavaType = { + val typeArg0 = implicitly[JavaTypeable[T]].asJavaType(typeFactory) + typeFactory.constructReferenceType(classOf[Option[_]], typeArg0) + } + } } - private val OPTION = classOf[Option[_]] - private def isReference(c: Class[_]): Boolean = { - OPTION.isAssignableFrom(c) + implicit def arrayJavaTypeable[T : JavaTypeable]: JavaTypeable[Array[T]] = { + new JavaTypeable[Array[T]] { + override def asJavaType(typeFactory: TypeFactory): JavaType = { + val typeArg0 = implicitly[JavaTypeable[T]].asJavaType(typeFactory) + typeFactory.constructArrayType(typeArg0) + } + } } - private val ITERABLE = classOf[collection.Iterable[_]] - private def isCollectionLike(c: Class[_]): Boolean = { - ITERABLE.isAssignableFrom(c) + implicit def mapJavaTypeable[M[_,_] <: Map[_,_], K : JavaTypeable, V: JavaTypeable](implicit ct: ClassTag[M[K,V]]): JavaTypeable[M[K, V]] = { + new JavaTypeable[M[K, V]] { + override def asJavaType(typeFactory: TypeFactory): JavaType = { + val typeArg0 = implicitly[JavaTypeable[K]].asJavaType(typeFactory) + val typeArg1 = implicitly[JavaTypeable[V]].asJavaType(typeFactory) + typeFactory.constructMapLikeType(ct.runtimeClass, typeArg0, typeArg1) + } + } + } + + implicit def collectionJavaTypeable[I[_] <: Iterable[_], T : JavaTypeable](implicit ct: ClassTag[I[T]]): JavaTypeable[I[T]] = { + new JavaTypeable[I[T]] { + override def asJavaType(typeFactory: TypeFactory): JavaType = { + val typeArg0 = implicitly[JavaTypeable[T]].asJavaType(typeFactory) + typeFactory.constructCollectionLikeType(ct.runtimeClass, typeArg0) + } + } } + + // TODO generate genX for X up to a large enough number, 10? 22? + + implicit def gen3JavaTypeable[T[_, _, _], A: JavaTypeable, B: JavaTypeable, C: JavaTypeable](implicit ct: ClassTag[T[A, B, C]]): JavaTypeable[T[A, B, C]] = { + new JavaTypeable[T[A, B, C]] { + override def asJavaType(typeFactory: TypeFactory): JavaType = { + val typeArgs: Array[JavaType] = Array( + implicitly[JavaTypeable[A]].asJavaType(typeFactory), + implicitly[JavaTypeable[B]].asJavaType(typeFactory), + implicitly[JavaTypeable[C]].asJavaType(typeFactory) + ) + typeFactory.constructParametricType(ct.runtimeClass, typeArgs: _*) + } + } + } + + implicit def gen2JavaTypeable[T[_, _], A: JavaTypeable, B: JavaTypeable](implicit ct: ClassTag[T[A, B]]): JavaTypeable[T[A, B]] = { + new JavaTypeable[T[A, B]] { + override def asJavaType(typeFactory: TypeFactory): JavaType = { + val typeArgs: Array[JavaType] = Array( + implicitly[JavaTypeable[A]].asJavaType(typeFactory), + implicitly[JavaTypeable[B]].asJavaType(typeFactory) + ) + typeFactory.constructParametricType(ct.runtimeClass, typeArgs: _*) + } + } + } + + implicit def gen1JavaTypeable[T[_], A: JavaTypeable](implicit ct: ClassTag[T[A]]): JavaTypeable[T[A]] = { + new JavaTypeable[T[A]] { + override def asJavaType(typeFactory: TypeFactory): JavaType = { + val typeArgs: Array[JavaType] = Array( + implicitly[JavaTypeable[A]].asJavaType(typeFactory) + ) + typeFactory.constructParametricType(ct.runtimeClass, typeArgs: _*) + } + } + } + + implicit def gen0JavaTypeable[T](implicit ct: ClassTag[T]): JavaTypeable[T] = { + new JavaTypeable[T] { + override def asJavaType(typeFactory: TypeFactory): JavaType = { + val typeArgs: Array[JavaType] = Array() + typeFactory.constructParametricType(ct.runtimeClass, typeArgs: _*) + } + } + } + }