@@ -742,6 +742,8 @@ object JsonCodecMaker {
742
742
def isCollection (tpe : Type ): Boolean =
743
743
tpe <:< typeOf[Iterable [? ]] || tpe <:< typeOf[Iterator [? ]] || tpe <:< typeOf[Array [? ]]
744
744
745
+ def isJavaEnum (tpe : Type ): Boolean = tpe <:< typeOf[java.lang.Enum [? ]]
746
+
745
747
def scalaCollectionCompanion (tpe : Type ): Tree =
746
748
if (tpe.typeSymbol.fullName.startsWith(" scala.collection." )) Ident (tpe.typeSymbol.companion)
747
749
else fail(s " Unsupported type ' $tpe'. Please consider using a custom implicitly accessible codec for it. " )
@@ -808,26 +810,43 @@ object JsonCodecMaker {
808
810
(name, q " private[this] val $name = new _root_.java.util.concurrent.ConcurrentHashMap[ $keyTpe, $tpe] " )
809
811
})._1)
810
812
811
- case class JavaEnumValueInfo ( value : Tree , name : String , transformed : Boolean )
813
+ sealed trait TypeInfo
812
814
813
- val enumValueInfos = new mutable. LinkedHashMap [ Type , List [ JavaEnumValueInfo ]]
815
+ case class JavaEnumValueInfo ( value : Tree , name : String )
814
816
815
- def isJavaEnum (tpe : Type ): Boolean = tpe <:< typeOf[java.lang.Enum [? ]]
817
+ case class JavaEnumInfo (valueInfos : List [JavaEnumValueInfo ], hasTransformed : Boolean , doEncoding : Boolean ) extends TypeInfo {
818
+ val doLinearSearch : Boolean = valueInfos.size <= 8 && valueInfos.foldLeft(0 )(_ + _.name.length) <= 64
819
+ }
820
+
821
+ case class FieldInfo (symbol : TermSymbol , mappedName : String , tmpName : TermName , getter : MethodSymbol ,
822
+ defaultValue : Option [Tree ], resolvedTpe : Type , isStringified : Boolean )
823
+
824
+ case class ClassInfo (tpe : Type , paramLists : List [List [FieldInfo ]]) extends TypeInfo {
825
+ val fields : List [FieldInfo ] = paramLists.flatten
826
+ }
816
827
817
- def javaEnumValues (tpe : Type ): List [JavaEnumValueInfo ] = enumValueInfos.getOrElseUpdate(tpe, {
828
+ val typeInfos = new mutable.HashMap [Type , TypeInfo ]
829
+
830
+ def getJavaEnumInfo (tpe : Type ): JavaEnumInfo = typeInfos.getOrElseUpdate(tpe, {
818
831
val javaEnumValueNameMapper : String => String = n => cfg.javaEnumValueNameMapper.lift(n).getOrElse(n)
832
+ var hasTransformed = false
833
+ var doEncoding = false
819
834
var values = tpe.typeSymbol.asClass.knownDirectSubclasses.toList.map { s : Symbol =>
820
835
val name = s.name.toString
821
836
val transformedName = javaEnumValueNameMapper(name)
822
- JavaEnumValueInfo (q " $s" , transformedName, name != transformedName)
837
+ hasTransformed |= name != transformedName
838
+ doEncoding |= isEncodingRequired(transformedName)
839
+ new JavaEnumValueInfo (q " $s" , transformedName)
823
840
}
824
841
if (values eq Nil ) {
825
842
val comp = companion(tpe)
826
843
values =
827
844
comp.typeSignature.members.sorted.collect { case m : MethodSymbol if m.isGetter && m.returnType.dealias =:= tpe =>
828
845
val name = decodeName(m)
829
846
val transformedName = javaEnumValueNameMapper(name)
830
- JavaEnumValueInfo (q " $comp. ${TermName (name)}" , transformedName, name != transformedName)
847
+ hasTransformed |= name != transformedName
848
+ doEncoding |= isEncodingRequired(transformedName)
849
+ new JavaEnumValueInfo (q " $comp. ${TermName (name)}" , transformedName)
831
850
}
832
851
}
833
852
val nameCollisions = duplicated(values.map(_.name))
@@ -837,19 +856,19 @@ object JsonCodecMaker {
837
856
s " names of the enum that are mapped by the ' ${typeOf[CodecMakerConfig ]}.javaEnumValueNameMapper' function. " +
838
857
s " Result values should be unique per enum class. " )
839
858
}
840
- values
841
- })
859
+ new JavaEnumInfo ( values, hasTransformed, doEncoding)
860
+ }). asInstanceOf [ JavaEnumInfo ]
842
861
843
- def genReadEnumValue (enumValues : Seq [ JavaEnumValueInfo ] , unexpectedEnumValueHandler : Tree ): Tree = {
862
+ def genReadEnumValue (enumInfo : JavaEnumInfo , unexpectedEnumValueHandler : Tree ): Tree = {
844
863
def genReadCollisions (es : collection.Seq [JavaEnumValueInfo ]): Tree =
845
864
es.foldRight(unexpectedEnumValueHandler) { (e, acc) =>
846
865
q " if (in.isCharBufEqualsTo(l, ${e.name})) ${e.value} else $acc"
847
866
}
848
867
849
- if (enumValues.size <= 8 && enumValues.foldLeft( 0 )(_ + _.name.length) <= 64 ) genReadCollisions(enumValues )
868
+ if (enumInfo.doLinearSearch) genReadCollisions(enumInfo.valueInfos )
850
869
else {
851
870
val hashCode = (e : JavaEnumValueInfo ) => JsonReader .toHashCode(e.name.toCharArray, e.name.length)
852
- val cases = groupByOrdered(enumValues )(hashCode).map { case (hash, fs) =>
871
+ val cases = groupByOrdered(enumInfo.valueInfos )(hashCode).map { case (hash, fs) =>
853
872
cq " $hash => ${genReadCollisions(fs)}"
854
873
} :+ cq " _ => $unexpectedEnumValueHandler"
855
874
q """ (in.charBufToHashCode(l): @_root_.scala.annotation.switch) match {
@@ -858,16 +877,7 @@ object JsonCodecMaker {
858
877
}
859
878
}
860
879
861
- case class FieldInfo (symbol : TermSymbol , mappedName : String , tmpName : TermName , getter : MethodSymbol ,
862
- defaultValue : Option [Tree ], resolvedTpe : Type , isStringified : Boolean )
863
-
864
- case class ClassInfo (tpe : Type , paramLists : List [List [FieldInfo ]]) {
865
- val fields : List [FieldInfo ] = paramLists.flatten
866
- }
867
-
868
- val classInfos = new mutable.LinkedHashMap [Type , ClassInfo ]
869
-
870
- def getClassInfo (tpe : Type ): ClassInfo = classInfos.getOrElseUpdate(tpe, {
880
+ def getClassInfo (tpe : Type ): ClassInfo = typeInfos.getOrElseUpdate(tpe, {
871
881
case class FieldAnnotations (partiallyMappedName : Option [String ], transient : Boolean , stringified : Boolean )
872
882
873
883
def hasSupportedAnnotation (m : TermSymbol ): Boolean = {
@@ -930,7 +940,7 @@ object JsonCodecMaker {
930
940
}
931
941
})
932
942
})
933
- })
943
+ }). asInstanceOf [ ClassInfo ]
934
944
935
945
def isValueClass (tpe : Type ): Boolean = ! isConstType(tpe) && isNonAbstractScalaClass(tpe) &&
936
946
(tpe.typeSymbol.asClass.isDerivedValueClass || cfg.inlineOneValueClasses && ! isCollection(tpe) && getClassInfo(tpe).fields.size == 1 )
@@ -1027,7 +1037,7 @@ object JsonCodecMaker {
1027
1037
}
1028
1038
} else if (isJavaEnum(tpe)) {
1029
1039
q """ val l = in.readKeyAsCharBuf()
1030
- ${genReadEnumValue(javaEnumValues (tpe), q " in.enumValueError(l) " )}"""
1040
+ ${genReadEnumValue(getJavaEnumInfo (tpe), q " in.enumValueError(l) " )}"""
1031
1041
} else if (isConstType(tpe)) {
1032
1042
tpe match {
1033
1043
case ConstantType (Constant (v : String )) =>
@@ -1187,15 +1197,14 @@ object JsonCodecMaker {
1187
1197
if (cfg.useScalaEnumValueId) q " out.writeKey( $x.id) "
1188
1198
else q " out.writeKey( $x.toString) "
1189
1199
} else if (isJavaEnum(tpe)) {
1190
- val es = javaEnumValues(tpe)
1191
- val encodingRequired = es.exists(e => isEncodingRequired(e.name))
1192
- if (es.exists(_.transformed)) {
1193
- val cases = es.map(e => cq " ${e.value} => ${e.name}" ) :+
1200
+ val enumInfo = getJavaEnumInfo(tpe)
1201
+ if (enumInfo.hasTransformed) {
1202
+ val cases = enumInfo.valueInfos.map(e => cq " ${e.value} => ${e.name}" ) :+
1194
1203
cq """ _ => out.encodeError("illegal enum value: " + $x) """
1195
- if (encodingRequired ) q " out.writeKey( $x match { case .. $cases }) "
1204
+ if (enumInfo.doEncoding ) q " out.writeKey( $x match { case .. $cases }) "
1196
1205
else q " out.writeNonEscapedAsciiKey( $x match { case .. $cases }) "
1197
1206
} else {
1198
- if (encodingRequired ) q " out.writeKey( $x.name) "
1207
+ if (enumInfo.doEncoding ) q " out.writeKey( $x.name) "
1199
1208
else q " out.writeNonEscapedAsciiKey( $x.name) "
1200
1209
}
1201
1210
} else if (isConstType(tpe)) {
@@ -1378,28 +1387,27 @@ object JsonCodecMaker {
1378
1387
case class MethodKey (tpe : Type , isStringified : Boolean , discriminator : Tree )
1379
1388
1380
1389
val decodeMethodNames = new mutable.HashMap [MethodKey , TermName ]
1381
- val decodeMethodTrees = new mutable.ArrayBuffer [Tree ]
1390
+ val methodTrees = new mutable.ArrayBuffer [Tree ]
1382
1391
1383
1392
def withDecoderFor (methodKey : MethodKey , arg : Tree )(f : => Tree ): Tree = {
1384
1393
val decodeMethodName = decodeMethodNames.getOrElse(methodKey, {
1385
1394
val name = TermName (s " d ${decodeMethodNames.size}" )
1386
1395
val mtpe = methodKey.tpe
1387
1396
decodeMethodNames.update(methodKey, name)
1388
- decodeMethodTrees +=
1397
+ methodTrees +=
1389
1398
q " private[this] def $name(in: _root_.com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, default: $mtpe): $mtpe = $f"
1390
1399
name
1391
1400
})
1392
1401
q " $decodeMethodName(in, $arg) "
1393
1402
}
1394
1403
1395
1404
val encodeMethodNames = new mutable.HashMap [MethodKey , TermName ]
1396
- val encodeMethodTrees = new mutable.ArrayBuffer [Tree ]
1397
1405
1398
1406
def withEncoderFor (methodKey : MethodKey , arg : Tree )(f : => Tree ): Tree = {
1399
1407
val encodeMethodName = encodeMethodNames.getOrElse(methodKey, {
1400
1408
val name = TermName (s " e ${encodeMethodNames.size}" )
1401
1409
encodeMethodNames.update(methodKey, name)
1402
- encodeMethodTrees +=
1410
+ methodTrees +=
1403
1411
q " private[this] def $name(x: ${methodKey.tpe}, out: _root_.com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter): _root_.scala.Unit = $f"
1404
1412
name
1405
1413
})
@@ -1458,7 +1466,7 @@ object JsonCodecMaker {
1458
1466
case _ => cannotFindValueCodecError(tpe)
1459
1467
}
1460
1468
} else if (tpe.typeSymbol.isModuleClass) q " ${tpe.typeSymbol.asClass.module}"
1461
- else if (tpe <:< typeOf[ AnyRef ] ) q " null "
1469
+ else if (tpe <:< definitions. AnyRefTpe ) q " null "
1462
1470
else q " null.asInstanceOf[ $tpe] "
1463
1471
}
1464
1472
@@ -1904,7 +1912,7 @@ object JsonCodecMaker {
1904
1912
q """ if (in.isNextToken('"')) {
1905
1913
in.rollbackToken()
1906
1914
val l = in.readStringAsCharBuf()
1907
- ${genReadEnumValue(javaEnumValues (tpe), q " in.enumValueError(l) " )}
1915
+ ${genReadEnumValue(getJavaEnumInfo (tpe), q " in.enumValueError(l) " )}
1908
1916
} else in.readNullOrTokenError(default, '"') """
1909
1917
} else if (isTuple(tpe)) withDecoderFor(methodKey, default) {
1910
1918
val indexedTypes = typeArgs(tpe)
@@ -2271,15 +2279,14 @@ object JsonCodecMaker {
2271
2279
else q " out.writeVal(x.id) "
2272
2280
} else q " out.writeVal(x.toString) "
2273
2281
} else if (isJavaEnum(tpe)) withEncoderFor(methodKey, m) {
2274
- val es = javaEnumValues(tpe)
2275
- val encodingRequired = es.exists(e => isEncodingRequired(e.name))
2276
- if (es.exists(_.transformed)) {
2277
- val cases = es.map(e => cq " ${e.value} => ${e.name}" ) :+
2282
+ val enumInfo = getJavaEnumInfo(tpe)
2283
+ if (enumInfo.hasTransformed) {
2284
+ val cases = enumInfo.valueInfos.map(e => cq " ${e.value} => ${e.name}" ) :+
2278
2285
cq """ _ => out.encodeError("illegal enum value: " + x) """
2279
- if (encodingRequired ) q " out.writeVal(x match { case .. $cases }) "
2286
+ if (enumInfo.doEncoding ) q " out.writeVal(x match { case .. $cases }) "
2280
2287
else q " out.writeNonEscapedAsciiVal(x match { case .. $cases }) "
2281
2288
} else {
2282
- if (encodingRequired ) q " out.writeVal(x.name) "
2289
+ if (enumInfo.doEncoding ) q " out.writeVal(x.name) "
2283
2290
else q " out.writeNonEscapedAsciiVal(x.name) "
2284
2291
}
2285
2292
} else if (isTuple(tpe)) withEncoderFor(methodKey, m) {
@@ -2354,8 +2361,7 @@ object JsonCodecMaker {
2354
2361
if (cfg.decodingOnly) q " _root_.scala.Predef.??? "
2355
2362
else genWriteVal(q " x " , types, cfg.isStringified, EmptyTree )
2356
2363
}
2357
- .. $decodeMethodTrees
2358
- .. $encodeMethodTrees
2364
+ .. $methodTrees
2359
2365
.. ${fields.values.map(_._2)}
2360
2366
.. ${equalsMethods.values.map(_._2)}
2361
2367
.. ${nullValues.values.map(_._2)}
0 commit comments