@@ -129,7 +129,7 @@ object JsonCodecMaker {
129
129
130
130
def typeArg2 (tpe : Type ): Type = tpe.typeArgs.tail.head.dealias
131
131
132
- def isValueClass (tpe : Type ): Boolean = tpe <:< typeOf[ AnyVal ] && tpe .typeSymbol.asClass.isDerivedValueClass
132
+ def isValueClass (tpe : Type ): Boolean = tpe.typeSymbol.asClass.isDerivedValueClass
133
133
134
134
def valueClassValueMethod (tpe : Type ): MethodSymbol = tpe.decls.head.asMethod
135
135
@@ -156,9 +156,9 @@ object JsonCodecMaker {
156
156
def isContainer (tpe : Type ): Boolean =
157
157
tpe <:< typeOf[Option [_]] || tpe <:< typeOf[Traversable [_]] || tpe <:< typeOf[Array [_]]
158
158
159
- def containerCompanion (tpe : Type ): Tree = {
159
+ def collectionCompanion (tpe : Type ): Tree = {
160
160
val comp = tpe.typeSymbol.companion
161
- if (comp.isModule && (tpe <:< typeOf[ Option [_]] || comp.fullName.startsWith(" scala.collection." ) )) Ident (comp)
161
+ if (comp.isModule && comp.fullName.startsWith(" scala.collection." )) Ident (comp)
162
162
else fail(s " Unsupported type ' $tpe'. Please consider using a custom implicitly accessible codec for it. " )
163
163
}
164
164
@@ -215,25 +215,32 @@ object JsonCodecMaker {
215
215
}
216
216
217
217
def genReadArray (newBuilder : Tree , readVal : Tree , result : Tree = q " x " ): Tree =
218
- genReadCollection(newBuilder, readVal, result, q " '[' " , q " ']' " , q " in.arrayEndOrCommaError() " )
218
+ q """ if (in.isNextToken('[')) {
219
+ if (in.isNextToken(']')) default
220
+ else {
221
+ in.rollbackToken()
222
+ .. $newBuilder
223
+ do {
224
+ .. $readVal
225
+ } while (in.isNextToken(','))
226
+ if (in.isCurrentToken(']')) $result
227
+ else in.arrayEndOrCommaError()
228
+ }
229
+ } else in.readNullOrTokenError(default, '[') """
219
230
220
231
def genReadMap (newBuilder : Tree , readKV : Tree , result : Tree = q " x " ): Tree =
221
- genReadCollection(newBuilder, readKV, result, q " '{' " , q " '}' " , q " in.objectEndOrCommaError() " )
222
-
223
- def genReadCollection (newBuilder : Tree , loopBody : Tree , result : Tree ,
224
- open : Tree , close : Tree , endError : Tree ): Tree =
225
- q """ if (in.isNextToken( $open)) {
226
- if (in.isNextToken( $close)) default
232
+ q """ if (in.isNextToken('{')) {
233
+ if (in.isNextToken('}')) default
227
234
else {
228
235
in.rollbackToken()
229
236
.. $newBuilder
230
237
do {
231
- .. $loopBody
238
+ .. $readKV
232
239
} while (in.isNextToken(','))
233
- if (in.isCurrentToken( $close )) $result
234
- else $endError
240
+ if (in.isCurrentToken('}' )) $result
241
+ else in.objectEndOrCommaError()
235
242
}
236
- } else in.readNullOrTokenError(default, $open ) """
243
+ } else in.readNullOrTokenError(default, '{' ) """
237
244
238
245
def genWriteKey (x : Tree , tpe : Type ): Tree = {
239
246
val implKeyCodec = findImplicitKeyCodec(tpe)
@@ -253,20 +260,18 @@ object JsonCodecMaker {
253
260
tpe =:= typeOf[MonthDay ] || tpe =:= typeOf[OffsetDateTime ] || tpe =:= typeOf[OffsetTime ] ||
254
261
tpe =:= typeOf[Period ] || tpe =:= typeOf[Year ] || tpe =:= typeOf[YearMonth ] ||
255
262
tpe =:= typeOf[ZonedDateTime ] || tpe =:= typeOf[ZoneId ] || tpe =:= typeOf[ZoneOffset ]) q " out.writeKey( $x) "
256
- else if (tpe <:< typeOf[Enumeration # Value ]) {
257
- q " out.writeKey( $x.toString) "
258
- } else if (tpe <:< typeOf[java.lang.Enum [_]]) {
259
- q " out.writeKey( $x.name) "
260
- } else fail(s " Unsupported type to be used as map key ' $tpe'. " )
263
+ else if (tpe <:< typeOf[Enumeration # Value ]) q " out.writeKey( $x.toString) "
264
+ else if (tpe <:< typeOf[java.lang.Enum [_]]) q " out.writeKey( $x.name) "
265
+ else fail(s " Unsupported type to be used as map key ' $tpe'. " )
261
266
}
262
267
263
268
def genWriteConstantKey (name : String ): Tree =
264
- if (name.forall( JsonWriter .isNonEscapedAscii )) q " out.writeNonEscapedAsciiKey ( $name) "
265
- else q " out.writeKey ( $name) "
269
+ if (isEncodingRequired(name )) q " out.writeKey ( $name) "
270
+ else q " out.writeNonEscapedAsciiKey ( $name) "
266
271
267
272
def genWriteConstantVal (value : String ): Tree =
268
- if (value.forall( JsonWriter .isNonEscapedAscii )) q " out.writeNonEscapedAsciiVal ( $value) "
269
- else q " out.writeVal ( $value) "
273
+ if (isEncodingRequired(value )) q " out.writeVal ( $value) "
274
+ else q " out.writeNonEscapedAsciiVal ( $value) "
270
275
271
276
def genWriteArray (x : Tree , writeVal : Tree ): Tree =
272
277
q """ out.writeArrayStart()
@@ -447,11 +452,11 @@ object JsonCodecMaker {
447
452
else if (isValueClass(tpe)) q " null.asInstanceOf[ $tpe] "
448
453
else if (tpe <:< typeOf[Option [_]]) q " None "
449
454
else if (tpe <:< typeOf[IntMap [_]] || tpe <:< typeOf[LongMap [_]] || tpe <:< typeOf[mutable.LongMap [_]]) {
450
- q " ${containerCompanion (tpe)}.empty[ ${typeArg1(tpe)}] "
455
+ q " ${collectionCompanion (tpe)}.empty[ ${typeArg1(tpe)}] "
451
456
} else if (tpe <:< typeOf[scala.collection.Map [_, _]]) {
452
- q " ${containerCompanion (tpe)}.empty[ ${typeArg1(tpe)}, ${typeArg2(tpe)}] "
453
- } else if (tpe <:< typeOf[mutable.BitSet ] || tpe <:< typeOf[BitSet ]) q " ${containerCompanion (tpe)}.empty "
454
- else if (tpe <:< typeOf[Traversable [_]]) q " ${containerCompanion (tpe)}.empty[ ${typeArg1(tpe)}] "
457
+ q " ${collectionCompanion (tpe)}.empty[ ${typeArg1(tpe)}, ${typeArg2(tpe)}] "
458
+ } else if (tpe =:= typeOf[mutable.BitSet ] || tpe =:= typeOf[BitSet ]) q " ${collectionCompanion (tpe)}.empty "
459
+ else if (tpe <:< typeOf[Traversable [_]]) q " ${collectionCompanion (tpe)}.empty[ ${typeArg1(tpe)}] "
455
460
else if (tpe <:< typeOf[Array [_]]) withNullValueFor(tpe)(q " new Array[ ${typeArg1(tpe)}](0) " )
456
461
else if (tpe.typeSymbol.isModuleClass) q " ${tpe.typeSymbol.asClass.module}"
457
462
else q " null "
@@ -511,48 +516,48 @@ object JsonCodecMaker {
511
516
} """
512
517
} else if (tpe <:< typeOf[IntMap [_]]) withDecoderFor(methodKey, default) {
513
518
val tpe1 = typeArg1(tpe)
514
- val comp = containerCompanion (tpe)
519
+ val comp = collectionCompanion (tpe)
515
520
genReadMap(q " var x = $comp.empty[ $tpe1] " ,
516
521
q " x = x.updated(in.readKeyAsInt(), ${genReadVal(tpe1, nullValue(tpe1), isStringified)}) " )
517
522
} else if (tpe <:< typeOf[mutable.LongMap [_]]) withDecoderFor(methodKey, default) {
518
523
val tpe1 = typeArg1(tpe)
519
- val comp = containerCompanion (tpe)
524
+ val comp = collectionCompanion (tpe)
520
525
genReadMap(q " val x = if (default.isEmpty) default else $comp.empty[ $tpe1] " ,
521
526
q " x.update(in.readKeyAsLong(), ${genReadVal(tpe1, nullValue(tpe1), isStringified)}) " )
522
527
} else if (tpe <:< typeOf[LongMap [_]]) withDecoderFor(methodKey, default) {
523
528
val tpe1 = typeArg1(tpe)
524
- val comp = containerCompanion (tpe)
529
+ val comp = collectionCompanion (tpe)
525
530
genReadMap(q " var x = $comp.empty[ $tpe1] " ,
526
531
q " x = x.updated(in.readKeyAsLong(), ${genReadVal(tpe1, nullValue(tpe1), isStringified)}) " )
527
532
} else if (tpe <:< typeOf[mutable.Map [_, _]]) withDecoderFor(methodKey, default) {
528
533
val tpe1 = typeArg1(tpe)
529
534
val tpe2 = typeArg2(tpe)
530
- val comp = containerCompanion (tpe)
535
+ val comp = collectionCompanion (tpe)
531
536
genReadMap(q " val x = if (default.isEmpty) default else $comp.empty[ $tpe1, $tpe2] " ,
532
537
q " x.update( ${genReadKey(tpe1)}, ${genReadVal(tpe2, nullValue(tpe2), isStringified)}) " )
533
538
} else if (tpe <:< typeOf[Map [_, _]]) withDecoderFor(methodKey, default) {
534
539
val tpe1 = typeArg1(tpe)
535
540
val tpe2 = typeArg2(tpe)
536
- val comp = containerCompanion (tpe)
541
+ val comp = collectionCompanion (tpe)
537
542
genReadMap(q " var x = $comp.empty[ $tpe1, $tpe2] " ,
538
543
q " x = x.updated( ${genReadKey(tpe1)}, ${genReadVal(tpe2, nullValue(tpe2), isStringified)}) " )
539
- } else if (tpe <:< typeOf[mutable.BitSet ]) withDecoderFor(methodKey, default) {
540
- val comp = containerCompanion (tpe)
544
+ } else if (tpe =:= typeOf[mutable.BitSet ]) withDecoderFor(methodKey, default) {
545
+ val comp = collectionCompanion (tpe)
541
546
val readVal = if (isStringified) q " x.add(in.readStringAsInt()) " else q " x.add(in.readInt()) "
542
547
genReadArray(q " val x = if (default.isEmpty) default else $comp.empty " , readVal)
543
- } else if (tpe <:< typeOf[BitSet ]) withDecoderFor(methodKey, default) {
544
- val comp = containerCompanion (tpe)
548
+ } else if (tpe =:= typeOf[BitSet ]) withDecoderFor(methodKey, default) {
549
+ val comp = collectionCompanion (tpe)
545
550
val readVal = if (isStringified) q " x += in.readStringAsInt() " else q " x += in.readInt() "
546
551
genReadArray(q " val x = $comp.newBuilder " , readVal, q " x.result() " )
547
552
} else if (tpe <:< typeOf[mutable.Traversable [_] with Growable [_]] &&
548
553
! (tpe <:< typeOf[mutable.ArrayStack [_]])) withDecoderFor(methodKey, default) { // ArrayStack uses 'push' for '+='
549
554
val tpe1 = typeArg1(tpe)
550
- val comp = containerCompanion (tpe)
555
+ val comp = collectionCompanion (tpe)
551
556
genReadArray(q " val x = if (default.isEmpty) default else $comp.empty[ $tpe1] " ,
552
557
q " x += ${genReadVal(tpe1, nullValue(tpe1), isStringified)}" )
553
558
} else if (tpe <:< typeOf[Traversable [_]]) withDecoderFor(methodKey, default) {
554
559
val tpe1 = typeArg1(tpe)
555
- val comp = containerCompanion (tpe)
560
+ val comp = collectionCompanion (tpe)
556
561
genReadArray(q " val x = $comp.newBuilder[ $tpe1] " ,
557
562
q " x += ${genReadVal(tpe1, nullValue(tpe1), isStringified)}" , q " x.result() " )
558
563
} else if (tpe <:< typeOf[Array [_]]) withDecoderFor(methodKey, default) {
@@ -598,11 +603,11 @@ object JsonCodecMaker {
598
603
q " val _1: $t = ${genReadVal(t, nullValue(t), isStringified)}" : Tree
599
604
}{ case (acc, (t, i)) =>
600
605
q """ .. $acc
601
- val ${TermName (s " _ ${ i + 1 } " )}: $t =
606
+ val ${TermName (" _ " + ( i + 1 ) )}: $t =
602
607
if (in.isNextToken(',')) ${genReadVal(t, nullValue(t), isStringified)}
603
608
else in.commaError() """
604
609
}
605
- val vals = indexedTypes.map { case (t, i) => TermName (s " _ ${ i + 1 } " ) }
610
+ val vals = indexedTypes.map { case (t, i) => TermName (" _ " + ( i + 1 ) ) }
606
611
q """ if (in.isNextToken('[')) {
607
612
.. $readFields
608
613
if (in.isNextToken(']')) new $tpe(.. $vals)
@@ -628,15 +633,15 @@ object JsonCodecMaker {
628
633
val reqVarNum = required.size
629
634
val lastReqVarIndex = reqVarNum >> 5
630
635
val lastReqVarBits = (1 << reqVarNum) - 1
631
- val reqVarNames = (0 to lastReqVarIndex).map(i => TermName (s " req $i " ))
636
+ val reqVarNames = (0 to lastReqVarIndex).map(i => TermName (" req" + i ))
632
637
val bitmasks : Map [String , Tree ] = required.zipWithIndex.map {
633
638
case (r, i) => (r, q " ${reqVarNames(i >> 5 )} &= ${~ (1 << i)}" )
634
639
}(breakOut)
635
640
val reqVars =
636
641
if (lastReqVarBits == 0 ) Nil
637
642
else reqVarNames.init.map(n => q " var $n = -1 " ) :+ q " var ${reqVarNames.last} = $lastReqVarBits"
638
643
val checkReqVars = reqVarNames.map(n => q " $n == 0 " ).reduce((e1, e2) => q " $e1 && $e2" )
639
- val construct = q " new $tpe(.. ${members.map(m => q " ${m.name} = ${TermName (s " _ ${ m.name} " )}" )}) "
644
+ val construct = q " new $tpe(.. ${members.map(m => q " ${m.name} = ${TermName (" _ " + m.name)}" )}) "
640
645
val checkReqVarsAndConstruct =
641
646
if (lastReqVarBits == 0 ) construct
642
647
else {
@@ -649,12 +654,12 @@ object JsonCodecMaker {
649
654
val defaults = getDefaults(tpe)
650
655
val readVars = members.map { m =>
651
656
val tpe = methodType(m)
652
- q " var ${TermName (s " _ ${ m.name} " )}: $tpe = ${defaults.getOrElse(m.name.decodedName.toString, nullValue(tpe))}"
657
+ q " var ${TermName (" _ " + m.name)}: $tpe = ${defaults.getOrElse(m.name.decodedName.toString, nullValue(tpe))}"
653
658
}
654
659
val readFields = groupByOrdered(members)(hashCode).map { case (hashCode, ms) =>
655
660
val checkNameAndReadValue = ms.foldRight(unexpectedFieldHandler) { case (m, acc) =>
656
661
val decodedName = m.name.decodedName.toString
657
- val varName = TermName (s " _ ${ m.name} " )
662
+ val varName = TermName (" _ " + m.name)
658
663
val isStringified = getStringified(annotations, decodedName)
659
664
val readValue = q " $varName = ${genReadVal(methodType(m), q " $varName" , isStringified)}"
660
665
val resetReqFieldFlag = bitmasks.getOrElse(decodedName, EmptyTree )
@@ -750,7 +755,7 @@ object JsonCodecMaker {
750
755
genWriteMap(q " x " , q " out.writeKey(kv._1) " , genWriteVal(q " kv._2 " , typeArg1(tpe), isStringified))
751
756
} else if (tpe <:< typeOf[scala.collection.Map [_, _]]) withEncoderFor(methodKey, m) {
752
757
genWriteMap(q " x " , genWriteKey(q " kv._1 " , typeArg1(tpe)), genWriteVal(q " kv._2 " , typeArg2(tpe), isStringified))
753
- } else if (tpe <:< typeOf[mutable.BitSet ] || tpe <:< typeOf[BitSet ]) withEncoderFor(methodKey, m) {
758
+ } else if (tpe =:= typeOf[mutable.BitSet ] || tpe =:= typeOf[BitSet ]) withEncoderFor(methodKey, m) {
754
759
genWriteArray(q " x " , if (isStringified) q " out.writeValAsString(x) " else q " out.writeVal(x) " )
755
760
} else if (tpe <:< typeOf[Traversable [_]]) withEncoderFor(methodKey, m) {
756
761
genWriteArray(q " x " , genWriteVal(q " x " , typeArg1(tpe), isStringified))
@@ -775,7 +780,7 @@ object JsonCodecMaker {
775
780
} else if (tpe.typeSymbol.fullName.startsWith(" scala.Tuple" )) withEncoderFor(methodKey, m) {
776
781
val writeFields = tpe.typeArgs.zipWithIndex.map { case (t, i) =>
777
782
q """ out.writeComma()
778
- ${genWriteVal(q " x. ${TermName (s " _ ${ i + 1 } " )}" , t, isStringified)}"""
783
+ ${genWriteVal(q " x. ${TermName (" _ " + ( i + 1 ) )}" , t, isStringified)}"""
779
784
}
780
785
q """ out.writeArrayStart()
781
786
.. $writeFields
@@ -791,21 +796,26 @@ object JsonCodecMaker {
791
796
val isStringified = getStringified(annotations, decodedName)
792
797
defaults.get(decodedName) match {
793
798
case Some (d) =>
794
- if (isContainer(tpe)) {
795
- val nonEmptyAndDefaultMatchingCheck =
796
- if (tpe <:< typeOf[Array [_]]) {
797
- q """ v.length > 0 && {
799
+ if (tpe <:< typeOf[Traversable [_]]) {
800
+ q """ val v = x. $m
801
+ if (!v.isEmpty && v != $d) {
802
+ .. ${genWriteConstantKey(mappedName)}
803
+ .. ${genWriteVal(q " v " , tpe, isStringified)}
804
+ } """
805
+ } else if (tpe <:< typeOf[Option [_]]) {
806
+ q """ val v = x. $m
807
+ if (!v.isEmpty && v != $d) {
808
+ .. ${genWriteConstantKey(mappedName)}
809
+ .. ${genWriteVal(q " v.get " , typeArg1(tpe), isStringified)}
810
+ } """
811
+ } else if (tpe <:< typeOf[Array [_]]) {
812
+ q """ val v = x. $m
813
+ if (v.length > 0 && {
798
814
val d = $d
799
815
v.length != d.length || v.deep != d.deep
800
- } """
801
- } else q " !v.isEmpty && v != $d"
802
- val writeVal =
803
- if (tpe <:< typeOf[Option [_]]) genWriteVal(q " v.get " , typeArg1(tpe), isStringified)
804
- else genWriteVal(q " v " , tpe, isStringified)
805
- q """ val v = x. $m
806
- if ( $nonEmptyAndDefaultMatchingCheck) {
816
+ }) {
807
817
.. ${genWriteConstantKey(mappedName)}
808
- .. $writeVal
818
+ .. ${genWriteVal( q " v " , tpe, isStringified)}
809
819
} """
810
820
} else {
811
821
q """ val v = x. $m
@@ -815,15 +825,23 @@ object JsonCodecMaker {
815
825
} """
816
826
}
817
827
case None =>
818
- if (isContainer(tpe)) {
819
- val nonEmptyCheck = if (tpe <:< typeOf[Array [_]]) q " v.length > 0 " else q " !v.isEmpty "
820
- val writeVal =
821
- if (tpe <:< typeOf[Option [_]]) genWriteVal(q " v.get " , typeArg1(tpe), isStringified)
822
- else genWriteVal(q " v " , tpe, isStringified)
828
+ if (tpe <:< typeOf[Traversable [_]]) {
829
+ q """ val v = x. $m
830
+ if (!v.isEmpty) {
831
+ .. ${genWriteConstantKey(mappedName)}
832
+ .. ${genWriteVal(q " v " , tpe, isStringified)}
833
+ } """
834
+ } else if (tpe <:< typeOf[Option [_]]) {
823
835
q """ val v = x. $m
824
- if ( $nonEmptyCheck ) {
836
+ if (!v.isEmpty ) {
825
837
.. ${genWriteConstantKey(mappedName)}
826
- .. $writeVal
838
+ .. ${genWriteVal(q " v.get " , typeArg1(tpe), isStringified)}
839
+ } """
840
+ } else if (tpe <:< typeOf[Array [_]]) {
841
+ q """ val v = x. $m
842
+ if (v.length > 0) {
843
+ .. ${genWriteConstantKey(mappedName)}
844
+ .. ${genWriteVal(q " v " , tpe, isStringified)}
827
845
} """
828
846
} else {
829
847
q """ .. ${genWriteConstantKey(mappedName)}
@@ -870,6 +888,13 @@ object JsonCodecMaker {
870
888
}
871
889
}
872
890
891
+ private [this ] def isEncodingRequired (s : String ): Boolean = {
892
+ val len = s.length
893
+ var i = 0
894
+ while (i < len && JsonWriter .isNonEscapedAscii(s.charAt(i))) i += 1
895
+ i != len
896
+ }
897
+
873
898
private [this ] def groupByOrdered [A , K ](xs : Traversable [A ])(f : A => K ): mutable.Map [K , mutable.Buffer [A ]] = {
874
899
val m = mutable.LinkedHashMap .empty[K , mutable.Buffer [A ]].withDefault(_ => mutable.Buffer .empty[A ])
875
900
xs.foreach { x =>
0 commit comments